WireGuard is an extremely simple yet fast and modern VPN and is amazingly easy to set up. It does not have the best documentation, so if you are not an operations expert, it might be a little overwhelming at the first glance. At least, it was for me when I first started exploring it as an option to connect our distributed digital signage players on a same VPN for easy tunnelling access.

Introduction

Let me start with a small description of our infrastructure and what we wanted to achieve with Wireguard. This is just an example and there could be hundreds of different use cases depending on the need. So, we install digital signage players at several client locations (some of which are inside moving trains and buses), so it is not very easy to access them when an operation requires SSH access. We are almost able to tackle this with managing these clients through Chef but there are instances where it might be necessary to SSH into them to perform some tasks that we would like to keep off chef.

While looking for solutions for secure tunnelling between the devices and also, at times, to provide us with secure SSH access to these devices, we came across Wireguard. Here is how it works:

  1. You create a Wireguard Server (this is an optional step if all devices you are connecting are accessible through a public IP, e.g. in case of distributed servers in AWS or other cloud providers).
  2. Configure the Wireguard Server to create an interface (usually called wg0). This step involves creating a private key and a public key.
  3. Install Wireguard on your devices and configure them to connect to the Wireguard server’s public IP (in case you are skipping the step of creating a Wiregaurd Server, you will need to configure each device to connect to every other device’s public IP).
  4. Configure the Server to relay traffic by setting net.ipv4.ip_forward = 1 on the kernel.

This is basically the gist of it and it is as easy as it sounds.

Wireguard Server

As discussed above, if you are just trying to connect a few devices, each with a publicly accessible IP, you might not need to set up a separate Wiregaurd Server. But if you need to manage a fleet of devices, all/some without a public IP, read on.

The Unofficial Wireguard Documentation is one of the best resource when you are trying to set up Wireguard. Read that to get to know the internals of how the configuration of Wireguard works. But if you want to get started quickly, here is my setup with Docker.

Docker Image

linuxserver.io provides a well maintained docker image to manage a Wireguard server that can be used right out of the box and provides a variety of configuration options. Here is a sample (slightly adapted) docker-compose.yml from their README:

version: "3.9"
services:
  wireguard:
    image: ghcr.io/linuxserver/wireguard
    container_name: wireguard
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Zurich
      - SERVERURL=wireguard.domain.com #optional
      - SERVERPORT=51820 #optional
      - PEERS=20 #optional
      - PEERDNS=auto #optional
      - INTERNAL_SUBNET=10.13.13.0
      - ALLOWEDIPS=10.13.13.0/24
    volumes:
      - /home/wireguard/config:/config
    ports:
      - 51820:51820/udp
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    restart: unless-stopped

If you have not used Docker before, don’t fret, it is very easy to set up. Just install docker and docker-compose, create an empty directory on your server, put the above docker-compose.yml in it and run docker-compose up and you should be ready with a fully set up server. There are some configuration options that you will need to change:

  1. PUID and PGID - This should be the uid and gid of the user that runs docker-compose up. This use should own the volumes (/home/wireguard/config) in this example.
  2. SERVERURL - This should be the publicly accessible IP of your server. You can leave it out and the image will try to infer it automatically.
  3. SERVERPORT - This is the port your Wireguard server will be listening at.
  4. PEERS - This is the number of peers that you want to connect to your server. The image will create configurations for each peer and dump out the QR codes that you can scan to obtain the configuration. Or if you have mapped the volumes, the configuration files will also be accessible at /home/wireguard/config/peerX/peerX.conf.
  5. INTERNAL_SUBNET - This is the subnet that the VPN will be set up on. This means that all peers will be accessible on this subnet.
  6. ALLOWEDIPS - This is the range of IPs that will be able to reach the VPN connection. You can set it to 0.0.0.0/0 to route ALL traffic through the VPN, but it’s better to only have a specific subnet routed through it.

Client Configuration

Now that the server is set up, it is time to connect some clients to the Wireguard server. It is very easy to do this now.

  1. Install wireguard on your client. If you are on Ubuntu, it can be done simply with apt-get install wireguard.
  2. Copy the configuration you obtained from the server and put it on the client at /etc/wireguard/wg0.conf. You can use any path for this, but I prefer to use the etc for these kinds of configuration files.
  3. Run wg-quick up /etc/wireguard/wg0.conf. You should now be connected to the Wireguard server through VPN.

Use a separate config for each peer that you want to connect and you have established a small secure VPN tunnel network between your devices.