Skip to content

Transit Gateway — centralized egress

Centralized egress inspection using an AWS Transit Gateway (TGW) hub. All internet-bound traffic from three workload (spoke) VPCs is steered through a single dedicated edge/inspection VPC, where a pair of Enforza NVAs — one per Availability Zone — perform secure NAT and L7 egress filtering before traffic leaves via the Internet Gateway. Return traffic is forced back through the same firewall that handled the outbound flow — which is what makes a stateful NVA work correctly behind a TGW. Deploy with Terraform or CloudFormation.

Three workload spoke VPCs default-route to the TGW; the TGW forwards all egress to the edge VPC’s TGW attachment (appliance mode on), which pins each flow to one AZ / firewall. Each edge firewall SNATs out via its own EIP + IGW.

workload-a VPC workload-b VPC workload-c VPC
10.1.0.0/16 10.2.0.0/16 10.3.0.0/16
priv default 0/0→TGW priv default 0/0→TGW priv default 0/0→TGW
└────────────────────┼────────────────────┘
TGW attachments (one per spoke)
┌──────────────┴───────────────────────────┐
│ TRANSIT GATEWAY (hub) │
│ default assoc/propagation DISABLED │
│ SPOKE RT : 0.0.0.0/0 → edge attach │
│ FIREWALL RT : 10.1/16→wl-a 10.2/16→wl-b │
│ 10.3/16→wl-c │
└──────────────┬───────────────────────────┘
edge VPC attachment · APPLIANCE MODE = enable
┌──────────────┴───────────────────────────┐
│ EDGE / INSPECTION VPC 10.0.0.0/16 │
│ tgw-subnet-a 10.0.2.0/28 tgw-subnet-b 10.0.3.0/28 │
│ 0/0 → FW-A ENI 0/0 → FW-B ENI │
│ Enforza FW-A (AZ0) Enforza FW-B (AZ1)│
│ EIP · SNAT · src/dst off EIP · SNAT · off │
│ public-a 10.0.0.0/24 public-b 10.0.1.0/24 │
│ 0/0 → IGW · 10.x/16 → TGW (return) │
└──────────────┬───────────────────────────┘
IGW → Internet
  • One edge/inspection VPC 10.0.0.0/16: an IGW; two public subnets (10.0.0.0/24 AZ0, 10.0.1.0/24 AZ1) each with an Enforza firewall + Elastic IP (source/dest check off); two TGW-attachment subnets (10.0.2.0/28 AZ0, 10.0.3.0/28 AZ1).
  • A Transit Gateway (default route-table association and propagation disabled), with two TGW route tables: SPOKE and FIREWALL.
  • The edge VPC TGW attachment with appliance mode enabled.
  • Three workload VPCs (10.1.0.0/16, 10.2.0.0/16, 10.3.0.0/16), each with two private subnets, a TGW attachment, and a default route to the TGW.
  • TWO Enforza deployment keys — one per edge firewall, each one-time-use (Onboard Firewall → Deployment Keys). Firewall A and B need different keys.
  • A base OS AMI: defaults to the latest Amazon Linux 2023 (auto-resolved) — nothing to supply.
  • Terraform ≥ 1.5 or the AWS CLI.
  • IAM permissions for EC2, VPC, Transit Gateway, and Elastic IP; a region with ≥ 2 AZs.

Each firewall launches the stock AL2023 AMI and runs a small user-data script at first boot:

Terminal window
curl -fsSL https://dl.neon.efz.io/install.sh | bash -s -- --regkey=<KEY> --name=<NAME>

The installer sets up prerequisites and the engine and registers it. One key per firewall: firewall A uses deployment_key_a (name enforza-edge-fw-a), firewall B uses deployment_key_b (name enforza-edge-fw-b).

Terminal window
cd terraform
terraform init
terraform apply \
-var "deployment_key_a=<KEY-FOR-FIREWALL-A>" \
-var "deployment_key_b=<KEY-FOR-FIREWALL-B>"
# optional overrides:
# -var 'base_ami_id=ami-xxxxxxxx' -var 'instance_type=c6i.xlarge'
# -var 'ssh_key_name=my-key'
# -var 'availability_zones=["eu-west-2a","eu-west-2b"]'
Terminal window
cd cloudformation
aws cloudformation create-stack \
--stack-name enforza-03-tgw-centralized-egress-deployment-key \
--template-body file://template.yaml \
--parameters \
ParameterKey=DeploymentKeyA,ParameterValue=<KEY-FOR-FIREWALL-A> \
ParameterKey=DeploymentKeyB,ParameterValue=<KEY-FOR-FIREWALL-B> \
ParameterKey=AvailabilityZoneA,ParameterValue=eu-west-2a \
ParameterKey=AvailabilityZoneB,ParameterValue=eu-west-2b

After either deploy, each firewall self-registers via its key on first boot and appears in the Enforza console within a minute or two — no manual claim step.

Outbound (workload → internet):

  1. A workload sends to an internet IP; its private route table (0.0.0.0/0 → TGW) enters the Transit Gateway via that spoke’s attachment.
  2. The TGW SPOKE route table (0.0.0.0/0 → edge attachment) forwards it to the edge VPC.
  3. The packet arrives on the edge TGW attachment ENI in the same AZ it left (appliance mode preserves AZ affinity); that TGW-subnet’s route table sends 0.0.0.0/0 → the same-AZ firewall ENI (AZ0 → FW-A, AZ1 → FW-B).
  4. The firewall applies L7 egress policy and SNATs behind its EIP; its public route table (0.0.0.0/0 → IGW) sends the packet to the internet.

Return (internet → workload), symmetric: 5. The reply hits the firewall’s EIP; the firewall un-NATs it and, via its public route 10.x.0.0/16 → TGW, hands it to the TGW. 6. The TGW FIREWALL route table has a route per workload CIDR pointing at that workload’s attachment, so the reply reaches the originating spoke — over the same AZ ENI and same firewall (appliance mode pinned the flow).

Adding a workload VPC is three small, mechanical changes:

  • Terraform: add one entry to the workload_vpcs map (name → CIDR + two subnet CIDRs); for_each creates the VPC, subnets, TGW attachment, spoke association, default route, and the FIREWALL-RT return route automatically.
  • CloudFormation: add the VPC + two subnets + route table + TGW attachment, one SPOKE association, and one FIREWALL-RT return route (following the Workload A/B/C blocks), plus the new CIDR to the firewall security-group ingress.

To scale throughput, raise instance_type; the two firewalls already give per-AZ horizontal split via appliance-mode AZ affinity.

Terminal window
# Terraform
cd terraform
terraform destroy \
-var "deployment_key_a=<KEY-FOR-FIREWALL-A>" \
-var "deployment_key_b=<KEY-FOR-FIREWALL-B>"
# CloudFormation
aws cloudformation delete-stack --stack-name enforza-03-tgw-centralized-egress-deployment-key

Remember to remove the firewalls from the Enforza console after teardown.

Enforza is a trading name of Synvu Limited, a company registered (15761962) in the United Kingdom. Registered office address: 71–75 Shelton Street, Covent Garden, London, WC2H 9JQ, United Kingdom.