A mesh VPN using OpenBSD and WireGuard
752 words, 4 minutes
WireGuard is a new coming to OpenBSD 6.8 and it looks like a simple and efficient way to connect computers.
I own a few VPS (hello Vultr, hello OpenBSD.amsterdam) that tend to be connected through filtered public services and/or SSH tunnels. And that’s neither efficient nor easy to manage. Here comes the wg(4) era where all those peers will communicate with a bit more privacy and ease of management.
Road-warrior meets Server
Lets start with reading wg(4) and ifconfig(8) man pages. There are private keys involved in the process. And as far as I can understand, there is no specific directive on how to store them. I first thought about storing them in /etc/ssl/private. But I decided to treat those private keys like a do with wireless passphrases ; and store them in the hostname.if(5) file.
Using a pre-shared key isn’t mandatory but it helps because it “(…) offers a post-quantum resistance to the Diffie-Hellman exchange”. So let’s create and use one for that peer-to-peer connection:
# openssl rand -base64 32
Remember that key. It will have to be referenced in both peer’s configuration file.
On the Road-Warrior side, generate a private key and configure a basic WireGuard interface:
# cat > /etc/hostname.wg0 << EOF
wgkey $(openssl rand -base64 32) wgport 51820
192.168.2.20 netmask 255.255.255.0
up
EOF
# chmod 0600 /etc/hostname.wg0
On the Server side, repeat the previous commands.
On both side, we can now see the public key using ifconfig(8) and looking at the wgpubkey line. Grab the public key from the Server and reference it in the Road-Warrior’s configuration. Then grab the public key from the Road-warrior and reference it in the Server’s configuration.
root@roadwarrior# cat >> /etc/hostname.wg0 << EOF
wgpeer <Insert Server Public Key> \
wgpsk <Insert Preshared Key> \
wgendpoint <Insert Server IP or FQDN> 51820 \
wgaip <Insert Server wg0 IP>/32
EOF
root@server# cat >> /etc/hostname.wg0 << EOF
wgpeer <Insert Road-Warrior Public Key> \
wgpsk <Insert Preshared Key> \
wgendpoint <Insert Road-Warrior Last Known IP> 51820 \
wgaip <Insert Roard-Warrior wg0 IP>/32
EOF
On both side, mount the wg0 interface and check connectivity:
root@roadwarrior# sh /etc/netstart wg0
root@roadwarrior# pfctl -f /etc/pf.conf
root@roadwarrior# ping server
root@server# sh /etc/netstart wg0
root@server# pfctl -f /etc/pf.conf
root@server# ping roadwarrior
Both side should be pinging each other. Using ifconfig, you can check the peer connection. Be sure to allow traffic on configured UDP port in pf.conf(5) and reload pf(4) after the wg0 interface creation.
You may want to set the Server as the default router for the Road-Warrior. This is not what I want here. I’m just enabling remote connection to UDP and TCP ports on the Server ; ports that I don’t want to be exposed directly on the Wild Wild Web.
Server-to-Server configuration
To add another server to the mesh is quite straight forward. Simply replicate the private key generation/configuration on the New Server. Then teach the Server about the New Server and vice-versa. Then do the same with New Server and Road-Warrior.
root@newserver# cat > /etc/hostname.wg0 << EOF
wgkey $(openssl rand -base64 32) wgport 51820
192.168.2.10 netmask 255.255.255.0
up
wgpeer <Insert Server Public Key> \
wgpsk <Insert Preshared Key> \
wgendpoint <Insert Server IP or FQDN> 51820 \
wgaip <Insert Server wg0 IP>/32
EOF
root@newserver# chmod 0600 /etc/hostname.wg0
root@newserver# sh /etc/netstart wg0
root@newserver# pfctl -f /etc/pf.conf
root@server# cat >> /etc/hostname.wg0 << EOF
wgpeer <Insert NewServer Public Key> \
wgpsk <Insert Preshared Key> \
wgendpoint <Insert NewServer FQDN or IP> 51820 \
wgaip <Insert NewServer wg0 IP>/32
EOF
root@server# sh /etc/netstart wg0
Test the connectivity using ICMP. I did an iperf3(1) test:
user@newserver# iperf3 -c 192.168.2.23 -f M
(...)
[ ID] Interval Transfer Bitrate
[ 5] 0.00-10.01 sec 1.02 MBytes 0.85 Mbits/sec sender
[ 5] 0.00-10.72 sec 1.01 MBytes 0.79 Mbits/sec receiver
This is clearly good since the connection between those two servers are connected via ADSL with an upload rate of 0,82 Mbps (according to SpeedTest) and 1023 Kbps (according to the box information GUI).
You don’t have to use the same preshared key for each peer couple. It’s up to you to choose high security and ease of use.
As for now, this is what has been achieved:
This can be reproduced to connect many more servers while keeping a decent decentralized connectivity. Dealing with the Private/Public keys might be a bit challenging. But I feel like it’s worth the feature. Time will tell.
Thanks to Matt Dunwoodie and Jason A. Donenfeld for the wg(4) driver ; and to all OpenBSD devs to make all this fun possible.