IKEv2 VPN server with strongSwan and Let’s Encrypt

VPN helps to secure your Internet connection. There are many cases when you want your network traffic to be encrypted to prevent stealing your sensitive data, e.g., public Wi-FI networks. Numerous of VPN protocols exist. Most popular are PPTP, L2TP/IPsec, OpenVPN and IKEv2. In this guide I will explain setting up IKEv2 VPN server with strongSwan and Let’s Encrypt certificate with automatic renewal configuration.

IKEv2 stands for Internet Key Exchange protocol version 2. The protocol works natively on macOS, iOS, Windows. Several IKEv2 implementations exist for Android, Blackberry and Linux. The key strength of this protocol is resistance to network change, so VPN connection will remain established after you change the network, e.g., from cellular to Wi-FI.

Prerequisites

For this tutorial you need VPS with Linux (DigitalOcean provides machines starting at $5/month) and domain. This guide covers the following software versions:

  • Ubuntu 16.04 LTS
  • strongSwan 5.3.5
  • Certbot 0.26.1

Installing strongSwan

strongSwan is an open source IPsec implementation with full support of IKEv2 protocol. Let’s install it:

sudo apt-get update
sudo apt-get install strongswan strongswan-plugin-eap-mschapv2

Installing Certbot and obtaining Let’s Encrypt certificate

You can generate your own certificate if you don’t have a domain. The only drawback is that you will need to install your root certificate on any client, which will use your VPN server. This case is not covered in this guide.

If you have a domain, you can buy or use a free certificate provided by Let’s Encrypt certificate authority (CA). Let’s Encrypt issues a certificate which is valid for 90 days. After the certificate expires, you will have to renew it. Fortunately the process of certificate obtaining and renewal can be automated with Certbot utility.

Certbot packages in Ubuntu are old, so we will add PPA and install newer verision of utility:

sudo add-apt-repository ppa:certbot/certbot 
sudo apt-get update 
sudo apt-get install certbot

Next we will obtain the certificate. Replace yourdomain with your domain name:

sudo certbot certonly --standalone -d yourdomain

Your certificate and private key will be stored in /etc/letsencrypt/live/yourdomain.

Certbot will handle automatic certificate renewal process for you. Let’s create symbolic links to the files so you will not have to manually copy them to make available to strongSwan after every renewal:

sudo ln -s /etc/letsencrypt/live/yourdomain/chain.pem /etc/ipsec.d/cacerts/ca.pem
sudo ln -s /etc/letsencrypt/live/yourdomain/cert.pem /etc/ipsec.d/certs/certificate.pem
sudo ln -s /etc/letsencrypt/live/yourdomain/privkey.pem /etc/ipsec.d/private/key.pem

To restart strongSwan after successful certificate renewal edit file /lib/systemd/system/certbot.service and change this line to:

ExecStart=/usr/bin/certbot renew --deploy-hook "systemctl restart strongswan.service"

Reload systemctl daemon for the changes to take effect:

sudo systemctl daemon-reload

Configuring strongSwan

Next thing we need to do is to edit /etc/ipsec.conf:

config setup
  charondebug="ike 1, knl 1, cfg 1"
conn ikev2-vpn
  auto=add
  compress=no
  type=tunnel
  keyexchange=ikev2
  fragmentation=yes
  forceencaps=yes
  ike=aes256-sha1-modp1024
  esp=aes256-sha1
  dpdaction=clear
  dpddelay=30s
  rekey=no
  left=%any
  leftid=@yourdomain
  leftcert=certificate.pem
  leftsendcert=always
  leftsubnet=0.0.0.0/0
  right=%any
  rightid=%any
  rightauth=eap-mschapv2
  rightsourceip=10.10.10.0/24
  rightdns=8.8.8.8,8.8.4.4
  rightsendcert=never
  eap_identity=%identity

For the configuration parameters explanation refer to General Connection Parameters.

Configuring authentication

Now we have to add users to be able to connect to our VPN server. Edit /etc/ipsec.secrets file and replace username and password with client user name and password:

yourdomain : RSA key.pem
username %any% : EAP "password"

You can add more users by inserting additional lines. In order for changes to take effect you don’t have to reload the daemon. Just run:

sudo ipsec rereadsecrets

Disabling AppArmor profile

AppArmor strongSwan profiles cause problems with permissions. As a result, when daemon tries to read certificate or private key you will get Permission denied error. You can check if AppArmor is running:

sudo apparmor_status

If you see profiles /etc/apparmor.d/usr.lib.ipsec.charon or /etc/apparmor.d/usr.lib.ipsec.stroke, you should remove them:

sudo ln -s /etc/apparmor.d/usr.lib.ipsec.charon /etc/apparmor.d/disable/
sudo ln -s /etc/apparmor.d/usr.lib.ipsec.stroke /etc/apparmor.d/disable/
sudo apparmor_parser -R /etc/apparmor.d/usr.lib.ipsec.charon
sudo apparmor_parser -R /etc/apparmor.d/usr.lib.ipsec.stroke

Restarting and checking strongSwan status

After we successfully configured strongSwan, we can restart the service and check if it’s up and running:

sudo ipsec statusall
Status of IKE charon daemon (strongSwan 5.3.5, Linux 4.4.0-128-generic, x86_64):
  uptime: 31 minutes, since Oct 12 14:09:04 2018
  malloc: sbrk 1761280, mmap 0, used 568912, free 1192368
  worker threads: 11 of 16 idle, 5/0/0/0 working, job queue: 0/0/0/0, scheduled: 0
  loaded plugins: charon test-vectors aes rc2 sha1 sha2 md4 md5 random nonce x509 revocation constraints pubkey pkcs1 pkcs7 pkcs8 pkcs12 pgp dnskey sshkey pem openssl fips-prf gmp agent xcbc hmac gcm attr kernel-netlink resolve socket-default connmark farp stroke updown eap-identity eap-sim eap-sim-pcsc eap-aka eap-aka-3gpp2 eap-simaka-pseudonym eap-simaka-reauth eap-md5 eap-gtc eap-mschapv2 eap-dynamic eap-radius eap-tls eap-ttls eap-peap eap-tnc xauth-generic xauth-eap xauth-pam xauth-noauth tnc-tnccs tnccs-20 tnccs-11 tnccs-dynamic dhcp lookip error-notify certexpire led addrblock unity
Virtual IP pools (size/online/offline):
  10.10.10.0/24: 254/0/2
Listening IP addresses:
  youripaddress
Connections:
   ikev2-vpn:  %any...%any  IKEv2, dpddelay=30s
   ikev2-vpn:   local:  [yourdomain] uses public key authentication
   ikev2-vpn:    cert:  "CN=yourdomain"
   ikev2-vpn:   remote: uses EAP_MSCHAPV2 authentication with EAP identity '%any'
   ikev2-vpn:   child:  0.0.0.0/0 === dynamic TUNNEL, dpdaction=clear
Security Associations (0 up, 0 connecting):
  none
sudo ipsec listcerts
List of X.509 End Entity Certificates:

  altNames:  yourdomain
  subject:  "CN=yourdomain"
  issuer:   "C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3"
  serial:    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
  validity:  not before Sep 19 23:58:54 2018, ok
             not after  Dec 18 23:58:54 2018, ok
  pubkey:    RSA 2048 bits, has private key
  keyid:     00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
  subjkey:   00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
  authkey:   00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00

If something went wrong you can check the logs with:

sudo journalctl -u strongswan.service

Configuring iptables

Next thing we need to do is to configure iptables properly to close all ports which we don’t need and to set up masquerading to redirect all client traffic through VPN server.

First we clear all iptables rules:

sudo iptables -P INPUT ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -F
sudo iptables -Z

We need to open ports TCP 22 (SSH), TCP 80, 443 (Certbot), UDP 500, 4500 (IPsec):

sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -p tcp --dport  80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
sudo iptables -A INPUT -p udp --dport  500 -j ACCEPT
sudo iptables -A INPUT -p udp --dport 4500 -j ACCEPT

Enable Encapsulating Security Payload (ESP) forwarding and traffic masquerading:

sudo iptables -A FORWARD --match policy --pol ipsec --dir in  --proto esp -s 10.10.10.10/24 -j ACCEPT
sudo iptables -A FORWARD --match policy --pol ipsec --dir out --proto esp -d 10.10.10.10/24 -j ACCEPT
sudo iptables -t nat -A POSTROUTING -s 10.10.10.10/24 -o eth0 -m policy --pol ipsec --dir out -j ACCEPT
sudo iptables -t nat -A POSTROUTING -s 10.10.10.10/24 -o eth0 -j MASQUERADE

Also we should adjust packet maximum segment size to prevent problems with some VPN clients:

sudo iptables -t mangle -A FORWARD --match policy --pol ipsec --dir in -s 10.10.10.10/24 -o eth0 -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360

Finally we will drop other packets:

sudo iptables -A INPUT -j DROP
sudo iptables -A FORWARD -j DROP

These iptables rules will be lost after restart. So in order to make them persistent:

sudo apt-get install iptables-persistent
sudo netfilter-persistent save
sudo netfilter-persistent reload

Configuring IPv4 forwarding

Add or change the following parameters in /etc/sysctl.conf to enable IPv4 forwaring, disable ICMP redirects sending/receiving and disable Path MTU discovery to prevent the man-in-the-middle attacks:

net.ipv4.ip_forward=1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.ip_no_pmtu_disc = 1

Finally reboot the system:

sudo reboot

Configuring VPN client connection

macOS 10.12 and iOS 11

Setting connection in macOS and iOS is simple using my Python script generate-mobileconfig.py. To generate Apple Configuration file, execute the script with the following arguments:

./generate-mobileconfig.py username userpassword yourdomain

Windows 8.1

Setting connection in Windows 8.1 is pretty straightforward. Just set up a new VPN connection, then enter your hostname, user name and password.

Android

Download strongSwan VPN Client from Google Play.