Let’s say you have two servers: Cabbage and BEETroot (naming your servers after vegetables is normal, right?). Cabbage hosts a database and some other services and BEETroot hosts your application. You want them to be able to connect to each other securely, without having to deal with securing every one of the services to go over the Internet.

This guide was written for Ubuntu Server 18.04, but should be valid for any Linux system (aside from the installation steps).

Installing Wireguard

sudo add-apt-repository ppa:wireguard/wireguard
sudo apt-get install wireguard

Generating keys

On each server, a private key needs to be generated and a public key derived from it. For convenience, the bottom command will save both to files, named after the server’s hostname:

user@both-servers:~ $ wg genkey | tee "$(hostname -s).wgpriv" | wg pubkey > "$(hostname -s).wgpub"

Both should look similar to this: gMFBuyVWbx/O9DAn2ajAhnNN0GAfZTM8u7d0HTJoqWs=

Configuring

The Wireguard config files reside in /etc/wireguard/. They are ini files, use the .conf extension and are named after their Wireguard interfaces (wg[number]).

You will also need to decide on a port (which you will have to open) and private IP address for each of the servers (on the same subnet, of course).

Our convention is to use the same iface name and port (starting at 51820) on both sides of a P-P link and IPv4 addresses in a /31 subnet. (iface wg[n] ipv4 10.0.[n].[0,1]/31 port 51820+[n])

Edit /etc/wireguard/wg[n].conf with root privs:

# P-P link to [other side]

# Our config
[Interface]
Address = [our INTERNAL address]/31
PrivateKey = [our privkey]
ListenPort = [our port]

# [other side]
[Peer]
PublicKey = [other side's pubkey]
AllowedIPs = [other side's INTERNAL address]/31
Endpoint = [other side's EXTERNAL address]:[other side's port]

An example for Cabbage would therefore be:

# P-P link to BEETroot

# Our config
[Interface]
Address = 10.0.0.1/31
PrivateKey = gMFBuyVWbx/O9DAn2ajAhnNN0GAfZTM8u7d0HTJoqWs=
ListenPort = 51820

# BEETroot
[Peer]
PublicKey = omFWU/kRAAQOU+31j6RoIPA7HLVSqW67BvQuZ9z1uxA=
AllowedIPs = 10.0.0.0/31
Endpoint = beetroot.example.com:51820

Applying and testing

Assuming the previous example (wg0):

# Apply config
user@both-servers:~ $ sudo wg-quick up wg0
# Start the connection
user@both-servers:~ $ systemctl start wg-quick@wg0
# Make start on startup (optional)
user@both-servers:~ $ systemctl enable wg-quick@wg0

Let’s see if it works (example output):

user@Cabbage:~ $ sudo wg show
interface: wg0
  public key: omFWU/kRAAQOU+31j6RoIPA7HLVSqW67BvQuZ9z1uxA=
  private key: (hidden)
  listening port: 51820

peer: omFWU/kRAAQOU+31j6RoIPA7HLVSqW67BvQuZ9z1uxA=
  endpoint: 198.51.100.42:51820
  allowed ips: 10.1.0.0/31
  latest handshake: 1 second ago
  transfer: 148 B received, 92 B sent

user@Cabbage:~ $ ping 10.1.0.0 -c 1
PING 10.1.0.0 (10.1.0.0) 56(84) bytes of data.
64 bytes from 10.1.0.0: icmp_seq=1 ttl=64 time=46.1 ms

--- 10.1.0.0 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 46.174/46.174/46.174/0.000 ms