Network reachability
Ask Cushy whether traffic can flow between two IPs — see EVERY layer the packet traverses (security groups, network ACLs, route tables, gateways, the stateless return path), then PROVE the verdict with a live probe executed on the source instance: SSM Run Command on AWS, Run Command on Azure, Cloud Assistant on Alibaba Cloud.
Reachability answers a single, concrete question: *can traffic get from here to there — and if not, which layer is blocking it?* The platform traces the packet through every network control on the path and shows each one.
Start from the map — click the component you care about
The primary way to run a test is the network topology map (/network): click a VPC, subnet, instance, load balancer, database or gateway, then press Test reachability in the right-hand inspector. That component becomes the fixed anchor of the test — shown as a chip (name · type · VPC · account), not an editable address — and you only choose three things:
- Direction, as an arrow:
[this component] ──▶ [other](outbound — this component is the source) or[this component] ◀── [other](inbound — this component is the destination). Flipping the arrow re-runs the same pair the other way, which is often a different answer: a security group can allow one direction and block the other. - The other endpoint — either *pick a component* (a searchable list showing name · type · VPC · account, so two identically-named default VPCs are distinguishable) or *type an IP* (any IPv4, including a public address such as
8.8.8.8). - Protocol and port (tcp/443 by default).
The verdict, the layered hop list and the live probe all appear inside that modal. Press Esc (or click outside) to close it; focus returns to the button you opened it from.
Scope existed only to disambiguate a bare IP that falls inside several overlapping-CIDR networks. A component you clicked on the map is a *resource id*, which is unambiguous by construction — so the anchored flow never needs to ask. Scope is now purely reactive: it appears only if a *typed* IP turns out to be ambiguous (see below).
The standalone analyzer — when you only have two addresses
The Reachability analyzer panel below the map is still there for the case where neither endpoint is a component you can point at: type a source IP and a destination IP (optionally prefilling one from the component picker), choose protocol and port, and analyze. It renders exactly the same verdict, hops and probe evidence as the map flow.
What it checks — every layer, in order
A packet from an instance to another endpoint passes through a fixed sequence of controls. The analyzer evaluates each one and shows an allow / deny / not-applicable verdict with the matched rule and a plain-English reason:
- Source security group — egress. Does the source instance's security group allow the traffic out to the destination?
- Source subnet network ACL — egress. Does the source subnet's NACL (evaluated in rule-number order) permit it out?
- Route table. Which route in the source subnet's route table matches the destination — and what does it point at (local, an internet gateway, a NAT gateway, a peering/transit-gateway connection, or a VPC endpoint)?
- Gateway. The gateway hop itself: *local* is same-VPC delivery; an internet gateway needs the source to have a public IP; a NAT gateway is egress-only (outbound works, inbound cannot be initiated); peering / transit gateway carries cross-VPC traffic; a VPC endpoint reaches an AWS service privately.
- Destination subnet network ACL — ingress. Does the destination subnet's NACL allow the traffic in?
- Destination security group — ingress. Does the destination instance's security group allow it in? (This is the most common blocker.)
- Return path. Because network ACLs are stateless, the reply is checked too — the destination NACL egress and the source NACL ingress must permit the return. Security groups are stateful, so their reply is automatically allowed.
Reachable or not — and why
The result is a big REACHABLE or NOT REACHABLE header, a one-line summary, and the full layered diagram top-to-bottom. If the path is blocked, the offending layer is highlighted ("blocked at destination security group — no ingress rule for tcp/443 from 10.0.1.41"), so you go straight to the fix instead of guessing.
Overlapping CIDRs — when an IP alone is ambiguous
Two networks can legitimately carry the same address range: every AWS default VPC — in every region and every account — is 172.31.0.0/16, so 172.31.8.41 may exist in several of them at once. When the matches for an IP span more than one network, the analyzer refuses to guess (an authoritative-looking verdict about the wrong network would be worse than an error). Instead it lists the candidate networks — VPC/VNet name, CIDR, owning cloud account, provider, region, and the specific matched instance or subnet — and you pick the one you meant.
- Disambiguation is reactive, not a standing control: there is no scope dropdown to fill in up front. The candidate cards appear only when an analysis actually comes back ambiguous, and clicking a candidate pins that endpoint's network and re-runs in one step — in the map modal and in the standalone panel alike.
- An endpoint chosen as a component — the map anchor, or either picker — can never be ambiguous: a resource id resolves directly, so no scope is ever requested for it.
- An IP owned by exactly one instance stays unambiguous even when the surrounding CIDRs overlap — an exact resource match beats CIDR containment.
- The verdict header shows each endpoint's resolved network and account (
10.20.0.4 · vpc-0b63… (acct)), so a screenshot of a result is self-describing.
Where the data comes from
Security groups and network ACLs are discovered from your connected AWS account (DescribeSecurityGroups, DescribeNetworkAcls) alongside each instance's public IP and security-group membership (DescribeInstances). Like IAM discovery, these calls are best-effort: if the platform's role is missing a describe permission, that data degrades to empty and the account stays healthy — the analyzer simply has less to reason about, and says so.
SGs and NACLs are stored as first-class resources but are deliberately NOT drawn on the topology map — they are metadata the reachability engine consumes, not boxes to untangle. The map stays a clean inventory diagram; the analyzer is where their rules come to life.
Prove it with a live probe
The analyzer reasons about what the configuration permits. Verify with a live probe goes one step further: it sends *real traffic* from the source instance and returns the cloud provider's own execution record as evidence. Cushy composes one small test script server-side (a bash /dev/tcp TCP connect or ping for ICMP on Linux; Test-NetConnection / Test-Connection on a discovered Windows server) and executes it on the source instance through that cloud's native command channel — SSM Run Command on AWS, Run Command on Azure, Cloud Assistant on Alibaba Cloud — using the same keyless federation your cloud connection already uses: no extra agent beyond what those platforms already run, and no credential ever leaves the platform.
- The evidence is customer-verifiable. The result panel shows the provider's execution id, the executed document, exit code, raw output, timestamps and the measured latency — and names exactly where to find the same execution in *your own* console: AWS → Systems Manager → Run Command → command history (the SSM command id); Azure → the VM → Operations → Run command history (the async operation id); Alibaba Cloud → ECS → ECS Cloud Assistant → command execution result (the InvokeId). The proof does not depend on trusting Cushy.
- Agreement = proven. When the probe result matches the analyzer's verdict, the verdict is proven with evidence — *at that moment, from that instance*. It is a point-in-time proof, not a guarantee for all time.
- A contradiction is a finding, not an error. If the configuration permits the path but the live probe cannot connect, something the config model can't see is broken — a dead NAT gateway, a blackholed route, the remote service being down, or a host firewall inside the OS. Cushy flags this prominently (and audits
network.probe_contradiction) instead of hiding it. The reverse contradiction (analyzer says blocked, probe connects) means the synced model is missing a rule or path — re-sync and review. - A failed connection is a result. Connection refused / timed out comes back as *not connected* with the raw output as evidence — never as an exception.
Per-cloud availability and prerequisites
| Cloud | Probe channel | Evidence id | Prerequisites on the instance / role |
|---|---|---|---|
| AWS | SSM Run Command (AWS-RunShellScript / AWS-RunPowerShellScript) | SSM command id | SSM agent running (preinstalled on modern Amazon Linux, Ubuntu and Windows AMIs) + an instance profile with AmazonSSMManagedInstanceCore; the connector role needs ssm:SendCommand + ssm:GetCommandInvocation. |
| Azure | Run Command (RunShellScript / RunPowerShellScript, async operation polled) | Run Command operation id | The Azure VM agent running (present on marketplace images) and the VM in the RUNNING state; the connector principal needs the Microsoft.Compute/virtualMachines/runCommand/action RBAC action. Note: this Run Command records no exit code — the probe's marker output is the verdict. |
| Alibaba Cloud | ECS Cloud Assistant (RunCommand / DescribeInvocationResults) | Cloud Assistant InvokeId | The Cloud Assistant agent running (preinstalled on recent Alibaba Cloud public images; check with DescribeCloudAssistantStatus); the RAM role needs ecs:RunCommand + ecs:DescribeInvocationResults. |
| GCP | None — honest 501 | — | Google Cloud has no ad-hoc run-command API (VM Manager / OS Config covers patch jobs, declarative OS policies and inventory only — verified against the OS Config API surface). Manual check: gcloud compute ssh <vm> --tunnel-through-iap, then nc -zvw3 <ip> <port> (needs an SSH client + IAP setup, so Cushy does not fake it). |
A source instance that its cloud's command channel cannot reach (no SSM agent / VM agent not ready / no Cloud Assistant agent) gets an error naming exactly that prerequisite — the probe never fabricates a verdict for an instance it could not execute on.
The probe is gated like other command execution (Deployer and above — it runs a command on your instance), audited (network.probe_executed with the execution id), and the command is composed server-side from a validated IP and port only — the SAME script travels to all three clouds, so what you see in your provider's execution history is exactly what ran. TCP and ICMP only (there is no reliable UDP test); the static analyzer still covers UDP configuration.
Limitations
- Analysis is over your synced topology — connect a cloud account first; a never-connected org gets an honest "no topology yet".
- IPv4 only; the protocol/port model covers tcp/udp/icmp/all.
- Cross-VPC reachability follows a peering / VPN / transit-gateway connection when one exists; explicit cross-VPC route-table entries and Azure/Alibaba security models are on the roadmap.
- The static analyzer reasons about configuration (SGs, NACLs, routes, gateways); the live probe is the packet-level check — AWS, Azure and Alibaba Cloud today (SSM Run Command / Run Command / Cloud Assistant), tcp/icmp only, instance-sourced only. GCP has no run-command API (researched — SSH over IAP is the manual path).
- A passing probe proves connectivity at that moment from that instance — evidence, not a permanent guarantee; re-probe after changes.