Route based VPN between FortiGate and strongSwan
![thumbnail for this post](https://dosztal.com/blog/vpn-fortigate-strongswan/cover.jpg)
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:
- VPN tunnels for WAN backup between a FortiGate firewall and Cisco routers
- VPN tunnel between Cisco and VyOS routers using VTIs
- VPN tunnel between Cisco and VyOS behind NAT
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](swan_l1.png)
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](swan_l3.png)
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](swan_ike1.png)
![FGT IKE settings #2](swan_ike2.png)
![FGT IKE settings #3](swan_ike3.png)
![FGT IKE settings #4](swan_ike4.png)
Step 2: After clicking OK, the VTI appears in the interface list:
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.
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.
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