VPNs to Google Cloud Platform (GCP) when FortiGate is behind a NAT gateway

Ran in to problems getting a VPN up and running between GCP and a FortiGate 60-E that was behind a NAT gateway (with ports udp/500 + udp/4500 forwarded). On the GCP side, these messages would show up in the logs:

remote host is behind NAT
generating IKE_AUTH response 1 [ N(AUTH_FAILED) ]
looking for peer configs matching GCP.VPN.GATEWAY.IP[%any]...[]

This error means that GCP connected to the Peer VPN gateway successfully, but it in the IKEv2 headers, it identified itself by the private IP rather than the expected public one. AWS is not picky about this, but with GCP, the Peer VPN gateway must identify itself by using the same external IP address of the NAT device.

Most vendors have long supported an option to manually override the IP address for such scenarios. In Cisco IOS or IOS-XE, this can be controlled in the IKEv2 profile with the identity local address option:

crypto ikev2 profile GCP_IKEV2_PROFILE
 match address local interface GigabitEthernet1
 identity local address MY.PUBLIC.IP.ADDRESS
 authentication remote pre-share
 authentication local pre-share
 keyring local GCP_KEYRING
 lifetime 36000
 dpd 20 5 on-demand

With Palo Alto, this is configured in the IKE Gateway, Local Identification field:

For the sake of argument, we’ll say that CheckPoint uses the “Statically NATed IP” field to influence Local ID, although this doesn’t actually work.

Fortigate does offer “Local ID” field in version 6.4.6 and higher, under the Phase 1 proposal:

Seems nice and straightforward, but even after changing this setting, the VPN tunnel still won’t establish. Logs on the GCP end change slightly and now show this:

looking for peer configs matching GCP.VPN.GATEWAY.IP[%any]...[]

The private IP is no longer showing, so it seems the issue should be solved. Instead, GCP reports a “Peer not responding” message. The Fortigate actually reports Phase 1 success, waits a few seconds, and then starts the negotiation all over. So not very helpful.

I configured a test VPN between the FortiGate and a Palo Alto, which then gave a very specific and extremely useful error message:

IKE phase-1 negotiation is failed. When pre-shared key is used, peer-ID must be type IP address. Received type FQDN

Now this explains the problem! Even though the FortiGate is sending the correct IP address in the IKEv2 header, it’s being sent as the wrong identity type. The 5 identity types are listed in RFC 7815:

  • ID_IPV4_ADDR = 32 bit IPv4 address
  • ID_IPV6_ADDR = 128 bit IPv6 address
  • ID_FQDN = DNS hostname
  • ID_RFC822_ADDR = e-mail address
  • ID_KEY_ID = octet stream

If Fortigate were smart, it would either default to IPv4 address type or auto-determine this based on the text inputted in to the field. But it seems to simply default to FQDN. Oddly, there is a CLI option called “localid-type” under the Phase1-interface that clear is intended to provide this functionality:

FGT60E1234567890 # config vpn ipsec phase1-interface

FGT60E1234567890 (phase1-interface) # edit gcp

FGT60E1234567890 (gcp) # set localid-type 
auto         Select ID type automatically.
fqdn         Use fully qualified domain name.
user-fqdn    Use user fully qualified domain name.
keyid        Use key-id string.
address      Use local IP address.

But, similar to CheckPoint, it just doesn’t work, and can be considered a broken feature.

Since GCP does not support FQDN authentication, VPNs between GCP and FortiGates behind a NAT are not possible at this time.


FortiGate 60-E not supporting AES-GCM in Hardware

On a previous post I’d recommended using AES-GCM on VPNs to AWS and GCP since it’s generally a more efficient algorithm that offers higher throughput. So it came as a surprised today doing some deep-diving on my Fortigate 60-E today: AES-GCM is supported for Phase 2, it is not supported in hardware on the NPU:

FGT60ETK18XXXXXX # get vpn ipsec tunnel details
   name: 'aws1'
   type: route-based
   local-gateway: (static)
   remote-gateway: (static)
   mode: ike-v1
   interface: 'wan1' (5)
   rx  packets: 52  bytes: 6524  errors: 0
   tx  packets: 110  bytes: 6932  errors: 3
   dpd: on-demand/negotiated  idle: 20000ms  retry: 3  count: 0
   nat traversal mode: keep-alive   RFC 3947   interval: 10
     name: 'aws1'
     auto-negotiate: disable
     mode: tunnel
     src: 0:
     dst: 0:
       lifetime/rekey: 3600/3289   
       mtu: 1438
       tx-esp-seq: 2
       replay: enabled
       qat: 0
         spi: 7dbc0283
         enc:  aes-gc  35a72036fa9a87000c90415b1863827652bf9dfd875f28a6d20fd1569e5c0099de639dcc
         auth:   null  
         spi: ccdb6ab8
         enc:  aes-gc  21dd5c71a83142b0ecee1efe2c000c0dae586054160eb76df6f338d9071380b12103b0d9
         auth:   null  
       NPU acceleration: none

FGT60ETK18XXXXXX # get vpn ipsec stats crypto
NPU Host Offloading:
     Encryption (encrypted/decrypted)
     null      : 0                0               
     des       : 0                0               
     3des      : 0                0               
     aes-cbc   : 833309           0               
     aes-gcm   : 0                0               
     aria      : 0                0               
     seed      : 0                0               
     chacha20poly1305: 0                0               
     Integrity (generated/validated)
     null      : 0                0               
     md5       : 0                0               
     sha1      : 803671           0               
     sha256    : 29540            0               
     sha384    : 48               0               
     sha512    : 50               0 

Since the hardware offload isn’t occurring, the main CPU is moderately taxed when doing transfers via AES-GCM. Also, the throughput is only ~ 100 Mbps:

Reconfiguring the VPNs to AES-CBC and redoing the transfers, we get lower CPU usage and significantly higher throughput:

AWS or GCP IPSec Tunnels with BGP routing on a FortiGate software version 6.x

To use BGP routing on an AWS or GCP VPN connection, the tunnel interface needs to have its IP address assigned as a /32 and then the remote IP specified:

config system interface
    edit "GCP"
        set vdom "root"
        set ip
        set type tunnel
        set remote-ip
        set interface "wan1"

BGP can be configured under the GUI in Network -> BGP in most cases, but the CLI has additional options. Here’s an example config for a peer with ASN 64512, announcing the prefix.

config router bgp
    set as 65000
    set router-id
    set keepalive-timer 10
    set holdtime-timer 30
    set scan-time 15
    config neighbor
       edit ""
           set remote-as 64512
    config network
        edit 1
            set prefix

FortiGate Initial Config via CLI

Configure Network Interfaces


  • Set wan1 interface to static IP address
  • Configured default route of
  • Allow ping and HTTPS on wan1 interface
  • Set LAN interface to
  • Set LAN interface’s DHCP scope address range as
  • Change lease time from default of 7 days to 2 hours
config system interface
 edit wan1
  set mode static
  set ip
  set allowaccess ping https fgfm
 edit lan
  set ip
config router static
 edit 0 
  set gateway
  set distance 1
  set device wan1
config sys dhcp server
 edit 1
  set default-gateway
  set netmask
  set lease-time 7200
  config ip-range
   edit 1
    set start-ip
    set end-ip

Other useful commands

Change the admin user password:

config system admin
 edit admin
 set password MyNewPassword end

Create a secondary admin user

config system admin
    edit secondadminusername
      set accprofile super_admin
      set password MyPasswordGoesHere

Get L1 and L3 status of all interfaces

get system interface physical

Disabled/Enable an interface

config system interface
 edit lan
  set status down
 edit lan
  set status up

Enable sending of LLDP information

config system global
    set lldp-transmission enable

Check the route table

get router info routing-table all

Check the ARP Table

get system arp

Get the software version and serial number

get system status

Ping something

execute ping

Reboot the firewall

execute reboot



Authentication to Synology Directory Server (LDAP Server)

Upon configuring Directory Server the Synology will provide something like this:

The password configured is password for the ‘root’ user

Configuration for Cisco ASA / AnyConnect

aaa-server SYNOLOGY protocol ldap
aaa-server SYNOLOGY (Inside) host
 ldap-base-dn dc=myserver,dc=mydomain,dc=com
 ldap-scope subtree
 ldap-naming-attribute uid
 ldap-login-password <root user password>
 ldap-login-dn uid=root,cn=users,dc=myserver,dc=mydomain,dc=com
 server-type auto-detect

Configuration for FortiGate GUI

  • Common Name Identifier = uid
  • Distinguished Name = cn=users,dc=myserver,dc=mydomain,dc=com
  • Bind Type = Simple

Configuration for F5 BigIP

Need to change Authentication from ‘Basic’ to ‘Advanced’ to set Login LDAP attribute

  • Remote Directory Tree: dc=myserver,dc=mydomain,dc=com
  • Scope: Sub
  • BIND DN: uid=root,cn=users,dc=myserver,dc=mydomain,dc=com
  • Password: <root user password>
  • User Template: uid=%s,cn=users,dc=myserver,dc=mydomain,dc=com
  • Login LDAP Attribute: uid

To use Remote Role Groups:

Attribute String: memberOf=cn=users,cn=groups,dc=myserver,dc=mydomain,dc=com


Importing SSL Certificate on FortiGate 90D

The instructions talk about importing via GUI, but lower end platforms like the F90D don’t have that option.

Instead, certificates must be imported via a copy/paste job in the CLI

FWF90D5318062277 # config vpn certificate local
FWF90D5318062277 (local) # edit MyNewCert
FWF90D5318062277 (MyNewCert) # set private-key "-----BEGIN RSA PRIVATE KEY-----..."
FWF90D5318062277 (MyNewCert) # set certificate "-----BEGIN CERTIFICATE-----..."
FWF90D5318062277 (MyNewCert) # end

The certificate can then be set in the GUI.

FortiGate Static NAT using Port Forwarding / PAT

Easy in hindsight, but may be counter-intuitive for those coming from a Cisco or Palo Alto background such as myself.  There are two steps:

  1. Under Policy & Objects -> Virtual IPs, add a statement for each PAT rule with the “Port Forwarding” switch enabled at the bottom.
  2. Under Policy & Objects -> IPv4 Policy, add a rule from the public interface to the private interface with destination to be the object(s) created and service set to ALL.  NAT switch should remain disabled.