Wireguard VPN
UPDATE 2020-03-30 Wireguard 1.0 was included in Linux Kernel 5.6.
UPDATE 2018-08-06 Wireguard has been submitted for inclusion into the Linux Kernel source tree.
I recently stumbled upon what I think may be the holy grail - a VPN method that is simple to configure, high performance, and (so I’m told) highly secure. Until now my experience of using VPNs was that you could choose any two of the above, but never expect to get all three!
The ease by which you can get this up and running is quite astonishing. The documentation is quite good, but still has a few holes which hopefully will be covered here, adapted from https://www.wireguard.com/install/
Wireguard is conceptually quite different to other VPN products in that there isn’t a daemon that runs - it all happens in the linux kernel. There also isn’t any state: no concept of a tunnel being ‘up’ or ‘down’ - just a standard network interface with configuration applied to it - not dissimilar to a wifi interface. This has the advantage of allowing traffic to route seamlessly between, for example, fixed and wireless connections.
NOTE: Wireguard is not yet merged into mainline kernel which means compiling the required kernel module from source. Fortunately, thanks to DKMS this step is painless.
Installation
Both ends of the VPN described here are running stock Centos7
$ curl -Lo /etc/yum.repos.d/wireguard.repo https://copr.fedorainfracloud.org/coprs/jdoss/wireguard/repo/epel-7/jdoss-wireguard-epel-7.repo
$ yum install epel-release
$ yum install wireguard-dkms wireguard-tools
Configuration
Configuring wireguard can be done from command line with ip
(from the iproute
package) and wg
(from wireguard package) commands. I would recommend however not doing that, but instead using the included systemd service file which reads from a config file, described below.
Each endpoint has a single config file, similar to this: /etc/wireguard/wg0.conf
[Interface]
ListenPort = 51820
PrivateKey = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Address = 172.16.0.1/30
[Peer]
PublicKey = yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
AllowedIPs = 172.16.0.0/30, 10.0.1.0/24, 10.0.2.0/24
Endpoint = x.x.x.x:51820
Endpoint = x.x.x.x:51820
corresponds with the public IP (not hostname) and listening port of your peer. This value can be defined at both ends of a Wireguard connection, or just one end (e.g. if one end is hidden behind NAT). If one end doesn’t have this defined, then it just waits for incoming traffic and figures out the other peer from that.AllowedIPs =
is set to any IPs or subnets that should be routed via this tunnel.- IPv6 is also supported. If you don’t have routable address space, then generate a ULA address
fdxx:xxxx:xxxx:yyyy::/64
IPv4 routing is enabled. This is set in /etc/sysctl.conf
with: net.ipv4.ip_forward = 1
Authentication
The public/private key are generated using the wg
utility. wg genkey
generates the private key string. This string can then be piped into wg pubkey
to generate a corresponding public key e.g. all-in-one command
$ wg genkey | tee /dev/tty | wg pubkey
4EqGj/rnwKHidsQLsD9ktUCwIfLpwQ7TsHNK7zq4CUQ=
/pLbKEiydGaGZY1lyVaiCXOQY8sFh0O22D8f+3heHX0=
The private key (first line) goes into local config file, and the public key goes into the peer’s config file.
Usage
Systemd can bring the VPN up/down using the included wg-quick service file. To set the VPN to come up on boot enable the service:
systemctl enable wg-quick@wg0
Now, start/stop the service like so:
systemctl start wg-quick@wg0
systemctl stop wg-quick@wg0
This adds the wg0
interface, and inserts routes corresponding with the list of allowed IPs specified in the config file.
Networking
Firewall
The VPN itself uses a single UDP port. For the VPN tunnel to connect, both ends must be able to reach the other on UDP port 51280
. The port number is configurable.
Routing
- the tunnel uses the addresses 172.16.0.1 for A-end and 172.16.0.2 for B-end
- routes for each end’s network(s) are sent via the VPN interface
wg0
Diagnostics
Having done all the above, if things don’t appear to be working out, here’s some things to look at first:
- Check systemd log for the
wg-quick@wg0
service:journalctl -u wg-quick@wg0
- Check the
wg0
interface is up withip addr
:
6: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 8921 qdisc noqueue state UNKNOWN qlen 1
link/none
inet 172.16.0.1/30 scope global wg0
valid_lft forever preferred_lft forever
inet6 fe80::5e6:1a69:4213:44ba/64 scope link flags 800
valid_lft forever preferred_lft forever
- Attempt to ping the endpoing on the other end of the IP e.g.
ping 172.16.0.2
- Run tcpdump on each endpoint to see what traffic is coming in/out of the ethernet interfaces (
eth0
). Encrypted VPN traffic will show up as UDP packets on port51280
. - Run tcpdump on each endpoint’s wireguard interface (
wg0
) to see what’s passing over the tunnel itself.
Providing that the wireguard config is correct - keys match up, and allowed IPs are set - then you’re going to be dealing with a routing or firewalling issue somewhere in between.