StrongSwan IKEv2 VPN on Raspberry pi

By Atomstar on Monday 24 December 2018 01:08 - Comments (6)
Categories: Linux, RaspberryPi, Security, Views: 5.305

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 combined with ready-made strongswan configurations.

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.

Rationale for IKEv2/Strongswan

I've decided to go for IKEv2 for two main reasons:
  1. it's natively supported by iOS and macOS and
  2. it only requires strongswan 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 because
  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 also looks very promising (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:
  • My network is on 172.16.0.0/24,
  • the router/DNS server is available on 172.16.0.1,
  • the raspberry pi is at ${SERVER_FQDN}.
  • I want the VPN clients to be on 172.16.1.0/24 and use the same DNS server. Alternatively instead of using virtual IPs, you can assign DHCP'ed IPs to VPN clients by setting VPN_SUBNET to %dhcp
To this end, set the following variables:
SERVER_FQDN="my.router.com"
DNS_SERVER="172.16.0.1"
VPN_SUBNET="172.16.1.0/24"
VPN_IFACE="eth0"
SERVER_COUNTRY="NL"
YOUR_USERNAME="vpnuser"
YOUR_PASSWORD="vpnpassword"


Ciphers and security recommendations

These guides have a nice overview of cipher security, from which I distilled this selection:

Encryption: aes128, aes192, aes256, aes128gcm16, aes192gcm16, aes256gcm16
Integrity: sha256, sha384, sha512
Sign: modp2048, modp3072, modp4096, ecp256, ecp384, ecp521

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 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!).


code:
1
2
3
4
5
6
encrypt =['aes128', 'aes192','aes256','aes128gcm16','aes192gcm16','aes256gcm16']
integrity=['sha256','sha384','sha512']
sign = ['modp2048','modp3072','modp4096','ecp256','ecp384','ecp521']

ciphersuites = [e+'-'+i+'-'+s for e in encrypt for i in integrity for s in sign]
",".join(ciphersuites)+"!"

Client-side ciphers
See my other post on creating iOS profiles.
Speed
Since this is running on a raspberry, speed is also relevant. I found these results on my RPi3B+ using openssl speed -evp <cipher> and openssl speed <sign>. 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
sudo apt get install strongswan strongswan-pki strongswan-ikev2 \
  libcharon-extra-plugins libstrongswan-extra-plugins


Generate certificates and keys

More or less directly copied from the guide mentioned above. 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 600 .

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


/etc/ipsec.conf

# 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"
    uniqueids=no

conn ikev2-vpn
    auto=add
    compress=no
    type=tunnel
    keyexchange=ikev2
    fragmentation=yes
    forceencaps=yes

    # Manually specify the ciphers here if you want.
    #ike=aes192gcm16-aes128gcm16-prfsha256-ecp256-ecp521,aes192-sha256-modp3072
    #esp=aes192gcm16-aes128gcm16-ecp256-modp3072,aes192-sha256-ecp256-modp3072

    dpdaction=clear
    dpddelay=300s
    rekey=no
    left=%any
    leftid=@${SERVER_FQDN}
    leftcert=/etc/ipsec.d/certs/vpn-server-cert.pem
    leftsendcert=always
    leftsubnet=0.0.0.0/0
    right=%any
    rightid=%any
    rightauth=eap-mschapv2
    rightdns=${DNS_SERVER}
    rightsourceip=${VPN_SUBNET}
    # alternatively, set rightsourceip=%dhcp to get DHCP'ed IPs from wherever the server is getting IPs from.
    rightsendcert=never
    eap_identity=%identity

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

/etc/ipsec.secrets

${SERVER_FQDN} : RSA "/etc/ipsec.d/private/vpn-server-key.pem"
${YOUR_USERNAME} %any% : EAP "${YOUR_PASSWORD}"


N.B. Make sure your password is strong enough! To generate a strong password, use the following (source):
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.

iptables

# 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 \
  -s ${VPN_SUBNET} -j ACCEPT
sudo iptables -A FORWARD --match policy --pol ipsec --dir out --proto esp \
  -d ${VPN_SUBNET} -j ACCEPT

# 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, 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


/etc/sysctl.conf

# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1

# 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 as I did above, these are the only ports you need. If not, make sure you also forward protocols ESP and AH, but be aware that NAT'ing ESP and AH 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!

Troubleshooting

  • loading EAP_MSCHAPV2 method failed -- ensure you have charon and strongswan plugins installed (libcharon-extra-plugins libstrongswan-extra-plugins)
  • "Authentication failure" or immediate disconnect from iOS/macOS -- this happens when you don't explicitly install or trust the server certificate.
  • loading EAP_DYNAMIC method failed -- this happens when you have multiple EAP authentication methods in IPSec.conf but no associated strongswan.conf solution. Note this is also listed explicitly in the example configuration file (if you read this 8)):
    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.
  • unknown attribute type (25) -- this is not really an error
  • received ESP_TFC_PADDING_NOT_SUPPORTED, not using ESPv3 TFC padding -- this is not an error either

Volgende: Setting up an A+-grade nginx SSL server 27-01 Setting up an A+-grade nginx SSL server
Volgende: Measuring CO2 using MH-Z19B and D1 mini pro 20-12 Measuring CO2 using MH-Z19B and D1 mini pro

Comments


By Tweakers user Sterk1, Monday 24 December 2018 11:02

softether wel eens overwogen ?
werkt geweldig als server op de raspberry pi
en native op, windows, mac en ios

mooie grafiscne interface en zeer flexibel

By Tweakers user Qwerty-273, Monday 24 December 2018 11:02

Any specific reason to choose the United States of America as the location? Or just as it was in the guide, or is the environment / ownership based in the US?
https://www.digicert.com/ssl-certificate-country-codes.htm

By Tweakers user Atomstar, Monday 24 December 2018 12:46

Looks promising! I've added it to the list of alternatives, thanks!
Qwerty-273 wrote on Monday 24 December 2018 @ 11:02:
Any specific reason to choose the United States of America as the location?
That was the default, I've added a variable to capture this. Thanks!

By Tweakers user GekkePrutser, Tuesday 25 December 2018 16:57

Another important point: IKEv2 is also natively supported on all Samsung smartphones.

Android as such only supports IKEv1 but Samsung has added IKEv2 support to the base OS on their phones.

Another reason to have IKEv2 is that it is rarely blocked due to its use being very common in the corporate world. Unlike things like WireGuard.

Personally I also use IKEv2 with StrongSwan but I used algo to set it up, which makes the process quite easy. Unfortunately they moved to WireGuard for Android devices but IKEv2 still works as they still support it for iOS, so you can simply use it on Android as well by manually configuring it.

[Comment edited on Tuesday 25 December 2018 16:58]


By Marco, Tuesday 12 March 2019 14:29

Hi,
great instructions. I only have a problem with the forwarding.
If I use %dhcp in the ipsec.conf to get a IP from my normal dhcp I can ping the machine where the VPN Server is running on. I also can ssh to this machine.
But all other machines in the same network cannot be reached (no ping nothing). I checked the sysctl.conf (ip4 forwarding is on) and also the rules for the iptables.
Where can I search what happens or find out what is wrong?
Many thanks and Kind Regards,
Marco

By Marco, Tuesday 12 March 2019 15:48

Hi,
Marco wrote on Tuesday 12 March 2019 @ 14:29:
Hi,
great instructions. I only have a problem with the forwarding.
I was able to solve it- I had at first a typo in the ipsec.conf and then found out that the laptop uses ipv6 over LTE and therefore was not able to do anything. After I switched IPv6 off on that device it worked.
Is it also possible to add the config that IPv6 will work?

Comment form
(required)
(required, but will not be displayed)
(optional)