L2TP VPN in FreeBSD

From Wiki
Jump to: navigation, search

A slightly more concise version of this incredibly good forum post. Thanks to Oichi for improving this page and for translating it into Japanese.

This works in Mac OS X and iOS. NAT traversal is included, so this will work well even if the client or server is behind a NAT router or firewall. L2TP is significantly more secure than PPTP, and only a few UDP ports are required to be open, so this poses no difficulty with even the crappiest broadband routers.

Start with a FreeBSD RELEASE 9 or 9.1 install including full sources and the ports collection. The excellent FreeBSD handbook will guide you through this process.

There are a number of things we need which are not in the GENERIC kernel, so you will need to recompile the kernel, but this is incredibly simple in FreeBSD compared with Linux:

# mkdir /root/kernels
# cp /usr/src/sys/`uname -m`/conf/GENERIC /root/kernels/STOCKSY
# sed -i -e 's/GENERIC/STOCKSY/g' /root/kernels/STOCKSY
# cat <<EOF >>/root/kernels/STOCKSY
options         IPSEC
options         IPSEC_NAT_T
device          crypto
options         IPSEC_FILTERTUNNEL
device          enc
options         IPFIREWALL
options         IPFIREWALL_VERBOSE
options         IPFIREWALL_VERBOSE_LIMIT=5
options         IPFIREWALL_FORWARD
options         IPFIREWALL_NAT
options         LIBALIAS
options         IPDIVERT
EOF
# ln -s /root/kernels/STOCKSY /usr/src/sys/`uname -m`/conf/STOCKSY
# cd /usr/src
# make buildkernel KERNCONF=STOCKSY && make installkernel KERNCONF=STOCKSY

Reboot to install the new kernel.

After this kernel configurations, FreeBSD does not accept all connections from any hosts. So you will need to change /etc/rc.conf. We added descriptions to /etc/rc.conf below:

# firewall
firewall_enable="YES"
firewall_script="/etc/rc.firewall"
firewall_type="OPEN"
firewall_quiet="NO"
firewall_logiging="YES"

Please refer this page.

The VPN consists of two parts, racoon handles the encryption, whilst mpd5 handles the authentication. This is completely transparent to the user.

racoon is part of the IPSec tools port. It is necessary to to apply a patch to racoon to make it useful as a remote access VPN. Without this patch, it is impossible to connect from unknown IP addresses. Whilst it is less secure to share a pre-shared key between different clients from different IP addresses, the only danger is that different clients may be able to decrypt each other's data – this shouldn't be a concern for most people.

So, save this as /usr/ports/security/ipsec-tools/files/patch-zz-local-1.diff

diff -rup srca/racoon/localconf.c srcb/racoon/localconf.c
--- src/racoon/localconf.c 2012-01-29 21:17:41.000000000 +0000
+++ src/racoon/localconf.c 2012-01-29 21:19:09.000000000 +0000
@@ -207,7 +207,8 @@ getpsk(str, len)
 		if (*p == '\0')
 			continue;	/* no 2nd parameter */
 		p--;
-		if (strncmp(buf, str, len) == 0 && buf[len] == '\0') {
+		if (strcmp(buf, "*") == 0 ||
+			(strncmp(buf, str, len) == 0 && buf[len] == '\0')) {
 			p++;
 			keylen = 0;
 			for (q = p; *q != '\0' && *q != '\n'; q++)

Now build the ipsec-tools port:

# cd /usr/ports/security/ipsec-tools
# make install clean

Next, mpd5, no patching needed here:

# cd /usr/ports/net/mpd5
# make install clean

Press on with the configuration of racoon:

# mkdir /usr/local/etc/racoon
# vi /usr/local/etc/racoon/racoon.conf

path pre_shared_key "/usr/local/etc/racoon/psk.txt";

listen
{
    # REPLACE w.x.y.z with the IP address racoon will listen on (if NAT translated, this is the INSIDE IP)
        isakmp           w.x.y.z [500];
        isakmp_natt      w.x.y.z [4500];
    # NOTE, you can specify multiple IPs to listen on
        isakmp           p.q.r.s [500];
        isakmp_natt      p.q.r.s [4500];
        strict_address;
}

remote anonymous
{
        exchange_mode    main;
        passive          on;
        proposal_check   obey;
        support_proxy    on;
        nat_traversal    on;
        ike_frag         on;
        dpd_delay        20;

        proposal
        {
                encryption_algorithm  aes;
                hash_algorithm        sha1;
                authentication_method pre_shared_key;
                dh_group              modp1024;
        }

        proposal
        {
                encryption_algorithm  3des;
                hash_algorithm        sha1;
                authentication_method pre_shared_key;
                dh_group              modp1024;
        }
}

sainfo anonymous
{
        encryption_algorithm     aes,3des;
        authentication_algorithm hmac_sha1;
        compression_algorithm    deflate;
        pfs_group                modp1024;
}

Create a nice strong pre-shared key (the * indicates that the passphrase should be accepted from any source IP address - that's why we patched racoon):

# vi /usr/local/etc/racoon/psk.txt

* thisismylongpassphrasedoyouliketurtles

Then it is better to change permission of "psk.txt" for security.

# chmod 400 /usr/local/etc/racoon/psk.txt

Create the security policies:

# vi /usr/local/etc/racoon/setkey.conf

flush;
spdflush;
spdadd 0.0.0.0/0[0] 0.0.0.0/0[1701] udp -P in  ipsec esp/transport//require;
spdadd 0.0.0.0/0[1701] 0.0.0.0/0[0] udp -P out ipsec esp/transport//require;


Now the mpd5 configuration:

# vi /usr/local/etc/mpd5/mpd.conf

startup:
        # configure mpd users
        set user super pwSuper admin
        # configure the console
        set console self 127.0.0.1 5005
        set console open
        # configure the web server
        set web self 0.0.0.0 5006
        set web open

default:
        load l2tp_server

l2tp_server:
# Define dynamic IP address pool - these are the IP addresses which will be
# allocated to our remote clients when they join the LAN
# REPLACE w.x.y.from - w.x.y.to with the IP addresses mpd5 will allocate IP address range.
# e.g.  set ippool add pool_l2tp w.x.y.150 w.x.y.199
        set ippool add pool_l2tp w.x.y.from w.x.y.to

# Create clonable bundle template named B_l2tp
        create bundle template B_l2tp
        set iface enable proxy-arp
        set iface enable tcpmssfix
        set ipcp yes vjcomp
# Specify IP address pool for dynamic assigment.
       # This is the internal IP and netmask of the box
       # REPLACE w.x.y.z with the IP address for your VPN server
        set ipcp ranges w.x.y.z/24 ippool pool_l2tp
       # an accessible DNS server for clients to use
       # REPLACE w.x.y.dns with the IP address for your DNS server
       # e.g. set ipcp dns w.x.y.50
        set ipcp dns w.x.y.dns

# Create clonable link template named L_l2tp
        create link template L_l2tp l2tp
# Set bundle template to use
        set link action bundle B_l2tp
# Multilink adds some overhead, but gives full 1500 MTU.
        set link enable multilink
        set link no pap chap eap
        set link enable chap
        set link keep-alive 0 0
# We reducing link mtu to avoid ESP packet fragmentation.
        set link mtu 1280
# Configure L2TP
       # REPLACE with the IP address racoon will listen on (if behind NAT, this is the INSIDE IP)
       # Unfortunately, you can not specify multiple IPs here, so just comment the next line if you need that
        set l2tp self w.x.y.z
        set l2tp enable length
# Allow to accept calls
        set link enable incoming

Add accounts for VPN connection.

# vi /usr/local/etc/mpd5/mpd.secret

username password
# chmod 400 /usr/local/etc/mpd5/mpd.secret

If you don't already have it enabled, check that IP forwarding is enabled in /etc/sysctl.conf:

# vi /etc/sysctl.conf

net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1

Make sure everything that is required is enabled in /etc/rc.conf

# vi /etc/rc.conf

ipsec_enable="YES"
ipsec_program="/usr/local/sbin/setkey"
ipsec_file="/usr/local/etc/racoon/setkey.conf"
racoon_enable="YES"
racoon_flags="-l /var/log/racoon.log"
mpd_enable="YES"

Make sure that UDP 1701, 500 and 4500 are allowed as well as the ESP protocol. You'll also need to permit traffic in and out of the ngX interfaces which get created by mpd. For pf, this would be roughly:

# vi /etc/pf.conf

pass in on $ext_if inet proto udp from any to (self) port { 1701, 500, 4500 }
pass in on $ext_if inet proto esp

# There is a better way to do this with ifconfig groups - you're welcome to try getting
# mpd5 to do that!
pass quick on ng0 all
pass quick on ng1 all
pass quick on ng2 all
pass quick on ng3 all
pass quick on ng4 all
pass quick on ng5 all
# . . . . 

Reboot and everything should be fine. If you need to troubleshoot, it's easiest to stop the daemons and run them in the foreground with debugging:

# service racoon stop
# racoon -ddF

Similarly for mpd5:

# service mpd5 stop
/usr/local/sbin/mpd5 -p /var/run/mpd5.pid