Recently, I read a comment on Hacker News about someone who had created their own private mesh network between their Virtual private servers (VPS).

I decided to try and replicate this myself across three of my VPS instances.

Installing WireGuard

All three of my instances are running Ubuntu 18.04, see the WireGuard instructions for other setups: instructions

sudo add-apt-repository ppa:wireguard/wireguard
sudo apt-get update
sudo apt-get install wireguard-dkms wireguard-tools

After installing WireGuard on all three machines, I was able to proceed to configuring WireGuard.

Configure WireGuard

Generate Keys

On each machine, I created a private/public key pair:

(umask 077 && printf "[Interface]\nPrivateKey = " | sudo tee /etc/wireguard/wg0.conf > /dev/null)
wg genkey | sudo tee -a /etc/wireguard/wg0.conf | wg pubkey | sudo tee /etc/wireguard/publickey

Note: umask in the first line will create a new file with limited permissions.

We have also saved the public key to /etc/wireguard/publickey for use later.

Configuring the Interface

Edit each machines WireGuard config to add ListenPort and SaveConfig.

/etc/wireguard/wg0.conf:

ListenPort = 51820
SaveConfig = true
ListenPort:This can be any open port, make sure to poke a hole in your firewall
SaveConfig:Automatically save configuration

Next, I added a unique IP address to each config, this address will serve as a private IP for each server.

Server 1 - /etc/wireguard/wg0.conf:

Address = 10.0.0.1/24

Server 2 - /etc/wireguard/wg0.conf:

Address = 10.0.0.2/24

Server 3 - /etc/wireguard/wg0.conf:

Address = 10.0.0.3/24

The addresses are provided in CIDR notation. You can pick any subnet you'd like for your own network, I used 10.0.0.0/24.

After adding IP addresses we are read to configure peers.

Server 2 - /etc/wireguard/wg0.conf:

[Interface]
...

[Peer]
PublicKey = public_key_of_first_server
AllowedIPs = 10.0.0.1/32
Endpoint = public_IP_of_first_server:51820

[Peer]
PublicKey = public_key_of_third_server
AllowedIPs = 10.0.0.3/32
Endpoint = public_IP_of_third_server:51820

Server 3 - /etc/wireguard/wg0.conf:

[Interface]
...

[Peer]
PublicKey = public_key_of_first_server
AllowedIPs = 10.0.0.1/32
Endpoint = public_IP_of_first_server:51820

[Peer]
PublicKey = public_key_of_second_server
AllowedIPs = 10.0.0.2/32
Endpoint = public_IP_of_second_server:51820

Starting WireGuard

Open firewall

I am using ufw as my firewall and I poked holes on each machine:

sudo ufw allow 51820

Start it up

Now, I started WireGuard for the first time on each machine:

sudo systemctl start wg-quick@wg0

Next, we can check the VPN network interface wg0:

ip addr show wg0

Finally, check the state of WireGuard its self:

sudo wg

Note: the first server should only show an interface config, while the other two will also show configured peers.

Add missing peer configuration

On the first server add configuration for the other two:

sudo wg set wg0 peer public_key_of_second_server endpoint public_IP_of_second_server:51820 allowed-ips 10.0.0.2/32
sudo wg set wg0 peer public_key_of_third_server endpoint public_IP_of_third_server:51820 allowed-ips 10.0.0.3/32

Now check the connection from the first computer to either the second or third computer:

ping -c 3 10.0.0.3

Save the configuration by restarting WireGuard on the first server:

sudo systemctl restart wg-quick@wg0

Checking the VPN connection

Check the connection from each machine:

$ ping -c 3 10.0.0.1
$ ping -c 3 10.0.0.2
$ ping -c 3 10.0.0.3

Sample output:

PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=45.1 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=22.0 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=22.0 ms

--- 10.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 22.034/29.734/45.134/10.889 ms

Configure WireGuard to start on boot

sudo systemctl enable wg-quick@wg0

Conclusion

Setting up a Virtual Private Cloud with my own private mesh was super easy using WireGuard. I was able to follow along instructions from a couple posts to get it working. In retrospect the documentation on the WireGuard site looks quite handy as well.