Route-Aware Topology
Build 90 — Route-Aware Topology extends Infracast's asset graph with routing semantics, enabling real network reachability analysis instead of relying on security group approximations alone.
Why This Matters
Prior to Build 90, the path tracer checked Security Groups and NACLs but assumed routing worked.
A subnet with no route to an Internet Gateway would still appear "internet-exposed" if its SGs
allowed 0.0.0.0/0 — because we never validated the route table.
Build 90 closes this gap:
- Route tables produce graph edges, not just inventory nodes.
- Subnets are linked to their effective route table via
associated_withedges. - The path tracer validates routing before checking SGs — route-blocked paths are distinct from SG-blocked paths.
- 8 new finding rules fire on routing misconfigurations.
- A Network Connectivity page shows a subnet × reachability matrix.
New Edge Types
| Edge Type | Source | Target | Description |
|---|---|---|---|
routes_to | RouteTable | IGW / NAT / TGW / VGW / Peering / ENI | Route table entry pointing to a gateway |
associated_with | Subnet | RouteTable | Subnet is associated with this route table |
next_hop | Network Device | Network Device / Interface | RIB next-hop resolves to a known device interface |
Edge Properties
routes_to
{
"destination_cidr": "0.0.0.0/0",
"target_type": "ec2.internet_gateway",
"state": "active",
"propagated": false,
"blackhole": false
}
associated_with
{
"association_id": "rtbassoc-0abc123",
"main": false
}
next_hop (network devices)
{
"destination": "10.0.0.0/8",
"next_hop_ip": "10.1.0.1",
"protocol": "ospf",
"interface": "GigabitEthernet0/0"
}
Reachability Computation
ComputeSubnetReachability in the reachability engine walks the graph:
Subnet --associated_with--> RouteTable --routes_to--> Target
For each target type it computes:
| Flag | Trigger |
|---|---|
internet_egress: true | Route table has a route to an Internet Gateway |
nat_egress: true | Route table has a route to a NAT Gateway |
tgw_connected: [...] | Route table has a route to one or more Transit Gateways |
peering_connected: [...] | Route table has a route to one or more VPC Peering connections |
vgw_connected: true | Route table has a route to a VPN or Direct Connect gateway |
Walk depth is capped at 10 hops to prevent cycles. Blackhole routes are skipped.
Results are cached per-tenant with a 5-minute TTL (matching the DefaultCache in the package).
Path Tracer Integration
The path tracer now validates routing before SG checks:
- If the source subnet has a route table association (
RouteTableID != ""), route validation runs. - For internet destinations (
0.0.0.0/0or public IPs), the tracer checksinternet_egressornat_egress. - If no internet route exists, the result is
route_blocked = trueand the summary explains why. - Backward compatible: if no route data is present, the tracer proceeds as before.
Result Fields (new in Build 90)
type PathResult struct {
RouteBlocked bool // true = blocked at routing layer, not SG
// ...
}
type HopResult struct {
RouteDecision string // e.g. "routes_to IGW via rtb-0abc123"
// ...
}
UI Display
The path tracer UI distinguishes three blocked states:
- 🔒 BLOCKED — blocked by SG/firewall rule
- 🛣️ ROUTE BLOCKED — no route in route table (shown in orange)
- 🔓 REACHABLE — all checks pass
Topology Routing Layer
The topology canvas has a Routing toggle in the layer controls. When on:
routes_toedges: dashed orange lines with destination CIDR labelassociated_withedges: dashed amber linesnext_hopedges: dashed salmon/coral lines
These edges are hidden by default to avoid visual clutter. Toggle on when debugging routing paths.
Network Connectivity Page
Path: /network-connectivity (Infrastructure sidebar group)
The page shows a subnet reachability matrix:
| Column | Meaning |
|---|---|
| 🌐 Internet | Direct IGW route — subnet can egress internet |
| 🔁 NAT Egress | Routes via NAT gateway |
| 🔀 TGW | Connected to one or more Transit Gateways |
| 🔗 Peering | Connected to VPC peering connections |
| 🏢 VPN/DX | On-premises connectivity via VGW/DX |
Export to CSV is available. The page uses the same 5-minute cached reachability data as the API.
API Endpoints
All endpoints require nodes:read permission.
GET /api/v1/tenants/{tenantID}/network/reachability
Returns the full subnet reachability matrix. Cached for 5 minutes.
{
"tenant_id": "t-abc123",
"count": 42,
"subnets": {
"aws:us-east-1:ec2.subnet:subnet-0abc": {
"subnet_id": "aws:us-east-1:ec2.subnet:subnet-0abc",
"subnet_name": "my-public-subnet",
"internet_egress": true,
"nat_egress": false,
"vgw_connected": false,
"tgw_connected": [],
"peering_connected": [],
"egress_via": ["aws:us-east-1:ec2.internet_gateway:igw-0abc"],
"route_table_id": "aws:us-east-1:ec2.route_table:rtb-0abc"
}
}
}
GET /api/v1/tenants/{tenantID}/network/reachability/{subnetID}
Returns reachability for a single subnet. URL-encode the subnet ID.
GET /api/v1/tenants/{tenantID}/network/route-tables
Returns all route tables with their routes_to edges.
Finding Rules
Eight rules in rules/network_routing.yaml:
| ID | Severity | Title |
|---|---|---|
| NETWORK-ROUTE-001 | HIGH | Subnet with default route to IGW |
| NETWORK-ROUTE-002 | MEDIUM | Public subnet hosting non-public-facing services |
| NETWORK-ROUTE-003 | MEDIUM | TGW attached but no route propagation |
| NETWORK-ROUTE-004 | HIGH | VPC peering missing return route |
| NETWORK-ROUTE-005 | MEDIUM | Blackhole route present |
| NETWORK-ROUTE-006 | LOW | Route to deprecated/deleted gateway |
| NETWORK-ROUTE-007 | HIGH | Compliance-scoped subnet reachable via TGW from non-scoped subnet |
| NETWORK-ROUTE-008 | HIGH | On-premises CIDR reachable from internet-egress subnet (pivot path) |
Limitations
- No BGP path computation. We use installed RIB entries, not simulated best-path selection. BGP path simulation is deferred to Build 90.5.
- No ECMP flow-hash modeling. Multiple equal-cost next-hops are treated as parallel valid paths.
- No encrypted overlay tunnel introspection. IPsec/WireGuard/GRE tunnel endpoints are noted but not traced through.
- Stale between scans. Reachability is recomputed per-scan. Cache TTL is 5 minutes; invalidate on scan completion via
reachability.DefaultCache.Invalidate(tenantID). - Azure route table subnet associations use the raw ARM resource ID as the subnet node ID. Ensure Azure subnet discovery runs before route table discovery to get full join.
Source Files
| Component | File |
|---|---|
| AWS route parser | plugins/discovery/aws/ec2.go — DiscoverRouteTables |
| Azure route parser | plugins/discovery/azure/network.go — DiscoverRouteTables |
| GCP route parser | plugins/discovery/gcp/compute.go — DiscoverRoutes |
| Cisco IOS RIB→edge | plugins/discovery/cisco-ios/ios.go — getRoutingTable |
| Juniper Junos RIB→edge | plugins/discovery/juniper-junos/junos.go — getRoutingTable |
| Palo Alto RIB→edge | plugins/discovery/palo-alto/discovery.go |
| Fortinet RIB→edge | plugins/discovery/fortinet-fortigate/fortigate.go — DiscoverRoutes |
| Reachability engine | Reachability module |
| Path tracer integration | Path analysis module |
| API handlers | Network routing handlers |
| API routes | Routes configuration |
| Finding rules | rules/network_routing.yaml |
| Topology routing layer | ui/src/components/TopologyMap.tsx |
| Subnet reachability tab | ui/src/components/NodeCard.tsx |
| Network Connectivity page | ui/src/pages/NetworkConnectivity.tsx |
Build 90.5 Enhancements
Build 90.5 extends this model with three additional capabilities:
- BGP Path Modeling — RFC 4271 best-path selection, best vs backup path visibility, route hijack detection
- ECMP & Flow-Hash Modeling — Deterministic per-flow path prediction using platform-specific hash algorithms
- Tunnel Introspection — Reachability computation through IPsec, WireGuard, GRE, and VXLAN tunnels
These add 13 new finding rules (5 BGP, 3 ECMP, 5 tunnel) and extend the connectivity matrix with via_tunnel, via_bgp_dynamic, and ecmp_groups columns.