L2TP VPN in FreeBSD
From Wiki
A slightly more concise version of this incredibly good forum post.
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 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 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.
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.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
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
set ippool add pool_l2tp 172.16.0.150 172.16.0.199
# 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
set ipcp ranges 172.16.0.254/24 ippool pool_l2tp
# an accessible DNS server for clients to use
set ipcp dns 172.16.0.50
# 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
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 <pre> Similarly for <code>mpd5</code>: <pre> # service mpd5 stop /usr/local/sbin/mpd5 -p /var/run/mpd5.pid
