From f64fba5882fb144e5a257fb97ab9afeb8b43909c Mon Sep 17 00:00:00 2001 From: Nick Pegg Date: Mon, 17 Aug 2020 21:02:04 -0700 Subject: [PATCH] [post] Home VPN with Wireguard --- posts/2020-08-17_home-vpn-with-wireguard.yaml | 232 ++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 posts/2020-08-17_home-vpn-with-wireguard.yaml diff --git a/posts/2020-08-17_home-vpn-with-wireguard.yaml b/posts/2020-08-17_home-vpn-with-wireguard.yaml new file mode 100644 index 0000000..c80db34 --- /dev/null +++ b/posts/2020-08-17_home-vpn-with-wireguard.yaml @@ -0,0 +1,232 @@ +date: 2020-08-17 +tags: +- linux +- networking +title: Home VPN with Wireguard +--- +If you have an interest in Linux networking, by now you've probably heard of +[Wireguard](https://www.wireguard.com/). In case you haven't, it's a newer +cross-platform VPN whose main attraction is that it's way _way_ easier to set +up on Linux than other VPNs that have come before it. If you've ever had to set +up an IPSec VPN using Racoon or Openswan or StrongSwan or any other weird +animal-based tool, then you know how much of a royal pain in the ass it is. + +While I was on a tear improving my home network, I decided to finally bite the +bullet and set Wireguard up on my router and laptop, allowing me to securely +connect to my home network from anywhere. This post outlines what I did to make +that happen, in case you want to replicate it in your own home network. + +Fair bit of warning though: I'm assuming that you already have basic knowledge +of networking: subnetting, routing, that kind of stuff. + +--- +# My Home Network +To give you an idea of what I'm working with, the VPN server here will be my +[router running Ubuntu Linux]({{ "/2014/08/building-my-own-home-router-part-1/" | absolute_url }}), +and the client will be my laptop running Arch. My router setup is fairly basic +for a home-built Linux router: it has two network interfaces, one internal and +one external, has IP forwarding turned on in the kernel, and a bunch of +iptables rules to handle forwarding packets around and NAT. + +For the examples throughout this post, we'll call the VPN server `router` and +the client `laptop`. We'll assume that my home network's subnet is 10.0.0.0/24, +my router's internal network interface is called `intf-internal` and my +external interface is called `intf-external`. Since we need a subnet for the +VPN clients to use, we'll use 10.1.0.0/24 for that. + +I'll be giving you a crash-course in the basics of Wireguard here, but if you +want a deeper understanding of it you should visit [Wireguard's website for a +more in-depth overview](https://www.wireguard.com/#conceptual-overview). + +# Installation +Wireguard comes baked into the Linux kernel these days, but you'll need to +install the tools to be able to work with it. + +On Debian-based systems: +```bash +sudo apt install wireguard +``` + +On Arch Linux: +```bash +sudo pacman -S wireguard-tools +``` + +# Key Generation +Wireguard uses public-key encryption for all communication, and you need to +generate keypairs **for each peer** in the VPN. + +I like to be fancy and do all in mostly one line and have the public key +printed: +```bash +wg genkey | sudo tee /etc/wireguard/priv.key | wg pubkey | sudo tee /etc/wireguard/pub.key +sudo chown 600 /etc/wireguard/priv.key +``` + + +# Server Configuration +## WireGuard +Okay! Now that we have our keys generated it's to configure Wireguard. + +Here's my config in `/etc/wireguard/wg-home.conf`: +``` +[Interface] +# This Address both tells us the server's VPN address (10.1.0.1) as well as the +# subnet (10.1.0.0/24) +Address = 10.1.0.1/24 +# This is the UDP port the VPN server will be listening on +ListenPort = 51820 +# This is the contents of /etc/wireguard/priv.key on the server, which you +# generated earlier. NOTE: This is the literal key, NOT the path to the key file +PrivateKey = + +[Peer] +# This is the pub.key from our laptop. Like the priv key, this should be the +# literal key, not the path to the key file. +PublicKey = +# This is the IP address we've assigned to our client +AllowedIPs = 10.1.0.11/32 + +# You can keep adding more [Peer] blocks as you have more machines +``` + +This is a straight-forward config: under `[Interface]` we have an address with +a subnet defined, a port to listen on, and a peer defined. Under `[Peer]` the +`AllowedIPs` option in Wireguard configs is interesting because it essentially +defines what routes the peer on the other side handles for us; anything +destined for an IP in one of these subnets will get sent to that peer. In this +case it only handles traffic for itself, so we configure a single IP address, +so it's a single /32. + +Since we saved this file as `/etc/wireguard/wg-home.conf`, it will correspond +to a virtual interface on the router called `wg-home`. We want this interface +to come up automatically when the server boots up, and there are various ways +to do that depending on what network management system you use +(systemd-networkd, NetworkManager, etc.) but I prefer to just use the systemd +service units that are available: `wg-quick@.service` + +```bash +sudo systemctl enable wg-quick@wg-home.service +sudo systemctl start wg-quick@wg-home.service +``` + +If everything worked, you should be able to use the `wg show` command to see +that the configuration is applied: +```shell +$ sudo wg show +interface: wg-home + public key: + private key: (hidden) + listening port: 51820 + +peer: + allowed ips: 10.1.0.11/32 +``` + +## Iptables +Since we have custom iptables rules to handle the routing and NATing, we also +need some rules to allow +``` +# Allow outside clients to connect to Wireguard +-A INPUT -i intf-external -p udp --dport 51280 -j ACCEPT +# Anything coming from the VPN can talk to the router directly +-A INPUT -i wg-home -j ACCEPT + +# VPN clients are allowed to talk to the rest of the network, and vice versa +-A FORWARD -i wg-home -o intf-internal -j ACCEPT +-A FORWARD -i intf-internal -o wg-home -j ACCEPT +``` + +If you're running IPv6 on your network, don't forget to also add the same rules +via `ip6tables`! + +# Client Configuration +Okay we're in the home stretch! We just need to configure the client. Just like +the server, I've saved this config to `/etc/wireguard/wg-home.conf` so that it +will show up as the `wg-home` virtual interface. + +``` +[Interface] +Address = 10.1.0.11/24 +PrivateKey = +DNS = 10.0.0.1, home.your.net + +[Peer] +PublicKey = +AllowedIPs = 10.1.0.0/24, 10.0.0.0/24 +Endpoint = vpn.your.net:51820 +``` +This is quite a bit like the server's config, but with some interesting +changes. First, in `[Interface]` we define `DNS`. Any IPs listed here will be +used as DNS servers, and any non-IPs will be used as search domains. Using your +router for DNS with a search of your home network's domain means you can +reference your home computers by hostname, like `ssh router`, once you're +connected to the VPN. + +Under `[Peer]` you'll see an `Endpoint`, which is the external hostname for the +router. You'll also see that `AllowedIPs` is way different, +`10.1.0.0/24, 10.0.0.0/24`. This says that those two subnets are available on +the other side of the VPN, and any traffic destined for them should be sent +over the VPN. Be careful to not set this too wide, or otherwise you may disrupt +local (to your laptop) traffic if the public network you connected to uses +`10.x.x.x`. + +This sort of setup is typically called "split tunnel" in the VPN world, where +only a subset of traffic is being sent through the VPN. If instead you wanted +_all_ of your traffic sent over the VPN, you could just set +`AllowedIPs = 0.0.0.0/0, ::/0`, which will send all IPv4 and IPv6 traffic over. + +I also like to use the systemd service unit to manage this connection, but I +don't `enable` it so it doesn't automatically start when my laptop boots. +Instead I just start it ad-hoc when I want to connect to home. +```shell +sudo systemctl start wg-quick@wg-home.service +``` + +Just like the server, you can use `wg show` to see the status: +```shell +$ sudo wg show +interface: wg-home + public key: + private key: (hidden) + listening port: 59184 + +peer: + endpoint: :51820 + allowed ips: 10.1.0.0/24, 10.0.0.0/24 + latest handshake: 4 seconds ago + transfer: 1.52 KiB received, 2.09 KiB sent +``` + +And if we ping our server's VPN address: +```shell +$ fping 10.1.0.1 +10.1.0.1 is alive +``` + +Hooray! + +# Things to note +If your server has a tendency to change IPs (like home internet connections +do), and your `Endpoint` in your client's config points to a dynamic DNS +address, you'll need to either restart `wg-quick@wg-home.service` every time +that happens, or use the `reresolve-dns.sh` script that comes with the +Wireguard tools to handle this. The Arch Linux wiki has [a good writeup on how +to set that up](https://wiki.archlinux.org/index.php/WireGuard#Endpoint_with_changing_IP). + +The systemd service units make use of the `wg-quick` program to do the setup of +the VPN tunnels. You may think it's magical how it routes packets, but as a +matter of fact it just sets up an interface and routes, which you can inpect +just like any other interface or route in Linux: +```shell +$ ip addr show dev wg-home +80: wg-home: mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000 + link/none + inet 10.1.0.11/24 scope global wg-home + valid_lft forever preferred_lft forever + +$ ip route | grep wg-home +10.0.0.0/24 dev wg-home scope link +10.1.0.0/24 dev wg-home proto kernel scope link src 10.1.0.11 +``` +