Andras Dosztal
Andras Dosztal
Network architect
Nov 16, 2017 6 min read

Route based VPN between FortiGate and strongSwan

thumbnail for this post

The next chapter in my “VPN between Vendor A and Vendor B” series is about connecting a FortiGate firewall with strongSwan running on a Linux host. For the latter I’m using Ubuntu 17.04 but any other distribution will work fine.

If you’re interested in multi-vendor VPN setups, here are my other articles in the topic:

Layer 1 setup

I’ve created a small topology where the Linux host running strongSwan and the FortiGate VM are directly connected. The VPCS node represents a host on the firewall’s local network. Ethernetswitch-1 and the connected neighbor ports are used as an out of band management network; they have nothing to do with the solution described here.

Layer 1 topology

Layer 3 setup

The used subnets and host IPs are shown on the figure below. The blue line indicates the VPN tunnel.

Layer 3 topology

VPN parameters

The following parameters are used:

  • IKE version: v1
  • Encryption: AES256
  • Hash: SHA256
  • DH group: 21
  • Authentication: PSK
  • DPD: Enabled
  • NAT Traversal: Enabled

The same encryption, hash, and DH group is used both for Phase 1 and Phase 2.

FortiGate Settings

Step 1: Create the VPN tunnel using the “Custom” template and the following settings. The PSK was 123123123 in this lab (you’ll see it later in the strongSwan config files).

FGT IKE settings #1
FGT IKE settings #2
FGT IKE settings #3
FGT IKE settings #4

Step 2: After clicking OK, the VTI appears in the interface list:

FTG interfaces

Step 3: Add static routes. Select the VPN interface as the device. Note: You can’t (and don’t need to) set the gateway for these routes.

Static routing

strongSwan settings

StrongSwan stores its settings in config files. Modify them with the tunnel parameters, as well as the sysctl.conf to enable routing on the Linux host. Make sure the “mark” key has the same value as the “vti key” (shown later, both highlighted with red).

/etc/ipsec.conf:

%default
  authby=secret
  type=tunnel
  auto=route
  compress=no

conn FortiGate
  left=10.0.0.2
  leftsubnet=0.0.0.0/0
  right=10.0.0.1
  rightsubnet=0.0.0.0/0
  leftfirewall=no
  keyexchange=ikev1
  ike=aes256-sha256-ecp521
  esp=aes256-sha256-ecp521
  mark=42
  dpdaction=reset 

/etc/sysctl.conf:

net.ipv4.ip_forward=1
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

/etc/ipsec.secrets:

# Set your PSK here. Format:
# REMOTE_IP LOCAL_IP : PSK "YOUR_PSK"
10.0.0.2 10.0.0.1 : PSK "123123123"

Run these CLI commands on the Linux box after bringing up the strongSwan daemon:

# Create a loopback adapter; this is not mandatory but useful in certain cases (e.g. if you want to run OSPF).
sudo add 2.2.2.2/32 dev lo:0

# Create the VTI; the key has to match the mark value in ipsec.conf; you can use different values for additional tunnels.
sudo ip tunnel add vti0 local 10.0.0.2 remote 10.0.0.1 mode vti key 42

# Bring up the interface.
sudo ip link set vti0 up

# Assign an IP address to the VTI.
sudo ip addr add 10.1.1.2/32 dev vti0

# Apply the modified sysctl parameters.
sudo sysctl -p

# Add static routes.
sudo ip route add 10.1.1.1/32 dev vti0
sudo ip route add 192.168.1.0/24 dev vti0
sudo ip route add 1.1.1.1/32 dev vti0

Note: To make these settings persistent, you need to add them in your distro’s appropriate config files.

Policies

Don’t forget to add policies to allow traffic through the tunnel interfaces. If you’re working in a lab environment, you can start from “permit any any” to make sure the traffic doesn’t get blocked; obviously you should never do this on production systems or if your lab is directly connected to the internet.

Testing

If no errors were made, the tunnel should be up by now. You can verify its status by doing the checks described below.

Important: I ran into a bug where the FortiGate showed its interface as up but the static route did not appear in the routing table (it was marked as inactive in the database). In this case, shut down the tunnel interface, then enable it again.

Tunnel status

strongSwan:

$ sudo ipsec status FortiGate
Routed Connections:
   FortiGate{1}:  ROUTED, TUNNEL, reqid 1
   FortiGate{1}:   0.0.0.0/0 === 0.0.0.0/0
Security Associations (1 up, 0 connecting):
   FortiGate[1]: ESTABLISHED 8 minutes ago, 10.0.0.2[10.0.0.2]...10.0.0.1[10.0.0.1]
   FortiGate{2}:  INSTALLED, TUNNEL, reqid 1, ESP SPIs: c7e29a79_i 32b2a23d_o
   FortiGate{2}:   0.0.0.0/0 === 0.0.0.0/0
osboxes@osboxes:~$ ip addr show dev vti0
5: vti0@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1332 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ipip 10.0.0.2 peer 10.0.0.1
    inet 10.1.1.2/32 scope global vti0
       valid_lft forever preferred_lft forever

FortiGate:

You can either use the GUI or the CLI to check the tunnel status.

Tunnel status
Tunnel status

FGVM000000114668 # get vpn ipsec tunnel name swan
gateway
  name: 'swan'
  type: route-based
  local-gateway: 10.0.0.1:0 (static)
  remote-gateway: 10.0.0.2:0 (static)
  mode: ike-v1
  interface: 'port2' (4)
  rx  packets: 11  bytes: 1716  errors: 0
  tx  packets: 14  bytes: 1020  errors: 1
  dpd: enabled/negotiated  idle: 5000ms  retry: 3  count: 0
  selectors
    name: 'swan'
    auto-negotiate: enable
    mode: tunnel
    src: 0:0.0.0.0/0.0.0.0:0
    dst: 0:0.0.0.0/0.0.0.0:0
    SA
      lifetime/rekey: 43200/42611 
      mtu: 1438
      tx-esp-seq: f
      replay: enabled
      inbound
        spi: 32b2a23d
        enc:     aes  ebeb10e535b1de811a07a5f56218530311d8ffc1da72f23e43e6b8df2d1d26ee
        auth: sha256  ca977d29fafa295f7d9db75cf006911115693e961f9fc269f6746426bea44ee4
      outbound
        spi: c7e29a79
        enc:     aes  3f84e5659009e7cff7f9c723e29cbcaf681d852580a347f0cd184f4fc0b88dfc
        auth: sha256  d12992231c4300be6a59016fe17b2f7c4c5bdc639831a724688045200ac40813

Routing

strongSwan:

$ sudo route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.26.1.1      0.0.0.0         UG    100    0        0 ens3
1.1.1.1         0.0.0.0         255.255.255.255 UH    0      0        0 vti0
10.0.0.0        0.0.0.0         255.255.255.0   U     100    0        0 ens4
10.1.1.1        0.0.0.0         255.255.255.255 UH    0      0        0 vti0
169.254.0.0     0.0.0.0         255.255.0.0     U     1000   0        0 ens4
172.26.1.0      0.0.0.0         255.255.255.0   U     100    0        0 ens3

FortiGate:

FGVM000000114668 # get router info routing-table all
Codes: K - kernel, C - connected, S - static, R - RIP, B - BGP
       O - OSPF, IA - OSPF inter area
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       i - IS-IS, L1 - IS-IS level-1, L2 - IS-IS level-2, ia - IS-IS inter area
       * - candidate default

S*      0.0.0.0/0 [10/0] via 172.26.1.1, port1
C       1.1.1.1/32 is directly connected, lo0
S       2.2.2.2/32 [10/0] via 10.1.1.2, swan
C       10.0.0.0/24 is directly connected, port2
C       10.1.1.1/32 is directly connected, swan
C       10.1.1.2/32 is directly connected, swan
C       172.26.1.0/24 is directly connected, port1
C       192.168.1.0/24 is directly connected, port5

Ping

All commands here were executed on the Linux host.

$ ping 10.1.1.1 -c 5
PING 10.1.1.1 (10.1.1.1) 56(84) bytes of data.
64 bytes from 10.1.1.1: icmp_seq=1 ttl=255 time=0.446 ms
64 bytes from 10.1.1.1: icmp_seq=2 ttl=255 time=0.506 ms
64 bytes from 10.1.1.1: icmp_seq=3 ttl=255 time=1.17 ms
64 bytes from 10.1.1.1: icmp_seq=4 ttl=255 time=0.622 ms
64 bytes from 10.1.1.1: icmp_seq=5 ttl=255 time=0.600 ms

--- 10.1.1.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4061ms
rtt min/avg/max/mdev = 0.446/0.669/1.171/0.258 ms


$ ping 1.1.1.1 -I 2.2.2.2 -c 5
PING 1.1.1.1 (1.1.1.1) from 2.2.2.2 : 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=255 time=0.972 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=255 time=0.522 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=255 time=1.57 ms
64 bytes from 1.1.1.1: icmp_seq=4 ttl=255 time=0.715 ms
64 bytes from 1.1.1.1: icmp_seq=5 ttl=255 time=1.05 ms

--- 1.1.1.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4031ms
rtt min/avg/max/mdev = 0.522/0.966/1.570/0.356 ms


$ ping 192.168.1.10 -I 2.2.2.2 -c 5
PING 192.168.1.10 (1.1.1.1) from 2.2.2.2 : 56(84) bytes of data.
64 bytes from 192.168.1.10: icmp_seq=1 ttl=255 time=0.997 ms
64 bytes from 192.168.1.10: icmp_seq=2 ttl=255 time=0.645 ms
64 bytes from 192.168.1.10: icmp_seq=3 ttl=255 time=0.756 ms
64 bytes from 192.168.1.10: icmp_seq=4 ttl=255 time=1.10 ms
64 bytes from 192.168.1.10: icmp_seq=5 ttl=255 time=0.802 ms

--- 192.168.1.10 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4310ms
rtt min/avg/max/mdev = 0.645/0.862/1.101/0.321 ms