Just wrote this up for packetpushers.net , figured I’d paste it down here too so I can say I’ve posted something this year.
Being the networking nerd I am, I have a pretty big network at home. And as the denizens of the Packet Pushers IRC channel know, I do a lot of work with VPNs. One of my use cases is sharing the resources on my home network. My friends, family, and coworkers sometimes like to use my network for any number of reasons. As such, my internet router performs a decent amount of VPN duty. Historically when someone wants to connect their network to mine, and they don’t have the knowledge or resources to handle their end of the connection, I’d dig out an old netbook or something to use as a termination point. Preconfigure a few things on it, ship it out to them, make a couple changes on their “Best Buy Grade” router, and be done with it. But this isn’t a great solution. Wasting a netbook/laptop for the sake of bringing up a LAN-to-LAN tunnel is a bit silly.
Recently I got a Raspberry Pi to play with. I figured for 35 bucks I couldn’t go wrong. I think I’ve bought cappuccinos more expensive. My idea was that if I could get it to bring up a VPN and pass packets at a decent speed, it’d be a great solution for a super cheap super easy remote VPN endpoint. Turns out it works pretty good in this role, quite a bit more flexible than I’d planned on:
- Dynamic WAN IP of the network it’s living on
- Dynamic LAN IP of the unit itself
- Automatic establishing of VPN to head-end
- Unique IP to ping/ssh to, regardless of DHCP address
- No need to port-forward anything to the device
- No need to change routing to get return traffic back to your network
First, we need to enable packet forwarding on the Pi so we can actually pass traffic through it:
sudo sysctl net.ipv4.ip_forward=1
and to make the above persistent through reboot, add “net.ipv4.ip_forward=1” to /etc/sysctl.conf .
Install the a few packages. Some error messages may come up during the install but they can probably be safely ignored. I don’t recall if ssh is on the raspberry pi by default, so I’m tossing it down there just in case. Openswan checks for support on a lot of different options whether or not you’re going to use them. The other packages support openswan.
sudo apt-get install openssh-server openswan uml-utilities chkconfig
I noticed that by default “PermitRootLogin” was set to “Yes” in /etc/ssh/sshd_config . If you plan on port-forwarding TCP/22 to the device, you should probably edit this and set it to “No”
Next, add “tun” to the end of the /etc/modules file, so that after reboot we can create a
tun0 interface. Edit /etc/network/interfaces and add the following chunk after the section for
auto tun0 iface tun0 inet static pre-up /usr/sbin/tunctl -t tun0 address 172.31.100.1 netmask 255.255.255.255 up ifconfig tun0 up
In my case I’m using 172.31.100.1 as my unique “loopback” to hit from my head-end network. This gives me a pre-determined IP to hit, regardless of what the local address ends up being. To get traffic passing to/from this properly, we have to add a static route. I do this with an “@reboot” cronjob. I’m sure there’s a more graceful way to do it, but you want something like the following to be run a few seconds after boot when the tunnel interface has been brought up:
route add -net 10.213.100.0/24 gw 172.31.100.1
Time to get to the IPSec config! I’m using PSK auth for simplicity of this scenario. Drop the key at the end of the /etc/ipsec.secrets file. In this scenario the head end vpn endpoint is vpn1.iggdawg.com and the local IP isn’t important. “%any” will let you have a dynamic local address.
%any vpn1.iggdawg.com: PSK "DERPDERPDERPDERPDERP"
It’s strongly advised to use a big pre-shared-key here. I reccommend doing something like “man sendmail | sha512sum” and using the hash as a PSK. Obviously, pipe a different manpage than I did here.
Configure a profile in /etc/ipsec.conf to handle traffic from whatever your local address is to your local network to the network you’re interested in on the head end, 10.213.1.0/24 in this case. Set the ID on the far end to be the same thing as the peer hostname. :
version 2.0 config setup interfaces=%defaultroute protostack=netkey nat_traversal=yes keep_alive=30 conn tunnelipsec-10.213.1.0.24 type= tunnel authby= secret left=%defaultreoute # auto-configured as local interface IP #leftsubnet=192.168.1.0/24 # local network, commented out initially right=vpn1.iggdawg.com # remote peer hostname or IP address rightsubnet=10.213.1.0/24 # network behind the head end rightid="vpn1.iggdawg.com" # this makes setting the PSK much easier ike=aes128-sha1;modp1536 # This is the phase 1 policy. phase2alg=aes128-sha1 # This is the phase 2 policy. keyexchange= ike pfs= yes auto= start conn tunnelipsec-10.213.100.1.24 type= tunnel authby= secret left=%defaultroute leftsubnet=172.31.100.1/32 right=vpn1.iggdawg.com rightsubnet=10.213.100.0/24 rightid="vpn1.iggdawg.com" ike=aes128-sha1;modp1536 phase2alg=aes128-sha1 keyexchange= ike pfs= yes auto= start
Next is getting traffic in and out of the tunnel on the remote side.
sudo service ipsec start
This is all you need to get the remote device going. with these settings, specifically with “start= auto” configured, the device will start trying to connect right away.
Return traffic depends a bit on the other end. If you can get away with putting a static route on the Pi’s default gateway saying “everything destined to the network on the other end of the VPN, send traffic to the Raspberry Pi here”. But if that’s not an option, or you want to just play some more, we can use iptables (which is in the default Raspbian install) to source NAT the traffic to the ethernet interface’s IP. This makes some windows machines behave more nicely since it looks like all VPN traffic is LAN traffic. There are 2 options to perform the source NAT (replace <eth0 addr> with the address of eth0):
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source <eth0 addr>
Both options perform basically the same function, but from what I gather the second option is easier on CPU.
So let’s get to our router config. The setup below is written to be a very simple way of handling remote peers with dynamic addresses. This is how I have my head end router configured for the earlier example config. call me out if I forgot something. I have so much crypto config on this device it’s sometimes hard to pick out all the pieces for one particular connection
crypto isakmp policy 5 encr aes 128 hash sha authentication pre-share group 5 ! ! "address 0.0.0.0 0.0.0.0" means "anyone can auth via this key" crypto keyring spokes pre-shared-key address 0.0.0.0 0.0.0.0 key 6 DERPDERPDERPDERPDERP ! ! similarly, 0.0.0.0 here matches all peers crypto isakmp profile sites keyring spokes match identity address 0.0.0.0 ! crypto ipsec transform-set ESP-AES-128-SHA esp-aes esp-sha-hmac ! crypto dynamic-map vpnmap-dynamic 5 set transform-set ESP-AES-128-SHA set pfs group5 set isakmp-profile sites ! crypto map vpnmap 1000 ipsec-isakmp dynamic vpnmap-dynamic ! interface GigabitEthernet0/0 description Outside interface crypto map vpnmap
Obviously there are some security implications here. Since the profile will match connections from any device, and the defined key matches any device, if someone out there has your PSK they can hook up pretty easily. So make sure you have proper security controls in place in and behind the head end router.
So there you have it. This should be enough of a framework to get up and running using Raspberry Pi as a remote IPSec endpoint for a LAN-to-LAN tunnel. In my testing, I got 15-20 mbps to pass through the tunnel with iperf, which isn’t bad considering the platform. To my knowledge this is absolutely the cheapest way to throw a VPN spoke out onto the internet. And if you haven’t played with Raspberry Pi yet, and you’re a networking nerd, it’s a great way to blow 35 bucks and have a little fun playing with a new toy.