sbs20

DIY Devices - The VPN

2021-07-28 DIY devices Wireguard VPN

What a VPN looks like

There are times where it's really useful to be on my home network when I'm away. In the past I have achieved this with SSH port forwarding. It works well enough for most things - but it's a bit of a faff changing web proxies and of course it doesn't work easily for everything. I thought it would be easy just to set up OpenVPN. I was wrong.

Fast forward a few years and I saw Brian Moses tweeting about Tailscale, so I took a closer look. It's a really impressive service. And if I had to recommend a VPN to someone, it would be this.

The thing about Tailscale, though, is that it relies on a third party provider. It sounds like a great company with high ideals - but then look what happened to WhatsApp. Plus, I also have my own VPS and, well, I wanted to dig deeper, learn more and generally make it more complicated because that's more fun. It uses Wireguard underneath and the more I read about it, the more impressive it was - and when you have the likes of Linus Torvalds calling it a "work of art", it's something to be taken seriously.

There are so many different ways to configure any network - and a VPN is no different. I had thought that it might be nice to use my home router as the main VPN peer but my home IP, while pretty stable, is not guaranteed to stay stable. And while I can use DDNS to fix that, it's just another thing to go wrong. So, to prototype things, I thought I would get started by using my Debian VPS - hosted on Digital Ocean - to get things going.

And get it going I did. I am now able to connect to my home network and those of two separate families (with conflicting 192.168.0.0/24 subnets) and do so from anywhere - even my phone. Coupled with JuiceSSH I can do pretty much whatever I want wherever I am (although it's not conducive to easy working).

How? #

Installing on Debian (Buster) was just a matter of:

echo "deb http://deb.debian.org/debian/ buster-backports main" >> /etc/apt/sources.list
apt-get install wireguard wireguard-dkms wireguard-tools linux-headers-$(uname -r)

Then I chose an available subnet for a new VPN network and created a config file. I didn't do this in one go of course, because I spent ages reading about all the different options, getting it wrong and trying again. The best documentation I found was this repo by someone called pirate.

Go and read it, but in short:

Conclusion #

I have stayed with my "prototype", using the VPS as the central peer. And I'm really happy about it all. I love Wireguard. I love how easy it is to work with (once you get what's going on). And it's SOOO much easier than OpenVPN. The one downside I've found is that it breaks when the kernel gets updated - so apt upgrade can be bad. This doesn't matter for most of my peers since I can also SSH to them - but for the remote peers inside other home networks this can be bad. I haven't solved this yet, because it's not been too pressing, but I expect a simple script would do the job.

Appendix #

The actual setup #

The example below shows:

The bounce server #

/etc/wireguard/wg0.conf

[Interface]
# Name = vpn.example.com
Address = 10.0.254.1/32
ListenPort = 51820
PrivateKey = ${vpn-private-key}
# My internal DNS server
DNS = 10.0.0.1

PostUp = iptables -I FORWARD 1 -i %i -j ACCEPT
PostDown = iptables -D FORWARD -i %i -j ACCEPT

[Peer]
# Name = laptop.example.com
PublicKey = ${laptop-public-key}
AllowedIPs = 10.0.254.9/32

[Peer]
# Name = relay.example.com
PublicKey = ${relay-public-key}
AllowedIPs = 10.0.254.10/32,10.0.0.0/16

[Peer]
# Name = other.example.com
PublicKey = ${other-public-key}
AllowedIPs = 10.0.254.11/32,172.16.0.0/24

Laptop #

Nothing special here - just that it can access any 10.0.0.0/16 or 172.16.0.0/16 address via the bounce server, and it uses my internal DNS server.

[Interface]
# Name = laptop.example.com
PrivateKey = ${laptop-private-key}
Address = 10.0.254.9/32
DNS = 10.0.0.1, 10.0.0.2

[Peer]
# Name = vpn.example.com
PublicKey = ${vpn-public-key}
AllowedIPs = 10.0.0.0/16, 172.16.0.0/16
Endpoint = ${bounce-server-ip:port}

Relay #

The relay sits inside my home network. It could be my router, but it's not - it's a VM (but could be a docker container). Notable things:

[Interface]
# Name = relay.example.com
PrivateKey = ${relay-private-key}
Address = 10.0.254.10/32

PostUp = sysctl net.ipv4.ip_forward=1
PostUp = iptables -A FORWARD -i %i -j ACCEPT
PostUp = iptables -A FORWARD -i eth0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = sysctl net.ipv4.ip_forward=0
PostDown = iptables -D FORWARD -i %i -j ACCEPT
PostDown = iptables -D FORWARD -i eth0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
# Name = vpn.example.com
PublicKey = ${vpn-public-key}
AllowedIPs = 10.0.254.0/24,172.16.0.0/16
Endpoint = ${bounce-server-ip:port}
PersistentKeepalive = 25

Other #

This builds on the previous examples but it also adds address translation.

[Interface]
# Name = other.example.com
PrivateKey = ${other-private-key}
Address = 10.0.254.11/32

PostUp = sysctl net.ipv4.ip_forward=1
PostUp = iptables -A FORWARD -i %i -j ACCEPT
PostUp = iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
PostUp = iptables -A FORWARD -i eth0 -j DROP
PostUp = iptables -t nat -A PREROUTING -d 172.16.0.0/24 -i %i -j NETMAP --to 192.168.0.0/24
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

PostDown = sysctl net.ipv4.ip_forward=0
PostDown = iptables -D FORWARD -i %i -j ACCEPT
PostDown = iptables -D FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
PostDown = iptables -D FORWARD -i eth0 -j DROP
PostDown = iptables -t nat -D PREROUTING -d 172.16.0.0/24 -i %i -j NETMAP --to 192.168.0.0/24
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
# Name = vpn.example.com
PublicKey = ${vpn-public-key}
AllowedIPs = 10.0.254.0/24,10.0.0.0/16
Endpoint = ${bounce-server-ip:port}
PersistentKeepalive = 25

Bounce server firewall rules #

If you're enabling forwarding on a public facing server, then you will want to stop some of that. Yours will be different - but this is a good start

# /etc/iptables/rules.v4

# Install iptables-persistent
# `sudo apt-get install iptables-persistent`

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

COMMIT

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

# ==============================================================================
# Service rules
# basic global accept rules - ICMP, loopback, traceroute, established all accepted
-A INPUT -s 127.0.0.0/8 -d 127.0.0.0/8 -i lo -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -m state --state ESTABLISHED -j ACCEPT
-A INPUT -m conntrack --ctstate INVALID -j DROP

# enable traceroute rejections to get sent out
-A INPUT -p udp -m udp --dport 33434:33523 -j REJECT --reject-with icmp-port-unreachable

# SSH, Wireguard
-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A INPUT -p udp -m udp --dport 51820 -j ACCEPT
-A INPUT -j DROP

# ==============================================================================
# Forwarding rules
-A FORWARD -j DROP

COMMIT

Install on Raspberry Pi #

Installing Wireguard on a Raspberry Pi is slightly more involved, but this works really well. See the initial link for any further updates.

## https://github.com/adrianmihalko/raspberrypiwireguard
sudo apt-get install raspberrypi-kernel-headers
echo "deb http://deb.debian.org/debian/ unstable main" | sudo tee --append /etc/apt/sources.list.d/unstable.list
sudo apt-get install dirmngr
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 8B48AD6246925553
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 7638D0442B90D010
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 04EE7237B7D453EC
printf 'Package: *\nPin: release a=unstable\nPin-Priority: 150\n' | sudo tee --append /etc/apt/preferences.d/limit-unstable
sudo apt-get update
sudo apt-get install wireguard
sudo reboot