Harbourline Retail moves its web shop into AWS
Harbourline Retail, a fictional company, runs its offices behind FGT1 from the course lab. Its web shop is moving from WEB1 in the DMZ to SHOP1, a server in an AWS VPC. The security team wants the same protection in the cloud that the DMZ had: inbound HTTPS only, IPS on that traffic, outbound access only for updates, and logs in the same format as FGT1's. A FortiGate-VM, called FGT-AWS here, will sit in front of SHOP1.
The course lab is on premises, so this lesson adds its own addresses: a VPC 10.100.0.0/16 with a public subnet 10.100.1.0/24 and a private subnet 10.100.2.0/24. The Elastic IP is shown as 203.0.113.50, a documentation address standing in for the public address AWS would allocate.
What the cloud provider secures, and what stays with you
AWS and Azure both publish a shared responsibility model. The provider secures the data centres, hardware, hypervisor and its own network. You secure what you build on top: which ports are open, which routes exist, what the operating systems run, who holds API keys, and what traffic is allowed between your workloads. A FortiGate-VM is part of your side of that line. Its licence, firmware, configuration and logs are yours to manage, and so is the cloud bill: Fortinet's AWS guide reminds you that compute, storage, snapshots and data transfer are charged to you whichever licence you use.
The problems that bring firewalls into cloud designs are mostly of the customer's own making:
- Misconfiguration. One security group rule allowing
0.0.0.0/0exposes a database to the whole internet in seconds. - Stolen credentials. Leaked access keys or admin passwords let an attacker change the network itself, not just attack it.
- East-west movement. Inside a VPC, instances in different subnets can reach each other through the local route unless something says otherwise.
- Limited inspection. Native controls filter on addresses and ports. They don't see the exploit inside an allowed HTTPS session.
- Constant change. Instances appear and disappear with new addresses, so static address objects go stale quickly.
The FortiGate-VM answers the inspection and consistency problems: the same FortiOS policies, IPS and logging as on premises. It doesn't fix weak credentials or open security groups; those stay on your checklist.
How the VPC hands traffic to FGT-AWS
- 1. Inbound: The internet gateway maps 203.0.113.50 to 10.100.1.10 and delivers the packet to port1.
- 2. VIP: FGT-AWS translates 10.100.1.10:443 to SHOP1 and checks policy, IPS and logging.
- 3. Outbound: The private route table sends 0.0.0.0/0 to the ENI of port2, so SHOP1's traffic reaches FGT-AWS.
- 4. To the internet: FGT-AWS translates the source to 10.100.1.10; the gateway maps it to the Elastic IP.
In a VPC there is no cable to move. A subnet's route table decides where its traffic goes, and Fortinet's single-VM design uses two of them:
| Route table | Associated subnet | Destination | Target |
|---|---|---|---|
| Public | 10.100.1.0/24 | 10.100.0.0/16 0.0.0.0/0 | local Internet gateway |
| Private | 10.100.2.0/24 | 10.100.0.0/16 0.0.0.0/0 | local ENI of FGT-AWS port2 |
SHOP1 still sends its packets to the VPC router at 10.100.2.1 (AWS reserves each subnet's .1 address for it). The private route table then forwards anything non-local to FGT-AWS's private interface. That is the cloud version of setting a server's default gateway to the firewall.
Two AWS behaviours shape the FortiGate configuration. First, the internet gateway does 1:1 NAT between the Elastic IP and port1's private address, so 203.0.113.50 doesn't appear on any FortiOS interface; inbound packets arrive addressed to 10.100.1.10. Second, AWS drops packets that arrive at an interface but aren't addressed to it, unless that interface's source/destination check is disabled. A firewall forwards traffic for other hosts, so the check must be off on its interfaces.
Building the deployment in the AWS console
These are the steps of the "Single FortiGate-VM deployment" section of the FortiGate Public Cloud 7.6 AWS guide, with this lesson's values. Console menus move between AWS releases, so look for the names rather than the positions.
| Step | Where (AWS console) | What to set |
|---|---|---|
| 1. VPC and subnets | VPC › Your VPCs, then Subnets | VPC 10.100.0.0/16; public subnet 10.100.1.0/24; private subnet 10.100.2.0/24 |
| 2. Internet gateway | VPC › Internet gateways | Create one and attach it to the VPC (state changes to attached) |
| 3. Launch the FortiGate-VM | AWS Marketplace listing (BYOL or on-demand), launched through EC2 | Instance type; eth0 in the public subnet, add eth1 in the private subnet; static private addresses .10 recommended |
| 4. Elastic IP | EC2 › Elastic IPs › Associate | Associate with eth0's interface and 10.100.1.10. Two interfaces means no automatic public address |
| 5. Route tables | VPC › Route tables | Public: 0.0.0.0/0 → internet gateway. Private: 0.0.0.0/0 → eth1's interface ID. Associate each with its subnet |
| 6. Source/dest. check | EC2 › Network interfaces › Actions | Disabled on each FortiGate interface |
| 7. First login | Browser to the public DNS name or EIP | Username admin, initial password is the instance ID; BYOL uploads the .lic file and reboots |
The marketplace offers a predefined security group for the instance with SSH, HTTP, HTTPS, TCP 541 for an external FortiManager and a few others open. Fortinet's own guidance is to tighten it: allow only what you need, starting with HTTPS for management. For Harbourline that means TCP 443 from anywhere (the shop) and the admin port from FGT1's public address, 203.0.113.2, only.
FortiOS settings on FGT-AWS
Inside the VM, the configuration is ordinary FortiOS. One conflict needs solving first: the shop and the admin GUI both want TCP 443 on 10.100.1.10. Moving administrative HTTPS to another port frees 443 for the VIP.
This change drops your GUI session
admin-sport moves the GUI to the new port immediately. Open the new port in the security group first, keep an SSH session open while you change it, then reconnect to https://203.0.113.50:8443.config system global
set admin-sport 8443
end
config system interface
edit "port1"
set mode static
set ip 10.100.1.10 255.255.255.0
set allowaccess ping https ssh
next
edit "port2"
set mode static
set ip 10.100.2.10 255.255.255.0
set allowaccess ping
next
end
config router static
edit 1
set gateway 10.100.1.1
set device "port1"
next
endThe interface addresses match the private IPs given to the ENIs in AWS. The default route points at the VPC router of the public subnet (its .1 address), the same next hop the internet gateway is reached through.
config firewall vip
edit "VIP-SHOP1-HTTPS"
set extintf "port1"
set extip 10.100.1.10
set mappedip "10.100.2.20"
set portforward enable
set extport 443
set mappedport 443
next
end
config firewall policy
edit 1
set name "Internet-to-SHOP1"
set srcintf "port1"
set dstintf "port2"
set srcaddr "all"
set dstaddr "VIP-SHOP1-HTTPS"
set action accept
set schedule "always"
set service "HTTPS"
set utm-status enable
set ips-sensor "default"
set logtraffic all
next
edit 2
set name "SHOP1-updates-out"
set srcintf "port2"
set dstintf "port1"
set srcaddr "all"
set dstaddr "all"
set action accept
set schedule "always"
set service "HTTP" "HTTPS" "DNS"
set nat enable
next
endThe VIP's external IP is port1's private address, because that is what packets carry after the internet gateway's 1:1 NAT. Outbound, source NAT to 10.100.1.10 lets the gateway map replies back to the Elastic IP.
In the GUI the same objects live under Policy & Objects › Virtual IPs and Policy & Objects › Firewall Policy; the route is under Network › Static Routes. The IPS sensor sees the HTTPS handshake but not the requests inside it; to inspect those, apply an SSL inspection profile that uses SHOP1's own certificate.
Learn more: Virtual IPs: Publishing ServersSSL/TLS Inspection
Checking that traffic really passes through FGT-AWS
Cloud routing mistakes are silent: traffic either takes another path or vanishes before it reaches the VM. Check from the FortiGate outwards.
FGT-AWS # get router info routing-table all Routing table for VRF=0 S* 0.0.0.0/0 [10/0] via 10.100.1.1, port1, [1/0] C 10.100.1.0/24 is directly connected, port1 C 10.100.2.0/24 is directly connected, port2
FGT-AWS # diagnose sniffer packet any 'host 10.100.2.20 and port 443' 4 port1 in 198.51.100.77.60311 -> 10.100.1.10.443: syn 2203441 port2 out 198.51.100.77.60311 -> 10.100.2.20.443: syn 2203441 port2 in 10.100.2.20.443 -> 198.51.100.77.60311: syn 3381220 ack 2203442 port1 out 10.100.1.10.443 -> 198.51.100.77.60311: syn 3381220 ack 2203442
Also run get system status once after deployment: the Version line names the VM platform image and the build, and the licence status confirms that a BYOL licence was accepted. On Azure, Fortinet's guide shows the platform as FortiGate-VM64-AZURE for BYOL and FortiGate-VM64-AZUREONDEMAND for PAYG, which is a quick way to tell which model you are looking at.
When FGT-AWS sees nothing, or too much
SHOP1 can't reach the internet, and the sniffer on port2 is empty
The private route table points 0.0.0.0/0 at eth1's interface, and the policy is correct, yet diagnose sniffer packet port2 'host 10.100.2.20' 4 shows nothing when SHOP1 tries to update.
Show the cause and fix
Source/destination check is still enabled on the FortiGate's private interface. AWS delivers packets to an interface only if they are addressed to it, and SHOP1's update traffic is addressed to servers on the internet. Disable the check on every FortiGate interface (EC2 › Network interfaces › Actions › Change source/dest. check). Also confirm the private route table is associated with the private subnet; a table that exists but isn't associated does nothing.
Visitors reach the FortiGate login page instead of the shop
The VIP was created with extip 203.0.113.50, the Elastic IP, and the admin port is still 443.
Show the cause and fix
The internet gateway has already translated 203.0.113.50 to 10.100.1.10, so the VIP doesn't match and the FortiGate answers on 443 itself. Set the VIP's extip to port1's private address, and move administrative HTTPS off 443 (or put the shop on a second private address with its own Elastic IP).
An on-demand FGT-AWS shuts itself down a few minutes after boot
The instance launches, then powers off. The private subnet works in testing, but the VM doesn't stay up.
Show the cause and fix
On-demand (PAYG) FortiGate-VMs on AWS must reach FortiCare to validate the licence; without it, the VM shuts down to protect itself. Check that the instance has a public address (an EIP when it has two interfaces) and that the security group and network ACLs allow outbound HTTPS to directregistration.fortinet.com.
Inbound HTTPS shows nothing at all on port1
The VIP and policy look right, but the sniffer on port1 stays empty while a visitor tries to connect.
Show the cause and fix
AWS filtered the traffic before the VM saw it. Check the security group on eth0 for inbound TCP 443, the subnet's network ACL, and that the EIP is associated with eth0's private address 10.100.1.10, not with eth1. In practice, most "the FortiGate drops it" tickets in a new VPC turn out to be a security group or a route table, which is why the sniffer is the first check.
BYOL or PAYG for the FortiGate-VM
| BYOL | PAYG (AWS: on-demand) | |
|---|---|---|
| Bought from | Fortinet partners and distributors | The cloud marketplace |
| Term | Perpetual or annual subscription | Hourly (some listings offer annual) |
| First use | Upload the .lic file before configuring | Usable as soon as the instance boots |
| VDOMs | Supported | Not supported |
| Cloud compute charges | Paid separately | Paid separately |
| Support | Ordered with the licence | Register the VM's serial number with FortiCare |
The models are not interchangeable on one instance, so decide before you deploy. PAYG suits short projects and uncertain sizing; BYOL suits long-lived firewalls and the same ordering process you use for other platforms. Licences are sized by vCPU, so the instance type you choose and the licence you buy must match.
The same design in Azure
The idea carries over to Azure with different names. The Azure guide's single-VM example uses three subnets: external (port1), internal (port2, a transit network) and a protected subnet for the workloads.
| AWS | Azure | |
|---|---|---|
| Network | VPC, two subnets in the guide | VNet, three subnets in the guide |
| Steering to the FortiGate | Private route table: 0.0.0.0/0 → port2 ENI | Route table with UDRs on the protected subnet: 0.0.0.0/0 → port2's private IP |
| Public address | Elastic IP, 1:1 NAT to port1 | Public IP on the NIC's IP configuration, 1:1 NAT to port1 |
| Cloud gateway | Subnet's .1 address | First usable address of the subnet |
| First login | admin and the instance ID | The username and password set in the deployment (not "admin") |
| Licence names | BYOL, on-demand | BYOL, PAYG |
Larger designs build on this one: an active-passive pair (on failover, the new primary uses AWS or Azure API calls to take over addresses and routes), or several FortiGate-VMs behind a cloud load balancer with autoscaling. Fortinet's SDN connectors let policies match cloud metadata such as instance tags instead of fixed addresses, which deals with the constant-change problem above.
Learn more: High Availability (FGCP)
FortiGate CNF: the firewall you don't operate
A FortiGate-VM is yours to size, patch, license, cluster and scale. FortiGate CNF (Cloud-Native Firewall) removes that work. It is a software-as-a-service firewall: Fortinet runs the firewall infrastructure in AWS or Azure, scales it on throughput, CPU and memory, and you manage policy from the FortiGate CNF console. One CNF instance serves one region and can protect many VPCs (or VNets), availability zones and accounts with a shared policy.
The onboarding wizard on first login walks through three steps:
- Add cloud accounts. FortiGate CNF needs permissions in each account it protects, granted by running the CloudFormation template (AWS) or ARM template (Azure) it provides. The billing account can be a different one.
- Create a policy set. An ordered group of policies, applied to an instance as a whole. A built-in
allow_allset exists for testing routing only; it gives no protection. - Create the CNF instance and its endpoints. In AWS, each endpoint is a Gateway Load Balancer endpoint placed in a subnet you created and tagged (
fortigatecnf_subnet_type=endpoint). Your route tables then send traffic to that endpoint, much as the private route table above sends it to FGT-AWS.
In the CNF console, security profiles offer DNS filtering, known bad IP blocking and a choice of preset IPS profiles. Instances created in FortiManager mode are managed from FortiManager instead, where the wider set of FortiOS profiles is available. Logs can go to an S3 bucket or AWS Security Lake, and externally to syslog or FortiAnalyzer.
| FortiGate-VM | FortiGate CNF | |
|---|---|---|
| Who runs the firewall | You: instance, firmware, HA, scaling | Fortinet, as a managed service |
| How traffic arrives | Route tables to its interfaces | Route tables to CNF endpoints (GWLB endpoints in AWS) |
| Features | Full FortiOS, including VPNs and SD-WAN | Policy sets with DNS filter, bad IP blocking, IPS presets (more through FortiManager mode) |
| Fits | VPN hub, full NGFW at the VPC edge, one consistent FortiOS estate | Many VPCs and accounts in a region needing egress and east-west filtering with little operations work |
For Harbourline the choice is clear for now: one shop server, a VPN back to FGT1 planned, and a team that already runs FortiOS. FGT-AWS fits. If the company grows to dozens of VPCs across several accounts, CNF becomes the cheaper option to operate.
Try it: add a database subnet behind FGT-AWS
Harbourline adds a database, DB1 at 10.100.3.20 in a new subnet 10.100.3.0/24, and wants all traffic between SHOP1 and DB1 inspected by FGT-AWS. On paper (or in a sandbox AWS account), decide: does FGT-AWS need a third interface; which route tables need which new routes so SHOP1 → DB1 traffic doesn't take the local route directly; where source/destination check matters; and which FortiOS policy allows TCP 5432 from SHOP1 to DB1 only. Then write the sniffer command that would prove the traffic passes through the FortiGate.
- Route tables steer traffic to the FortiGate; the workloads keep the VPC router as their gateway.
- Source/destination check is disabled on every FortiGate interface in AWS.
- VIPs and policies use port1's private address, because the cloud translates the public one first.
- BYOL or PAYG is decided at deployment and can't be switched on the same VM.
Test yourself on FortiGate in the public cloud
SHOP1's private subnet has a route table with 0.0.0.0/0 → FGT-AWS port2's interface. SHOP1's own default gateway is still 10.100.2.1. Will SHOP1's internet traffic pass through FGT-AWS?
Inbound connections to the Elastic IP 203.0.113.50 don't match the VIP. The VIP's extip is 203.0.113.50. What should it be?
A sniffer on FGT-AWS port2 shows nothing when SHOP1 tries to reach the internet. The route table is correct and associated. What is the most likely cause?
A team wants to run VDOMs on a new FortiGate-VM in Azure. Which licensing model must they choose?
A company has 40 VPCs in one AWS region across six accounts and wants egress filtering with known bad IP blocking and IPS, without running firewall instances. What fits best?