This post will guide you through the most basic WireGuard scenario: connecting from a remote device (“peer”) to a server somewhere out there through which you want to route your traffic securely.

I’ve always been on a crusade to improve VPN performance since I do a fair bit of work remotely with reasonable bandwidth at my disposal. Until WireGuard, I’d not been happy with the performance hit most VPN implementations impose. With WireGuard, I can get 900+mbps out of a gigabit link with minimal CPU overhead.

WireGuard is a kernel module that creates a virtual interface (wg0) that can be manipulated using standard tools like route and ifconfig. As such, you’ll need to be running a reasonably recent Linux kernel. If you’re running a custom or older kernel, you might want to first see the requirements to make sure you’re good to go.

The examples shown below are run on an Ubuntu 16.04 install with a 4.4 kernel and a peer running macOS Mojave.

1. Install WireGuard on your server

SSH in and install the WireGuard packages:

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

If you see any errors, particularly with DKMS, you should really go read the requirements above.

2. Generate a keypair for your server

Please take good care of your keys. Don’t leave them sitting around!

You need to have a public and private key on every system that runs WireGuard. You’ll be running this command a lot, so it helps to understand what’s going on.

First, your umask is set to 077. This ensures that the resulting files, privatekey and publickey are readable only by you. Then, we use the Wireguard command wg to generate the private key, and then use the private key to generate a public key.

umask 077; wg genkey | tee privatekey | wg pubkey > publickey

3. Create the configuration file

Configs are held in /etc/wireguard. We’ll configure this as /etc/wireguard/wg0.conf:

[Interface]
Address: 10.20.90.1/24
PostUp: iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o br0 -j MASQUERADE
PostDown: iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o br0 -j MASQUERADE
ListenPort: 51820
PrivateKey: <contents of “privatekey” generated earlier>
SaveConfig: true

Note that in your case, you will likely need to substitute eth0 for my example above. I have a bridge interface (br0) on my host that contains my eth0 interface. If you want to do IPv6, you’ll need to add corresponding IPv6 rules using ip6tables. You’ll also have to add an IPv6 address to the [Interface] configuration right after your IPv4 network, separated by a comma.

In this example, you’ll see that we are allocating 10.20.90.1/24 to the wg0 interface. Peers that connect will have addresses within this subnet.

4. Permit incoming traffic

Make sure your firewall permits incoming traffic to the ListenPort defined above. Since we defined the port as 51820, that’s what needs to be allowed in. Wireguard uses UDP, so make sure you allow UDP and not TCP traffic:

ufw allow 51820/udp

5. Optional: set WireGuard to start on boot

This ensures that WireGuard will start and listen for connections after system reboots.

systemctl enable wg-quick@wg0

6. Start WireGuard

Wireguard ships with a helper, called wg-quick, that you’ll use to start/stop the service as well as perform other duties.

wg-quick up wg0

At this point, your WireGuard server is ready to go. It’s listening for incoming connections from other peers (clients) and will start automatically should you reboot your server.

Adding Peers

For each client, you’ll follow the same procedure as outlined below. For mobile devices, you can craft a configuration file as seen below and encapsulate it within a QR code that WireGuard apps can use to quickly import. To do so, use qrencode -t ansiutf8 < wg0.conf.

1. Generate a keypair for your peer

I did warn that you’ll be using these commands again!

umask 077; wg genkey | tee privatekey | wg pubkey > publickey

2. Create the configuration file

Just like on the server, create /etc/wireguard/wg0.conf

[Interface]
PrivateKey: <contents of the privatekey file you generated on the client>
Address: 10.20.90.101/32
DNS: 8.8.8.8

[Peer]
PublicKey: <contents of the publickey file generated on the server>
AllowedIPs: 0.0.0.0/0, ::/0
Endpoint: <ip of your Wireguard server>:51820
PersistentKeepalive: 25

3. Add the peer to the server

Because we specified SaveConfig above, don’t edit wg0.conf by hand. Instead, use the ‘wg’ CLI to add the peer:

wg set wg0 peer <contents of the publickey file on your client> allowed-ips 10.20.90.101/32

4. Connect to the server

You should now be able to start WireGuard on the client and it will connect and route all its traffic through the WireGuard server. On the client side, use wg-quick to turn up the interface, just like on the server: wg-quick up wg0

On either the client or server, you can use ‘wg’ to see client status. Before a client connects, it’ll look similar to this:

# wg
interface: wg0
  public key: <…>
  private key: (hidden)
  listening port: 51820

peer: <…>
  allowed ips: 10.20.90.101/32

Once a client connects, you’ll see more detail, like this:

# wg
interface: wg0
  public key: <…>
  private key: (hidden)
  listening port: 51820

peer: <…>
  endpoint: <your remote public IP and port>
  allowed ips: 10.20.90.102/32
  latest handshake: 4 seconds ago
  transfer: 16.76 KiB received, 10.14 KiB sent

That’s it! There are many settings you can play with to adjust how traffic flows such as routing smaller subnets of traffic through the Wireguard tunnel and leaving the rest alone. Because WireGuard uses a simple interface, you can use all your standard host utilities to manipulate traffic just as you would a traditional Ethernet interface.