cerf
Centralized inspection VPC

Centralized inspection VPC

Centralized egress and traffic inspection

One inspection VPC behind Transit Gateway steers every spoke's egress and east-west traffic through a firewall before a shared NAT.

Distributed versus centralized inspection

AWS Network Firewall can be deployed in three shapes. In a distributed model a firewall lives inside each individual VPC, giving the smallest blast radius but multiplying the number of firewalls, NAT gateways, and route tables you operate. In a centralized model the firewall lives in one inspection VPC and every other VPC (a spoke) reaches it through AWS Transit Gateway. A combined model puts east-west and egress inspection in the center while leaving dedicated inbound internet access distributed. The centralized model is the one that scales to dozens of accounts because you configure the ruleset, logging, and NAT once instead of per VPC.

The inspection VPC is not a general-purpose VPC. Per Availability Zone it carries two dedicated subnets: a firewall endpoint subnet that holds the Network Firewall (or Gateway Load Balancer) endpoint, and a separate Transit Gateway attachment subnet where the TGW places its elastic network interface. AWS is explicit that the firewall subnet is reserved: Network Firewall cannot inspect traffic whose source or destination lives inside the firewall subnet, so you never run workloads there.

Two traffic classes flow through this hub. North-south (egress) traffic is a spoke workload reaching the internet or on-premises. East-west traffic is one spoke talking to another. Both are forced through the same inspection endpoint by routing, not by an agent on the instance, which is why the design is transparent to the workloads. The tradeoff you accept is that the inspection VPC and Transit Gateway become a shared choke point, so they must be sized, spread across AZs, and monitored as critical infrastructure.

Centralized inspection VPCCentralized egress and east-west inspection via Transit Gateway
Centralized egress and east-west inspection via Transit GatewayAWS Region us-east-1Prod spoke VPC 10.0.0.0/16Dev spoke VPC 10.1.0.0/16Inspection / egress VPC 10.100.0.0/16TGW attachment subnet RT: 0.0.0.0/0 -> firewall (same AZ)Firewall subnetNAT public subnetdefault route -> TGWVPC attachmentdefault route -> TGWVPC attachmentspoke RT: 0.0.0.0/0same-AZ firewall endpointinspected egress -> NATsource NAT to Elastic IPto interneteast-west return to TGWinspection RT: spoke CIDRsto Dev spokedeliver to workloadProd workload10.0.0.0/16TGW attachmentProd spokeDev workload10.1.0.0/16TGW attachmentDev spokeTransit Gatewayspoke RT + inspecti…Inspectionattachmentappliance mode ONFirewallendpointone per AZShared NATgatewayElastic IPInternetgatewayInternet
Trace
Spoke VPCs reach one inspection VPC through Transit Gateway; egress exits a shared NAT gateway, east-west returns to the destination spoke.

The data path and route-table design

Routing does all the steering, and it lives in two places: the VPC route tables and the Transit Gateway route tables. In each spoke, the workload subnet has a default route (0.0.0.0/0) pointing at the Transit Gateway. The Transit Gateway then uses separate route tables to break symmetry: a spoke route table associated with the spoke attachments sends 0.0.0.0/0 to the inspection VPC attachment, while a separate inspection (firewall) route table associated with the inspection attachment carries the propagated spoke CIDRs so post-inspection traffic can be delivered back to the right spoke. Keeping spokes and the inspection VPC in different TGW route tables is what forces traffic into the firewall instead of letting the TGW shortcut spoke-to-spoke.

Inside the inspection VPC the route tables are per-subnet and per-AZ. The TGW attachment subnet in a given AZ has a route table whose default route points at the firewall endpoint in that same AZ, so traffic arriving from the TGW is handed to the local firewall. After inspection, a return route table sends traffic back toward the Transit Gateway (for east-west, so it can reach the destination spoke) or onward to the NAT gateway (for egress). To stop spokes from bypassing the firewall by talking to each other directly through the TGW, you can add blackhole routes for the spoke CIDRs in the spoke route table so the only permitted next hop is the inspection VPC.

For egress specifically, AWS calls out a common trap: the Transit Gateway attachment in the egress VPC must be placed in a private subnet, not the public NAT subnet. If the attachment sits in the public subnet, traffic is routed straight to the internet gateway, which then drops it because the spoke instances have no public IP. Putting the attachment in a private subnet routes traffic to the NAT gateway first, which rewrites the source to its Elastic IP before the internet gateway forwards it.

Centralized inspection VPCCentralized egress and east-west inspection via Transit Gateway
Centralized egress and east-west inspection via Transit GatewayAWS Region us-east-1Prod spoke VPC 10.0.0.0/16Dev spoke VPC 10.1.0.0/16Inspection / egress VPC 10.100.0.0/16TGW attachment subnet RT: 0.0.0.0/0 -> firewall (same AZ)Firewall subnetNAT public subnetdefault route -> TGWVPC attachmentdefault route -> TGWVPC attachmentspoke RT: 0.0.0.0/0same-AZ firewall endpointinspected egress -> NATsource NAT to Elastic IPto interneteast-west return to TGWinspection RT: spoke CIDRsto Dev spokedeliver to workloadProd workload10.0.0.0/16TGW attachmentProd spokeDev workload10.1.0.0/16TGW attachmentDev spokeTransit Gatewayspoke RT + inspecti…Inspectionattachmentappliance mode ONFirewallendpointone per AZShared NATgatewayElastic IPInternetgatewayInternet
Trace
Spoke VPCs reach one inspection VPC through Transit Gateway; egress exits a shared NAT gateway, east-west returns to the destination spoke.

Appliance mode and flow symmetry

Stateful firewalls track connection state, so the forward and return packets of a single flow must hit the same appliance in the same AZ. Transit Gateway's default behavior fights this: when appliance mode is not enabled, the TGW tries to keep traffic in its originating Availability Zone. That means a response originating in AZ 2 of a spoke is delivered to the AZ 2 firewall, even though the original request was inspected by the AZ 1 firewall. The AZ 2 appliance has no state for that flow and drops the packet. This is the classic asymmetric-routing failure.

Enabling appliance mode on the inspection VPC's TGW attachment fixes it. With appliance mode on, the Transit Gateway uses a flow hash to select one network interface in the appliance VPC and pins the entire flow (both directions) to that same interface, so bidirectional traffic is routed symmetrically through the same AZ for the life of the flow. Enabling it removes the need for workarounds like source NAT on the appliance to drag return traffic back.

There are hard constraints worth memorizing. You must connect exactly one Transit Gateway to the appliance VPC: multiple TGWs do not share flow state, so stickiness is not guaranteed. Flow stickiness is only guaranteed when the source and destination traffic arrive at the inspection VPC from the same TGW attachment; traffic can drop if the inspection VPC receives a flow from a different gateway (for example, an internet gateway) and then hands it to the TGW attachment after inspection. Enabling appliance mode on an existing attachment can also shift its current AZ path, so treat it as a change with routing impact.

Centralized inspection VPCCentralized egress and east-west inspection via Transit Gateway
Centralized egress and east-west inspection via Transit GatewayAWS Region us-east-1Prod spoke VPC 10.0.0.0/16Dev spoke VPC 10.1.0.0/16Inspection / egress VPC 10.100.0.0/16TGW attachment subnet RT: 0.0.0.0/0 -> firewall (same AZ)Firewall subnetNAT public subnetdefault route -> TGWVPC attachmentdefault route -> TGWVPC attachmentspoke RT: 0.0.0.0/0same-AZ firewall endpointinspected egress -> NATsource NAT to Elastic IPto interneteast-west return to TGWinspection RT: spoke CIDRsto Dev spokedeliver to workloadProd workload10.0.0.0/16TGW attachmentProd spokeDev workload10.1.0.0/16TGW attachmentDev spokeTransit Gatewayspoke RT + inspecti…Inspectionattachmentappliance mode ONFirewallendpointone per AZShared NATgatewayElastic IPInternetgatewayInternet
Trace
Spoke VPCs reach one inspection VPC through Transit Gateway; egress exits a shared NAT gateway, east-west returns to the destination spoke.

Network Firewall or a GWLB appliance fleet

You have two engines for the inspection VPC. AWS Network Firewall is the managed, stateful, rules-based option (Suricata-compatible) with nothing to patch or scale. A Gateway Load Balancer (GWLB) fronting a fleet of third-party appliances is the choice when you need a specific vendor's layer 7 IPS/IDS or deep packet inspection. GWLB is a bump-in-the-wire: it transparently passes all layer 3 traffic to the appliances and is invisible to the source and destination.

GWLB reaches its appliances by encapsulating the original IP packet in a GENEVE header and forwarding it over UDP port 6081, which lets every protocol and port be inspected without configuring per-port listeners; the GENEVE header does not count against the GWLB MTU. For stateful firewalls, GWLB uses the packet's 5-tuple (or 3-tuple) to pin a flow to a single appliance for its lifetime, the same stickiness principle appliance mode provides at the TGW layer. The appliances must support GENEVE (RFC 8926) to participate, and GWLB endpoints are powered by AWS PrivateLink.

There is also a newer shortcut. Transit Gateway now supports a network function attachment for AWS Network Firewall: you attach the firewall directly to the TGW, AWS provisions a service-managed buffer VPC with one GWLB endpoint per AZ and appliance mode already enabled, and you skip building and routing an inspection VPC entirely. It supports static routing only and does not support third-party firewalls, so the GWLB-plus-appliance pattern remains the path for vendor NGFWs. Remember Network Firewall's blind spots regardless of engine: it does not inspect VPC peering traffic, AWS Global Accelerator traffic, or the AmazonProvidedDNS traffic for EC2.

Centralized inspection VPCGWLB appliance fleet: GENEVE inspection and appliance-mode symmetry
GWLB appliance fleet: GENEVE inspection and appliance-mode symmetryAWS Region us-east-1Spoke VPC 10.0.0.0/16Inspection / egress VPC 10.100.0.0/16Availability Zone AAvailability Zone BNAT public subnetdefault route 0.0.0.0/0 -> TGWVPC attachmentappliance mode selects ENI (4-tuple)TGW subnet RT -> GWLBEGENEVE encap, UDP 60815-tuple hash to applianceinspected traffic returnsdecapsulateroute to shared NATSNAT to Elastic IPto internetAZ-B path (other flows)TGW subnet RT -> GWLBEGENEVE encap, UDP 60815-tuple hash to applianceSpoke workload10.0.0.0/16TGW attachmentspoke VPCTransit Gatewayappliance mode ONTGW ENI (AZ-A)attachment subnetGWLB endpoint(AZ-A)PrivateLinkFirewallappliance…3rd-party NGFWTGW ENI (AZ-B)attachment subnetGWLB endpoint(AZ-B)PrivateLinkFirewallappliance…3rd-party NGFWGateway LoadBalancer5-tuple flow sticki…Shared NATgatewayElastic IPInternetgatewayInternet
Trace
Gateway Load Balancer GENEVE-encapsulates traffic to a third-party appliance fleet; TGW appliance mode and GWLB 5-tuple hashing keep each flow on one appliance.

Why one shared NAT beats per-VPC NAT

A NAT gateway bills two ways: a fixed hourly charge for every NAT gateway you run, plus a per-gigabyte data-processing charge on everything that passes through it. In a distributed design where each of N spoke VPCs owns its own NAT gateway (and typically one per AZ for resilience), you pay the fixed hourly charge N times over, whether or not the VPC is sending much traffic. Centralizing egress collapses those fixed charges to a single set of NAT gateways in the egress VPC, one per AZ, shared by every spoke. The per-GB processing still applies to the aggregate traffic, but the multiplied per-gateway overhead disappears, which is the dominant saving as the number of VPCs grows.

Centralizing NAT also centralizes the Elastic IP footprint. All spoke egress leaves from a small, known set of NAT Elastic IPs, which is what makes SaaS allow-listing and partner firewall rules manageable; per-VPC NAT would scatter source IPs across the whole estate. The same VPC that terminates inspection is the natural place to land the NAT, so the flow becomes spoke to TGW to inspection endpoint to NAT gateway to internet gateway, with a single point to attach flow logs and egress controls.

The cost is not free of caveats. Centralizing routes all egress through the Transit Gateway, so TGW per-GB data-processing charges now apply to that traffic, and if you use GWLB, PrivateLink charges apply to the endpoints. For very high-throughput, cost-sensitive estates you weigh those hub charges against the NAT consolidation savings. Some third-party appliances that support source NAT and overlay (two-arm) routing can even perform the address translation themselves, letting you drop the NAT gateway entirely, though that depends on vendor support and should be confirmed before relying on it.

More diagrams

Centralized egress with inspection through AWS Network Firewall
Centralized egress with inspection through AWS Network FirewallAWS Cloudus-east-1Spoke VPC A 10.1.0.0/16Inspection VPC 100.64.0.0/16Egress VPC 10.100.0.0/16Private subnetFirewall subnetPublic subnet0.0.0.0/0GENEVEHTTPS 443App instance10.1.1.0/24Transit Gatewayappliance modeNetworkFirewallGENEVE endpointNAT gatewayelastic IPInternetgatewayInternet
Trace
Spoke route tables send 0.0.0.0/0 to the Transit Gateway, which hands traffic to Network Firewall endpoints in the inspection VPC; appliance mode keeps each flow symmetric through the same firewall AZ. Inspected traffic returns to the Transit Gateway and leaves through the NAT gateway and internet gateway in the central egress VPC, so spokes never need their own internet path.

Sources

Practice what you just read

6 questions from this architecture

Loading…