L2TP/IPSec VPN server setup

27 December 2016
Like just about all my technically-minded friends, I have decided that from now on all my internet access from the UK will be via offshore proxies, as a direct response to the UK's new snooping law requiring ISPs to record all internet traffic. For me this was the last straw in a long line of so-called “anti-terrorist” legislation, and I have decided to by-pass all UK internet controls as a matter of principle, by setting up my own VPN servers on non-UK VPS systems. This how-to details setting up L2TP/IPSec with both PSK (pre-shared key) and RSA keys on Ubuntu 16.04 - I choose Ubuntu because most VPS providers I have come across offer a choice between some RedHat-based (usually CentOS) and Debian-based (usually Ubuntu) images, and of that selection Ubuntu seems the best bet.

Pre-shared key VPN setup

IPSec security in this setup is provided by strongSwan, which in Ubuntu 16.04 is the replacement for Openswan. It isn't quite a drop-in replacement, although the main changes seems to be deprecation of many options that I think are now auto-detected or are always on, and confusingly the strongSwan website seems to assume it is used standalone rather than as the encryption layer for an external L2TP daemon. Once strongSwan provides the IPSec secure connection, xl2tpd establishes an L2TP tunnel over it, which in turn uses PPPd to provide the Level-2 network interfaces. Below I have assumed a clean install, so have given the content of just the files that need to be changed. Most of this content is straight out of the Gentoo IPsec/L2TP VPN server wiki page (sections 2.3.1, 3.1, & 4.1.2) with minor changes, and has only been included here verbatim to avoid link-rot.


config setup # strictcrlpolicy=yes # uniqueids = no conn vpnserver type=transport authby=secret pfs=no rekey=no keyingtries=1 left=%any leftprotoport=udp/l2tp right=%any rightprotoport=udp/%any auto=add


xxx.xxx.xxx.xxx %any : PSK "sekret"

Replace xxx.xxx.xxx.xxx with the IP address of your VPN's network interface, and sekret with your pre-shared key. It must have quotes around it.


[global] ipsec saref=no [lns default] ip range = local ip = require chap = yes refuse pap = yes require authentication = yes ppp debug = yes pppoptfile = /etc/ppp/options.xl2tpd length bit = yes

Here the subnet is used as a VPN address pool, with the VPS server itself having and connecting VPN clients using the upper part of the 8-bit subnet. I don't think the config file format supports comments (or at least not ones starting with a ‘#’)..


ms-dns ms-dns noccp auth crtscts mtu 1410 mru 1410 nodefaultroute lock noproxyarp silent name MyVPN

From experimentation the choice of PPPd options doesn't seem particularly important, although the ones that should be of interest are listed below. Failed VPN connection attempts are virtually always IPSec errors rather than L2TP tunnel setup going wrong.

Needed if usepeerdns is to work on connecting clients.
Disables Compression Control Protocol. Enabling PPP compression isn't going to gain much.
mtu & mru
A reduced MTU & MRU takes account of tunelling overheads.
This is for the VPS, not the remote client. Set to no because the VPS's existing default route using the externa; interface should be used.
ARP Proxy is for exposing remote systems to the local network. Since the VPS is simply a NAT gateway and not a whole subnet, there is no point enabling this.
name MyVPN
Name of connection. Not actually required..


remy * sekret *

Replace values as appropriate. The space-seperated columns are as follows:

  1. Client name. Will be the username for VPN clients.
  2. Server name. If not an asterisk (matches anything) then it need to match the name directive in options.xl2tpd above.
  3. Plaintext password. Doesn't add much to overall security.
  4. IP addresses where clients (users) can connect from. Asterisk means anywhere.

Certificate-based setup

Switching over from pre-shared key to certificate-based authentication doesn't involve much work, but once again there are plenty of places for things to go wrong. Details of strongSwan configuration are mostly from the Gentoo IPSec/L2TP wiki (section 2.3.2), and details of key & certificate generation are from the strongSwan Simple CA page.

First thing to do is generate the keys & certificates and copy them to the expected places:

ipsec pki --gen > caKey ipsec pki --self --in caKey \ --dn "C=UK, OU=VPN, O=MyNET, CN=MyNET VPN CA" \ --ca > caCert ipsec pki --gen > clientKey ipsec pki --pub --in clientKey > clientKey.pub ipsec pki --issue --cacert caCert --cakey caKey \ --dn "C=UK, OU=VPN, O=MyNET, CN=S4" \ --in clientKey.pub > clientCert ipsec pki --gen > serverKey ipsec pki --pub --in serverKey > serverKey.pub ipsec pki --issue --cacert caCert --cakey caKey \ --dn "C=UK, OU=VPN, O=MyNET, CN=VPN" \ --in serverKey.pub > serverCert ipsec pki --print --in clientKey ipsec pki --print --in serverKey

cp caCert /etc/ipsec.d/cacerts/ cp serverKey /etc/ipsec.d/private/server.key cp serverCert /etc/ipsec.d/certs/server.crt

The easiest way of getting the required keys & certificates onto an Android device is to create a PKCS12 archive that contains both the CA certificate and the user certificate & key in PEM format:

openssl x509 -inform der -outform pem -in caCert -out caCert.pem openssl x509 -inform der -outform pem -in clientCert -out clientCert.pem openssl rsa -inform der -outform pem -in clientKey -out clientKey.pem openssl pkcs12 -in clientCert.pem -inkey clientKey.pem -certfile caCert.pem \ -export -out vpnclient.p12

You will be asked for a password to secure the archive. Android can load the resulting vpnclient.p12 archive from either local storage or from your Google Drive. Note that on Samsung mobiles loading such user-generated certificate into the credentials store will require you to have a “high security” screen lock, which does not include the pattern-drawing lockscreen.

Finally some strongSwan configuration files need to be changed. The differences compared to a pre-shared key setup are highlighted in bold:


config setup # strictcrlpolicy=yes # uniqueids = no conn vpnserver type=transport authby=rsasig pfs=no rekey=no keyingtries=1 left=%any leftprotoport=udp/l2tp leftcert=server.crt leftid="C=UK, OU=VPN, O=MyNET, CN=VPN" right=%any rightprotoport=udp/%any rightrsasigkey=%cert auto=add


: RSA server.key

Routing setup

At this point starting up the IPSec/L2TP servers and connecting will result in an interface named ppp0 appearing on your VPS server, so all that is left to do is to get the VPS to act as a NAT gateway (IP Masquerade) for traffic coming in on that interface. Much of this is from Raymii's IPSEC L2TP VPN guide, which like most guides was a great help but was not exactly a one-stop-shop for my purposes.

echo 1 > /proc/sys/net/ipv4/ip_forward for prefix in /proc/sys/net/ipv4/conf/*; do echo 0 > ${prefix}/accept_redirects; echo 0 > ${prefix}/send_redirects; done iptables -F INPUT iptables -A INPUT -p udp --dport 1701 -m policy --dir in --pol ipsec -j ACCEPT iptables -A INPUT -p udp --dport 1701 -j REJECT iptables -F FORWARD iptables -P FORWARD DROP iptables -A FORWARD -i ppp+ -o eth0 -j ACCEPT iptables -A FORWARD -i eth0 -o ppp+ -j ACCEPT iptables -t nat -A POSTROUTING -s -o ens3 -j MASQUERADE

The INPUT chain rules are to close a security hole. Replace eth0 with your VPS's main network interface if it is different.


Android-related gotchas

My VPN connections are getting deleted
Having VPN connections setup technically requires a “high security” screen lock, but this does not seem to be enforced when adding connections. Guessing they are simply not getting saved across reboots as a result.
DNS issues using the VPN
Under Show advanced options, set DNS servers to - these are Google's public DNS servers.
Not all traffic being routed over VPN
Under Show advanced options, set Forwarding routes to

xl2tpd connects unencrypted if IPSec fails

I consider this a massive security hole because it both bypasses any protection IPSec provides to your server and all your traffic can easily be snooped. Solution is to configure firewalls to deny unencrypted traffic to L2TP.

Getting L2TP No Authorization errors

For no obvious reason connections are being denied by xl2tpd without any apparent checking of CHAP credentials, and setting access control and/or require authentication to no in xl2tpd.conf does not alleviate the problem. Log messages are also somewhat terse:

xl2tpd[xxxxx]: control_finish: Denied connection to unauthorized peer xxx.xxx.xxx.xxx xl2tpd[xxxxx]: Connection 40447 closed to xxx.xxx.xxx.xxx, port 1701 (No Authorization)

This was caused by changing [lns default] in xl2tpd.conf to use a name other than default. Easiest thing is to change it back.