← All How-to Guides
AWS Fortinet FortiGate GWLB Security VPC

How to Secure AWS Ingress and Egress with Fortinet FortiGate and GWLB

Deploy FortiGate firewalls with AWS Gateway Load Balancer for transparent ingress and egress traffic inspection

Advanced ⏱ 45 min

Prerequisites

  • AWS account with VPC networking experience
  • Familiarity with Transit Gateway and route table manipulation
  • FortiGate AMI subscription from AWS Marketplace
  • FortiManager license for centralized policy management
  • Terraform 1.5+ installed

Last year I helped a financial services company migrate their on-premises FortiGate firewalls to AWS. They wanted the same level of inspection for both inbound and outbound traffic. The challenge: AWS doesn’t have a traditional firewall insertion model. We needed Gateway Load Balancer (GWLB) to transparently intercept traffic at scale — and the architectural decisions we made fundamentally changed their cloud security posture.

Choose Your Architecture Pattern

You have two options. Pick based on your org structure and risk tolerance.

Centralized — A dedicated security VPC hosts all FortiGate instances and GWLB. Workload VPCs connect via Transit Gateway. One set of firewalls for everything.

Distributed — Each workload VPC gets its own GWLB endpoint and FortiGate instances. Each team manages their own security posture.

Centralized FortiGate GWLB Architecture Centralized architecture with security VPC and Transit Gateway

Pattern Pros Cons
Centralized Unified policy, lower cost, easier auditing Potential bottleneck, single security VPC
Distributed Blast radius isolation, low latency, team autonomy Higher cost, policy drift risk, more overhead

Deploy GWLB with FortiGate

resource "aws_lb" "gwlb" {
  name               = "fortigate-gwlb"
  internal           = true
  load_balancer_type = "gateway"
  subnets            = aws_subnet.security[*].id
}

resource "aws_lb_target_group" "fortigate" {
  name        = "fortigate-targets"
  port        = 6081
  protocol    = "GENEVE"
  vpc_id      = aws_vpc.security.id
  target_type = "instance"

  health_check {
    interval = 10
    port     = "65500"
    protocol = "TCP"
  }
}

resource "aws_lb_listener" "fortigate" {
  load_balancer_arn = aws_lb.gwlb.arn
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.fortigate.arn
  }
}

Launch FortiGate with Auto Scaling

resource "aws_launch_template" "fortigate" {
  name          = "fortigate-launch-template"
  image_id      = var.fortigate_ami_id
  instance_type = "c5.xlarge"

  user_data = base64encode(templatefile("${path.module}/fortigate_bootstrap.txt", {
    hostname       = "fortigate-instance"
    admin_password = random_password.fortigate_admin.result
  }))
}

resource "aws_autoscaling_group" "fortigate" {
  vpc_zone_identifier = aws_subnet.security[*].id
  target_group_arns   = [aws_lb_target_group.fortigate.arn]
  min_size            = 2
  max_size            = 6
  desired_capacity    = 2

  launch_template {
    id      = aws_launch_template.fortigate.id
    version = "$Latest"
  }
}

Create GWLB Endpoints in Workload VPCs

resource "aws_vpc_endpoint_service" "gwlb" {
  gateway_load_balancer_arns = [aws_lb.gwlb.arn]
  acceptance_required        = false
}

resource "aws_vpc_endpoint" "gwlb_endpoint" {
  service_name      = aws_vpc_endpoint_service.gwlb.service_name
  vpc_id            = aws_vpc.workload.id
  vpc_endpoint_type = "GatewayLoadBalancer"
  subnet_ids        = [aws_subnet.workload_inspection.id]
}

Then update route tables so workload subnets send traffic to the GWLB endpoint instead of directly to the internet gateway or NAT gateway.

Trace the Ingress Flow

  1. User sends request to ALB public IP
  2. ALB route table sends traffic to GWLB endpoint
  3. GWLB encapsulates packet in Geneve (UDP 6081)
  4. FortiGate decapsulates, inspects (IPS/IDS, threat scan)
  5. FortiGate re-encapsulates, returns to GWLB
  6. GWLB forwards to application instance
  7. Response follows the same symmetric path

The original source and destination IPs stay intact throughout — FortiGate logs show real client IPs for proper threat correlation.

Trace the Egress Flow

  1. Application sends outbound request
  2. Workload subnet routes to GWLB endpoint
  3. GWLB → FortiGate inspects for malware, applies DNS filtering
  4. FortiGate returns to GWLB → NAT gateway → Internet
  5. Return traffic re-enters via GWLB for stateful inspection

Critical: Use a dedicated inspection subnet for GWLB endpoints. This simplifies route table logic and prevents accidental traffic bypass.

Connect FortiManager

FortiManager pushes policies to all FortiGate instances simultaneously. During bootstrap, each FortiGate registers with FortiManager via IP and pre-shared key. New auto-scaled instances automatically enroll and pull the latest policies — no manual intervention needed.

Key Takeaway

FortiGate with AWS GWLB gives you transparent, enterprise-grade traffic inspection without changing your application architecture. The Geneve encapsulation preserves original packet headers, auto-scaling handles traffic spikes, and FortiManager keeps policies consistent. Plan your route tables carefully — they’re the difference between inspected and bypassed traffic.

Questions? Connect with me on LinkedIn.