Tim's blah blah blah

StrongSwan IKEv2 VPN on Raspberry pi

(Updated: )

Guide to set up road warrior VPN server (i.e. road warrior = mobile clients connecting to static server, vs e.g. site-to-site connection) using IKEv2 using strongswan on a raspberry pi. This guide is largely based on this digitalocean guide (digitalocean.com) combined with ready-made strongswan configurations (strongswan.org).

Update 20181224 : added algo VPN configurator
Update 20190223 : added cipher analysis / recommendation, clarified eth0 interface use on server, ESP/AH forwarding, added password generation one-liner.
Update 20200801 : minor fixes in commands.


Rationale for IKEv2/Strongswan

I’ve decided to go for IKEv2 (wikipedia.org) for two main reasons:

  1. it’s natively supported by iOS and macOS and
  2. it only requires strongswan (strongswan.org) to operate. Two other options are 1) OpenVPN: requires non-native app/program to connect. 2) IPSEC/L2TP: requires xl2tpd on top of *swan.

After deciding on IKEv2, there are four main contenders for implementation. I chose strongswan (strongswan.org) because (serverfault.com)

  1. it’s available in the default Raspbian/debian apt repository and
  2. it appears newer than openswan.

Libreswan dropped out because it’s not available from the default apt repo, so it required compiling from source, which would’ve taken a long time on a raspberry pi. Softether (softether.org) also looks very promising (vpnxd.com) (thanks Sterk1!) as it supports a wide range of VPN protocols. Unfortunately like Libreswan this is also not available from the default apt repo.

Alternative guides

I found some alternative guides/setups that could also work very well:

Automated installer scripts

There are a few scripts that automate VPN setup. One requirement is that you trust the script to run as root, which I prefer not to. The advantage is that these scripts might have thought of more than I did, and are better maintained than static guides.

Manual guides

Other guides have instructions to install VPN manually:

Target setup

The target setup is as follows:


Ciphers and security recommendations

These (cisco.com) guides (cisco.com) have a nice overview of cipher security, from which I distilled this selection:

When authenticating by password, use long passwords with at least the entropy content as the cipher strength (e.g. 128 bit for AES128).

The default ciphers on StrongSwan are reasonably OK, although unfortunately the broken MD5, SHA1, and 3DES are also included (perhaps for compatibility). Unfortunately, I haven’t found a way to remove support for specific ciphers, and removing the plugins from /etc/strongswan.d/charon does not work.

There are two ways to fix this:

  1. Set secure ciphers server-side manually and explicitly
  2. Choose secure ciphers client-side

Server-side ciphers

The relevant ipsec.conf settings (strongswan.org) are esp and ike.

ESP syntax is encryption-integrity[-dhgroup][-esnmode], for IKE this is encryption-integrity[-prf]-dhgroup. By using encryption-integrity-dhgroup, these are compatible
with each other. Unfortunately you have to list all permutations of the encryption, integrity, and signing ciphers. Don’t forget to add an exclamation mark to enforce these ciphers!

If you want to specifically enable ciphers on the server, you could use this python script (not tested!).

encrypt =['aes128', 'aes192','aes256','aes128gcm16','aes192gcm16','aes256gcm16']
sign = ['modp2048','modp3072','modp4096','ecp256','ecp384','ecp521']

ciphersuites = [e+'-'+i+'-'+s for e in encrypt for i in integrity for s in sign]

Client-side ciphers

See my other post on creating iOS profiles (vanwerkhoven.org).


Since this is running on a raspberry, speed is also relevant. I found these results on my RPi3B+ using openssl speed -evp (stackexchange.com) and openssl speed (stackexchange.com). Although the speeds are to some extent in arbitrary units, the relative values should be comparable. Based on this I chose to use SHA256 with AES-128-CBC and ECP256 for my own setup.

The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes  16384 bytes
md5              24634.10k    74728.55k   154655.66k   211365.59k   224220.50k   232265.05k
sha1              6816.60k    22071.29k    53680.45k    82852.25k    99040.45k    92405.76k
sha256            6001.71k    18437.16k    46062.42k    68029.68k    74043.08k    76436.82k
sha384            3025.30k    12140.95k    19447.72k    29114.71k    34839.29k    32287.40k
aes-128-cbc      30789.43k    43457.24k    47193.47k    47911.17k    48994.78k    48894.46k
aes-192-cbc      38234.35k    38777.04k    43397.33k    43725.83k    42692.50k    42976.49k
aes-256-cbc      33992.59k    36185.00k    35603.11k    38232.87k    37623.13k    35748.90k
aes-128-gcm      21008.15k    25713.49k    27408.30k    27992.41k    28466.52k    26858.84k
aes-192-gcm      19203.80k    23740.33k    25268.31k    24521.16k    21925.65k    22093.74k
aes-256-gcm      18081.80k    22149.27k    22574.48k    22588.82k    20678.59k    22074.42k

                          sign    verify    sign/s verify/s
256 bit ecdsa (nistp256)   0.0005s   0.0012s   1944.2    838.7
384 bit ecdsa (nistp384)   0.0204s   0.0109s     49.1     91.9
dsa 2048 bits               0.008810s 0.008716s  113.5    114.7    

dsa4096 and ecdsap512 gave a segfault on openssl, not sure why.

Install required software

Plugins are required to support EAP-MSCHAPv2 (strongswan-ikev2 might not be needed anymore as of Ubuntu 20.04). netfilter-persistent is used to make iptables rules persistent.

sudo apt install strongswan strongswan-pki strongswan-ikev2 \
      libcharon-extra-plugins libstrongswan-extra-plugins netfilter-persistent   

Generate certificates and keys

More or less directly copied from the guide mentioned above (digitalocean.com). Ideally run this as a trusted user in a directory that nobody else can access:

# ensure no other use can read this
mkdir ~/vpn-certs/
cd ~/vpn-certs/
chmod 700 .

ipsec pki --gen --type rsa --size 4096 --outform pem > server-root-key.pem
chmod 600 server-root-key.pem

ipsec pki --self --ca --lifetime 3650 \
    --in server-root-key.pem \
    --type rsa --dn "C=${SERVER_COUNTRY}, O=VPN Server, CN=VPN Server Root CA" \
    --outform pem > server-root-ca.pem

ipsec pki --gen --type rsa --size 4096 --outform pem > vpn-server-key.pem

ipsec pki --pub --in vpn-server-key.pem \
    --type rsa | ipsec pki --issue --lifetime 1825 \
    --cacert server-root-ca.pem \
    --cakey server-root-key.pem \
    --dn "C=${SERVER_COUNTRY}, O=VPN Server, CN=${SERVER_FQDN}" \
    --san ${SERVER_FQDN} \
    --flag serverAuth --flag ikeIntermediate \
    --outform pem > vpn-server-cert.pem

sudo cp ./vpn-server-cert.pem /etc/ipsec.d/certs/vpn-server-cert.pem
sudo cp ./vpn-server-key.pem /etc/ipsec.d/private/vpn-server-key.pem

sudo chown root /etc/ipsec.d/private/vpn-server-key.pem
sudo chgrp root /etc/ipsec.d/private/vpn-server-key.pem
sudo chmod 600 /etc/ipsec.d/private/vpn-server-key.pem


# based on 
# https://www.digitalocean.com/community/tutorials/how-to-set-up-an-ikev2-vpn-server-with-strongswan-on-ubuntu-16-04
# combined with improved cipher selection from 
# https://wiki.strongswan.org/projects/strongswan/wiki/UsableExamples 
# with improved cipher selection

config setup
    charondebug="ike 1, knl 1, cfg 0"

conn ikev2-vpn

    # Manually specify the ciphers here if you want.

    # alternatively, set rightsourceip=%dhcp to get DHCP'ed IPs from wherever the server is getting IPs from.

If you want to select your own ciphers, read here (strongswan.org) on how to configure, here on what key length is recommended (keylength.com), and here for general security recommendations (strongswan.org).


${SERVER_FQDN} : RSA "/etc/ipsec.d/private/vpn-server-key.pem"

N.B. Make sure your password is strong enough! To generate a strong password, use the following (source (strongswan.org)):

dd if=/dev/urandom count=1 bs=32 2>/dev/null | base64

This gives a 32-character length string of 6 bit printable characters (base64 is 6-bit) such that the password strength is 32*6 = 192bit.


# ensure we don't lock our ssh session out
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# always accept loopback
sudo iptables -A INPUT -i lo -j ACCEPT

# accept ports 500 and 4500, required for IKEv2
sudo iptables -A INPUT -p udp --dport  500 -j ACCEPT
sudo iptables -A INPUT -p udp --dport 4500 -j ACCEPT

# forward ESP
sudo iptables -A FORWARD --match policy --pol ipsec --dir in  --proto esp \
sudo iptables -A FORWARD --match policy --pol ipsec --dir out --proto esp \

# more forwarding
sudo iptables -t nat -A POSTROUTING -s ${VPN_SUBNET} -o ${VPN_IFACE} -m policy \
  --pol ipsec --dir out -j ACCEPT
sudo iptables -t nat -A POSTROUTING -s ${VPN_SUBNET} -o ${VPN_IFACE} -j MASQUERADE

# fix fragmentation
sudo iptables -t mangle -A FORWARD --match policy --pol ipsec --dir in \
  -s ${VPN_SUBNET} -o ${VPN_IFACE} -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss \
  --mss 1361:1536 -j TCPMSS --set-mss 1360

# Drop everything we don't accept - for added security. 
# NB only add if you know what you're doing,
# I accidentally locked out my grafana/influxdb setup here :p
# Also, for a router behind NAT I think this is superfluous.
#sudo iptables -A INPUT -j DROP
#sudo iptables -A FORWARD -j DROP

# make persistent
sudo netfilter-persistent save
sudo netfilter-persistent reload

To review your iptables rules, this StackExchange post explains how to view all iptables tables (stackexchange.com), by default only the filter table is shown:

iptables -vL -t filter
iptables -vL -t nat
iptables -vL -t mangle
iptables -vL -t raw
iptables -vL -t security


# Uncomment the next line to enable packet forwarding for IPv4

# Do not accept ICMP redirects (prevent MITM attacks)
net.ipv4.conf.all.accept_redirects = 0
# Do not send ICMP redirects (we are not a router)
net.ipv4.conf.all.send_redirects = 0

# Not sure if this is required, I added it to be sure
net.ipv4.ip_no_pmtu_disc = 1

Run sysctl -p after updating. If that doesn’t work, reboot.

Port forwarding

Optionally (almost always), ensure port-forwarding on your home router is enabled such that UDP 4500 and 500 on the server are reachable from the internet. If using forceencaps (strongswan.org) as I did above, these are the only ports you need. If not, make sure you also forward protocols ESP (wikipedia.org) and AH (wikipedia.org), but be aware that NAT’ing ESP and AH (internet-computer-security.com) is apparently not always supported.

Connecting clients

Ensure you install server-root-ca.pem on any clients. On macOS, after adding to keychain, make sure to trust it explicitly.

For iOS/macOS, Server and Remote ID are the same FQDN of your server.

After setting everything up, you’re done!


IF you need to support several EAP methods at the same time, you need to use eap-dynamic and not use any other conn with eap settings. Add the settings for the eap-dynamic plugin to your strongswan.conf file.

#Linux #RaspberryPi #Security