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:!192.168.1.0/24. 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 authby=secret pfs=no rekey=no keyingtries=3 dpddelay=30 dpdtimeout=60 dpdaction=clear compress=yes left=%defaultroute leftprotoport=udp/1701 right=%any rightprotoport=udp/0 auto=add
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:
126.96.36.199 %any: PSK "put-your-preshared-key-here"
Replace 188.8.131.52 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.
[global] access control = no rand source = dev [lns default] ip range = 192.168.1.120-192.168.1.127 local ip = 192.168.1.1 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.
ipcp-accept-local ipcp-accept-remote ms-dns 192.168.1.1 noccp name vpn auth crtscts idle 1800 mtu 1410 mru 1410 nodefaultroute debug lock proxyarp connect-delay 5000 plugin pppol2tp.so require-mschap-v2
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" 192.168.1.1/24 options-name username "password" 192.168.1.1/24
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.