Wireguard is undoubtedly the go-to choice for a VPN. It's operationally simple, utilises state of the art cryptography, built into the Linux kernel, and evangelised by Linus himself1.
Using systemd, we can declaratively deploy Wireguard on almost any host by copy-and-pasting a small handful of files. Let's not dawdle.
The Wireguard Device
First, we prepare a Wireguard virtual network device.
/etc/systemd/network/99-wg0.netdev
[NetDev]
Name=wg0
Kind=wireguard
Description=WireGuard tunnel wg0
[WireGuard]
ListenPort=51820
Wireguard uses asymmetric cryptography to connect devices together.
We could store a private key in the 99-wg0.netdev
file in plain text, but that's not particularly secure.
Instead we can rely on systemd-creds
to protect the private key. And by using a subshell (inside the <(...))
we don't reveal the private key at any point.
systemd-creds encrypt <(openssl rand -base64 32) /etc/credstore.encrypted/network.wireguard.private.99-wg0
The Wireguard Network
Next up is the Wireguard network. Address=
is prechosen to relieve mental burden on yourself, the reader — you can
change it if you prefer; but please, choose something from the allocated
pool of private IPs: 192.168.0.0/16,
172.16.0.0/12,
or 10.0.0.0/8.
RFC 19182
/etc/systemd/network/99-wg0.network
[Match]
Name=wg0
[Network]
Address=172.16.0.1/12
Run systemctl restart systemd-networkd
followed by wg
to see your newly created virtual device.
$ wg
interface: wg0
public key: (base64 key)
private key: (hidden)
listening port: 51820
Connecting a Peer
A VPN is no good without a devices connecting to it. So let's create a peer. A peer is simply a device in the network: a server, a laptop, a phone, etc.
It's quite impossible to give initial instructions here as the configuration method will depend on what device you're using as a peer. If you're using macOS, iOS, or iPadOS, the official Wireguard.app is great.
Regardless, it doesn't matter what device you choose because after you fill in the placeholders below, the configuration will look similar:
[Interface]
PrivateKey=(hidden)
Address=172.16.0.2/32
DNS=194.242.2.2
[Peer]
PublicKey=<copied from wg command>
AllowedIPs=0.0.0.0/0, ::/0
Endpoint=<server ip>:51820
Here's a pithy description of each setting:
-
PrivateKey=is probably generated for you, depending on the device -
Address=is the address of this peer -
DNS=can be omitted, but I've provided Mullvad's3 because they're in the business of privacy. They use Wireguard too. -
PublicKey=needs to be copied from your server. -
AllowedIPs=send all traffic through the VPN -
Endpoint=is the IP or domain name of your server. Probably the same as yoursshcommand
Back over on the server, you'll need to register your new peer. We'll use a systemd drop-in so that we don't have to change any of the previous files.
/etc/systemd/network/99-wg0.netdev.d/peer.conf
[WireGuardPeer]
PublicKey=(peer public key)
AllowedIPs=172.16.0.2/32
Test the Network
Suprisingly, that's all you need. Enable Wireguard on your device and
run wg
on your server. You'll see something like below.
$ wg
interface: wg0
public key: (base64 key)
private key: (hidden)
listening port: 51820
peer: (base64 key)
endpoint: <peer ip>:52136
allowed ips: 172.16.0.2/32
latest handshake: 6 seconds ago
transfer: 2.60 MiB received, 3.55 MiB sent
If you see a handshake and some transferred data, you've got yourself a successfully established connection.
Routing to the Internet
If you want your remote peers to connect to the internet via your server, you'll need to have IP forwarding enabled. Easy enough with a systemd drop-in for sysctl4:
/etc/sysctl.d/30-ipforward.conf
net.ipv4.ip_forward=1
net.ipv6.conf.default.forwarding=1
net.ipv6.conf.all.forwarding=1
Then enable.
sysctl --system