DIY Linux Router Part 6: WireGuard VPN
The following is the sixth part of a multipart series describing how I build (software not hardware) my own Linux router from scratch, based on Debian 11.
- Part 1: Hardware
- Part 2: Interfaces, DHCP and VLAN
- Part 3: PPPOE and Routing
- Part 4: Firewall and Port Forwards
- Part 5: DNS with Unbound
- Part 7: WiFi
- Part 8: NetFlow / IPFIX
I don't have much use for a VPN into my home network, but sometimes it's nice to be able to reach my Synology shares from my laptop while being away.
There are a lot of different VPN software solution out there, the big three one being IPsec, OpenVPN and WireGuard.
- IPsec is very fast and widely available, but a nightmare to configure and debug.
- OpenVPN ist much easier to configure and debug but not the fastest (Still fast enough for my 200/45 mbit/s).
- WireGuard is the newest, it's very easy to configure and even faster than IPsec. But it currently lacks tooling around it. There is no IP address auto configure for example.
I used OpenVPN in the past and switched to WireGuard when in became available in Ubuntu 20.04 without the need for a third party repo and was even integrated natively into systemd-networkd.
You can install WireGuard with apt:
apt install wireguard
WireGuard is very basic, it just creates a virtual interface on a computer (peer) and connects it to one or more virtual interfaces on other computers. WireGuard does not concern itself with distributing IPs. You habe to choose the IPs for you peers manually and configure them in the WireGuard configuration.
I use 10.10.10.0/24 as my WireGuard network.
- 10.10.10.1 - Router
- 10.10.10.2 - Android
- 10.10.10.3 - Surface Pro
WireGuard uses private and public keys for authentication. You need one key pair per peer using WireGuard. There is no server/client model, every peer and can route whatever you want through another peer. To generate a key you use the
wg genkey | tee private.key | wg pubkey > public.key
This will give you a private and public key inside these two files. Do this for every peer you have.
Like OpenVPN, WireGuard also can use an additional preshared key to encapsulate all UDP packets that are send between peers. This will in theory help with post-quantum resistance, for the day when quantum computers maybe exist. This key can be generated with:
wg genpsk > psk.key
Resulting in the file:
Since WireGuard is integrated into systemd-networkd, we can use .netdev and .network files to configure it. First we create a virtual WireGuard interface:
WireGuard section includes the UDP port we want WireGuard to listen on (51820 being the default) and the private key of our router peer.
WireGuardPeer section can be added multiple times and each correspond to one other peer. These sections include the public key of the peer, the optional preshared key (this can be different for each peer) and the allowed IPs. The
AllowedIPs option restricts which source IPs are allowed, when coming into the WireGuard interface on the router, from the corresponding peer. The Android and Windows Client use NAT by default when routing traffic through WireGuard, so we only need to allow the internal IPs.
We then add the chosen IP address for the router and add it to the interface using a .network file. Use /24 as a subnet mask to automatically set up a route for 10.10.10.0/24 to reach our other peers on this interface.
With these two files. we can restart systemd-networkd.service or reboot for the changes to apply. The interface should looks like this then:
sudo wg should look like this:
We now need some iptables rules to open the listen port and allow forwarding of traffic coming from the WireGuard interface.
The Windows and Android Client both use the default WireGuard ini config file format. To connect the clients to the router and then route the home network traffic through it, we would use the following:
You need to configure an endpoint, since at least one peer needs to know where the other peer is located. Since your IP can change when using a home network connection, I recommend using a DynDNS Service. I wrote one myself: https://www.sherbers.de/running-my-own-dynamic-dns-service-with-cloudflare/
Setting the DNS options allows the clients to use the Unbound DNS server and use internal DNS names to reach resources if there are any. We can also route all traffic through the router if we want:
You should now be able to route your clients through your router and access resources on the internal network. An easy way to test this, is to disconnect your phone from your Wi-Fi, go to any of the many "Whats my IP address" Websites and see if your IP changes to you home IP when activating the WireGuard tunnel.
Next up: Wifi