Setting Up A Wireguard VPN

Motivation and concept

Wireguard is a piece of software that implements a communication protocol for building virtual private networks with the goal of being performant, being easy to use, and to have a low attack surface.

You can use wireguard to enable a couple of use cases.

The most common one is to increase the privacy of the internet traffic of your device by encripting and redirecting it through the server where the VPN is mounted.

This is specially useful when going to cibersecurity conferences and hanging out in rooms full of bored psychopathic hackers doing ARP injections all over the place in order to intercep whatever traffic goes through the network.

It is also useful to hide your navigation history from your internet provider, which in some places might be used to build a profile on you, as a way to fight against surveillance capitalism if you are against that kind of stuff.

The second and most useful use case for VPN technologies, but also the lesser known to non initiates, is the fact that you can hide a complete network of computer resources behind a single UDP port. This is specially useful to protect those resources against the violent break in attempts comming from the public facing internet.

The concept involves setting up servers that will only have open ports to the VPN interface, while using the VPN server as a hub for IP communications.

For example, you might use a jitsi server for videocalls and a owncloud instance for sharing documents and files in your organization. Another use case might be that you want to access your all powerful Ryzen Threadripper with 4 Nvidia GeForce RTX 4090 GPUs at home from your refurnished second hand thinkpad while you are on the road in order to play with the latest LLM. Or maybe you might want to commit your changes to a private Gitea or Gitlab server, a testing kubernetes cluster, etc…

Hopefully you get the idea.

These servers have an unique identity in the VPN, which consists of a public and private key pair and an IP.

Within the VPN, these services are available to other nodes as if they were on the same local network, which simplifes access control and configuration.

For a single individual, using a single VPS or Virtual Private Server on the cloud and the CLI inteface provided by the wireguard toolchain can be enough to connect your personal devices and a couple of self-hosted servers.

However, managing a VPN with multiple servers and users can easily become challenging as the number of identities increases and the server load begins to scale up due to the fact that the single VPN server begins to become the bottleneck for your infrastructure.

After a certain point, there are other solutions such as tailscale and firezone that are more appropriate to manage the infrastructure of a growing organization such as a business.

Remember that you want to keep your infrastructure as simple and self contained as possible, any additional resources that you do not really need implies additional costs and attack surfaces as well as mental entropy that you do not want to deal with. Embrace self-hosting minimalism.

On this article, I will document how to deploy this setup in the most simple way.

Setup

Operating System

Wireguard has been integrated inside the Linux kernel, so you can use the Linux distribution of your choice. However, you might want to consider an operating system highly focused on security such as OpenBSD or Alpine, given that this will be the keystone of your infrastructure and a really good target. In any case, you should choose the option you feel more comfortable with and learn how to harden it well in order to avoid surprises.

For this tutorial I will use Debian 12, given that is the most vanilla Linux distro that my VPS offers.

An important requirement is that the IP of the VPN server needs to be known by the clients, you can achieve this via a static IP or a dynamic DNS solution. For this tuturial we will asume that the server has a static IP for keeping it simple.

The VPN Server

In debian you can get access to all the wireguard tools by installing wireguard-tools from your favorite package manager.

sudo apt update
sudo apt upgrade -y
sudo apt install wireguard-tools

This will create a new directory on /etc/wireguard which is where your configuration files will live.

The steps to successfuly build your VPN network involve:

  1. Creating the private and public keys for the server and each of the nodes.
  2. Creating the configuration files for the server and each of the nodes.
  3. Distributing this configuration files to the VPN clients through a secure channel.

Wireguard by itself only solves the problem for the first two steps, you will need to figure out by yourself how to do the step three for your particular devices. A secure channel might be a QR code that is printed through an SSH session or a message securely encripted and authenticated through a public channel.

To generate the private and public keys you can use the following one liner.

wg genkey | tee private.key | wg pubkey > public.key

This will create two files called private.key and public.key containing your private and public keys respectively.

Remember, you need to keep the private.key private, otherwise you are missing the point regarding the security of your network.

You need a pair for each node that you want to connect to the network. For this particular example:

  • Private and public key for the server at /etc/wireguard/
  • Private and public key for node A at /etc/wireguard/nodes/A
  • Private and public key for node B at /etc/wireguard/nodes/B

In order to deploy wireguard you only need the configuration files for the server and the clients, so you can remove these key files after you are done.

The main structure for the configuration file in the server follows this template:

[Interface]

# IP for server in VPN network with the subnet defined.
#   This will allow us to connect up to 255 hosts on this network.
#   However, you might reach the traffic limits of this server before reaching that number.
Address = 10.10.10.1/24

# Internet facing UDP port for the VPN Server, usually 51820
ListenPort = 51820
PrivateKey = # private key for the server

# NAT configuration
PostUp = /etc/wireguard/postup.sh && sysctl -w net.ipv4.ip_forward=1
PostDown = /etc/wireguard/postdown.sh && sysctl -w net.ipv4.ip_forward=0

[Peer]
# Node A
PublicKey = # public key for A
# IP for A in VPN network
AllowedIPs = 10.10.10.1

[Peer]
# Node B
PublicKey = # public key for B
AllowedIPs = 10.10.10.2

Wireguard will only give access to the VPN network to those peers defined in this configuration file. If the peer fails the handshake, this will mean that they do not have the right private.key on their configuration files so they will be rejected.

You can find specific details on the protocol here if you are interested.

If you want to block repeated attacks to this port, you can probably use something such as fail2ban to detect failed attempts and block the origin IP in order to mitigate a posible degeneration of service. But this is beyond the scope of this tutorial and hopefully it will not be needed.

In order to configure the VPN server as a NAT device you will need to define additional firewall rules. The PostUp and PostDown instructions provide a way of executing additional code, usually bash scripts, in order to further customize your wireguard deployment.

When playing with firewall rules in any remote server be careful and make sure to keep the port for your SSH server available. If your machine becomes unreachable, you will either need physical access to it in order to reset these rules or use the console of your cloud provider to fix them.

Credits to Vivek Gite from cyberciti for guiding me and many others through the potential mess that is doing this with iptables.

/etc/wireguard/postup.sh

#!/bin/bash
IPT="/sbin/iptables"

IN_FACE="eth0"                   # NIC connected to the internet
WG_FACE="wg0"                    # WG NIC
SUB_NET="10.10.10.0/24"            # WG IPv4 sub/net aka CIDR
WG_PORT="51820"                  # WG udp port

## IPv4 ##
$IPT -t nat -I POSTROUTING 1 -s $SUB_NET -o $IN_FACE -j MASQUERADE
$IPT -I INPUT 1 -i $WG_FACE -j ACCEPT
$IPT -I FORWARD 1 -i $IN_FACE -o $WG_FACE -j ACCEPT
$IPT -I FORWARD 1 -i $WG_FACE -o $IN_FACE -j ACCEPT
$IPT -I INPUT 1 -i $IN_FACE -p udp --dport $WG_PORT -j ACCEPT

/etc/wireguard/postdown.sh

#!/bin/bash
IPT="/sbin/iptables"

IN_FACE="eth0"                   # NIC connected to the internet
WG_FACE="wg0"                    # WG NIC 
SUB_NET="10.10.10.0/24"            # WG IPv4 sub/net aka CIDR
WG_PORT="51820"                  # WG udp port

# IPv4 rules #
$IPT -t nat -D POSTROUTING -s $SUB_NET -o $IN_FACE -j MASQUERADE
$IPT -D INPUT -i $WG_FACE -j ACCEPT
$IPT -D FORWARD -i $IN_FACE -o $WG_FACE -j ACCEPT
$IPT -D FORWARD -i $WG_FACE -o $IN_FACE -j ACCEPT
$IPT -D INPUT -i $IN_FACE -p udp --dport $WG_PORT -j ACCEPT

Finally, you will want to set flag net.ipv4.ip_forward to 1 in the kernel in order to allow the transit of ip packets between network interfaces. This is required to setup the NAT between interfaces eth0 and wg0.

In order to harden the VPN server, you can set the permissions of the /etc/wireguard directory to be readable and writable only by root.

sudo chmod 600 /etc/wireguard

To launch the VPN, you can use wg-quick to automatically do the remaining configuration for you.

Startup

sudo wg-quick up wg0

Shutdown

sudo wg-quick down wg0

In order to automatically enter the VPN on startup, you can enable the service with systemctl by executing these commands when the VPN is down.

systemctl enable wg-quick@wg0.service
systemctl start wg-quick@wg0.service
systemctl status wg-quick@wg0.service

Now, if everything went well, you have the VPN server up and running.

You can check the status of the server through the command sudo wg show, which will various information about the server and the defined peers.

The VPN Client

For each VPN client, you need to define a configuration file such as the following one with the keys that you have previously generated.

[Interface]
PrivateKey = <private key for node X>
# The DNS server for your VPN network.
DNS = 1.1.1.1, 8.8.8.8
# The address must be unique for this client on
# the network defined by the server.
Address = 10.10.10.1/32

[Peer]
PublicKey =  # public key for the VPN server
AllowedIPs = 0.0.0.0/0

# Public facing static IP of your VPN server on
# interface eth0 and port for UDP
Endpoint = # vpn_server_ip:51820

Then you will need to distribute this file to the client.

Notice that you can setup your own DNS servers.

On this tutorial we will use public DNS servers to get the VPN traffic up and running, but you can potentially setup something like a pi-hole in order to block known advertiser addresses or set your own domain names for the network resources we want to deploy in the VPN network.

Linux VPN Client

To retrieve the configuration file into Linux, you can copy and paste from a SSH session in the server or you can use a tool such as scp to download it locally into /etc/wireguard. You will need to have wireguard installed on this client, wich can be done in the same manner as in the server depending on your particular distro. You can connect to the server either using an utility such as Network Manager or wg-quick as exposed in the previous section.

You can know your traffic is being redirected through the VPN by asking google your IP or getting it from the CLI with curl icanhazip.com, which should be the same as the one for your VPN server.

Mobile VPN Client

The main advantage of configuring a Mobile VPN client is that it has a camera and client configuration files can be shared via a qr code. You can generate QR codes from the CLI using the command qrencode in debian.

Obtain the qr code of a configuration file on the VPN server

sudo apt install qrencode
sudo qrencode -t ansiutf8 < /etc/wireguard/nodes/B/wg0.conf

In Android there is an official wireguard client as well as in iOS that have a feature to import configuration files from a QR code.

However, I have noticed in my personal use that battery life will be noticeably impacted when using a VPN, even if the wireguard protocol is significantly more perfomant that the existing alternatives and the fact that it is not a chatty protocol itself.

Closing thoughts

With this setup you can connect any number of clients until the resources of the VPN server are reached, either in terms of network bandwitdth or computation. This is a good solution for personal use or for small businesses that want to have ownership of their IT infrastructure.

However, the next question arises. You have your VPN network, but do you actually know what is going on in it? Are you able to maintain it properly?

These are the hard questions I hope to solve in the future articles. Stay tuned for more.

Sources

whoami

Jaime Romero is a software developer and cybersecurity expert operating in Western Europe.