Your Own Free L2TP/IPsec VPN

If you have a VPS for web applications, it’s relatively easy to set up your own L2TP/IPsec VPN for use by Mac OS X or iOS clients. When you’re away from your home or office on someone else’s Wi-Fi (at coffee shops or a conference), it’s a good idea to use a VPN to keep your network use secure and private. While there are free VPN services (Cloak is one), the free plan is time and bandwidth limited. You can pay to lift the time limit, but why pay for another service if you can piggyback on another you already have?

(And if you don’t already have a VPS, you’ll get a much higher bandwidth cap by getting the cheapest one from Linode than by paying a dedicated VPN provider. On the other hand, what I explain here is bare-bones. Cloak, for example, offers some very nice features this solution doesn’t, such as auto-connection.)

I said it was relatively easy, but the truth is that it took me several hours to get everything configured properly. There are a few guides out there, but none that succinctly explained everything correctly. This is my attempt to rectify that problem.

This guide also assumes your server is running a Linux distribution and the clients are Mac OS X or iOS. I don’t have Windows clients, so I can’t say if this will work for them. I use Debian Linux; adjust package installation and configuration file paths if you use something else. My configuration is typical for “road warriors,” meaning clients can connect from any IP address and may be behind a NAT.

Install Openswan (IPsec userspace daemon) and xl2tpd (L2TP daemon):

$ sudo aptitude install openswan xl2tpd

Edit /etc/ipsec.conf. In the config setup section, add an exclusion for your internal network to the virtual_private line. Mine looks like %v4:! Unless you’re on an old kernel, change protostack from auto to netkey to squelch a warning. Then add a connection section. Here is mine:

conn L2TP-PSK

This says the IPsec connection will authenticate with a pre-shared key (instead of client certificates). DPD is Dead Peer Detection and is needed because iOS clients don’t tell the server when they disconnect. My settings send DPD keepalives every 30 seconds, and clear connections that don’t respond after 60 seconds.

Edit /etc/ipsec.secrets and add a line similar to this: %any: PSK "put-your-preshared-key-here"

Replace with your VPN server’s public IP address and put your chosen pre-shared key in quotes. This is the secret shared between the server and your clients.

Next, edit /etc/xl2tpd.conf. I added the following to mine.

access control = no
rand source = dev
[lns default]
ip range =
local ip =
require chap = yes
refuse pap = no
require authentication = yes
name = LinuxVPNserver
ppp debug = yes
pppoptfile = /etc/ppp/options.l2tp
length bit = yes

ip range is the range of IP addresses from your internal network that you want clients to get an address from. local ip is the internal IP address of your server.

Edit /etc/ppp/options.l2tp:

name vpn
idle 1800
mtu 1410
mru 1410
connect-delay 5000

ms-dns is the address of a DNS server on your internal network. name is used in the next file.

Finally, add entries to /etc/ppp/chap-secrets to set up individual VPN users:

username	options-name	"password"
options-name	username	"password"

username is the client’s user name and options-name must match the name parameter from the previous file, options.l2tp. The last parameter is the subnet and mask to match this client. It should be the range of your internal network.

On my server, Openswan didn’t create the links in /etc/rc?.d required to start itself when the server boots. insserv addresses that:

$ sudo insserv ipsec
$ sudo invoke-rc.d ipsec start

Finally, you need to allow incoming connections if your server uses a firewall. iptables rules look like this:

iptables -A INPUT -m state --state NEW -i eth0 -p udp --dport 500 -j ACCEPT
iptables -A INPUT -m state --state NEW -i eth0 -p udp --dport 4500 -j ACCEPT
iptables -A firewall -p esp -j ACCEPT
iptables -A firewall -m policy --dir in --pol ipsec -p udp --dport 1701 -j ACCEPT
iptables -A firewall -i ppp+ -p all -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

This allows UDP traffic on ports 500 (IPsec) and 4500 (NAT-Traversal) and all ESP (IPsec Encapsulating Security Payload) traffic. Additionally, it allows incoming UDP traffic to port 1701 (L2TP) if it’s wrapped in IPsec. Finally, PPP interfaces are created to pass traffic and we need a rule to permit that.

You can now create L2TP VPN configurations on your Mac and iOS devices, using the values you chose along the way, and you should have a working VPN.

If I have something wrong (it’s entirely possible I left something out), get in touch and I’ll fix the post.