Setting up a simple WireGuard tunnel
I've been spending a good amount of time lately learning about and experimenting with self-hosting and networking.
At some point I inevitably needed to figure out how to set up a secure way to connect to computers remotely without exposing them to the public Internet. A way to do this is by setting up a VPN tunnel between them, and for this purpose I decided to give WireGuard a try.
What I noticed right away when reading the quick start guide on the WireGuard
website is that the steps for setting up the network interface for the VPN were
a bit too manual. Since the server where I wanted to configure WireGuard runs
Debian, I decided to instantiate a Systemd service using the wg-quick@.service
template. In case you don't know what a Systemd template file is, the
systemd.unit(5)
man page explains it:
Unit names can be parameterized by a single argument called the "instance name". The unit is then constructed based on a "template file" which serves as the definition of multiple services or other units. A template unit must have a single "@" at the end of the unit name prefix (right before the type suffix). The name of the full unit is formed by inserting the instance name between "@" and the unit type suffix. In the unit file itself, the instance parameter may be referred to using "%i" and other specifiers.
Server configuration
First things first, we need to install the wireguard
package:
# apt install wireguard
Now we need to generate a key pair for the WireGuard node, and a configuration file for the tunnel interface. Since these files store sensitive information, make sure to set proper file permissions:
# umask 077
Since the wireguard
package created the /etc/wireguard
directory with proper
permissions for us, we can put the key files there:
# cd /etc/wireguard/
# wg genkey | tee privatekey | wg pubkey > publickey
We can now proceed to create the /etc/wireguard/wg0.conf
file. Make sure that
all these files are only readable by root
.
The configuration file format is explained in the wg(8)
man page:
The configuration file format is based on INI. There are two top level sections -- Interface and Peer. Multiple Peer sections may be specified, but only one Interface section may be specified.
And since we are going to start the interface with wg-quick
, we have a few
more options available. From wg-quick(8)
:
The configuration file adds a few extra configuration values to the format understood by wg(8) in order to configure additional attributes of an interface. It handles the values that it understands, and then it passes the remaining ones directly to wg(8) for further processing.
Let's start by setting up the Interface
section:
# Server: /etc/wireguard/wg0.conf
[Interface]
PrivateKey = <server private key>
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey
: This needs to be set to the content of the/etc/wireguard/privatekey
file that we created previously.Address
: The IP address for the interface. You can choose any IP range, but make sure that it doesn't conflict with other interfaces on any of the peers that you want to connect to the VPN. In this example we chose10.10.0.1/24
.ListenPort
: The port that WireGuard uses for UDP data.51820
is commonly used, but you can use any port. This value is optional, and if it's not specified, it's chosen randomly.
And that's it for the Interface
section.
Now we need to configure the peers. Peers need to have their own key pairs, and
we generate them using the same method that we used for the server by running
the wg genkey
and wg pubkey
commands. Once we have the keys for the peers,
we add a Peer
section for each peer to the wg0.conf
file on the server:
# Server: /etc/wireguard/wg0.conf
# [Interface]
# ...
[Peer]
PublicKey = <peer 1 public key>
AllowedIPs = 10.10.0.2/32
# [Peer]
# PublicKey = <peer 2 public key>
# AllowedIPs = 10.10.0.3/32
# ...
PublicKey
: The content of the file generated withwg pubkey
for the peer.AllowedIPs
: This is a list of IPs from which incoming traffic for this peer is allowed and to which outgoing traffic for this peer is directed. Since we are making a simple setup, we will only allow a single IP for each peer, and this IP will be in the range of the server's WireGuard interface.
Once we finish editing the wg0.conf
file, we are ready to start the WireGuard
interface. To do this, we start and enable a Systemd service for the wg0
interface:
# systemctl start wg-quick@wg0
# systemctl enable wg-quick@wg0
When the service is running, you can check the status of the WireGuard interface
and its peers by running the wg
command without arguments.
Client configuration
If the client machine runs Linux and Systemd, the steps to configure the
WireGuard interface should be mostly the same as what we did for the server. The
wg0.conf
for each peer should look similar to this:
# Client: /etc/wireguard/wg0.conf
[Interface]
PrivateKey = <client private key>
Address = 10.10.0.2/24
[Peer]
PublicKey = <server public key>
AllowedIPs = 10.10.0.1/32
Endpoint = <server endpoint>:51820
In this file, PrivateKey
is the private key of the client, which corresponds
to the public key set on the Peer
section of the server configuration file.
Note that Address
is set to the same IP as the one from AllowedIPs
on the
server's Peer
section (10.10.0.2
), but using the same CIDR mask as the
server interface (/24
).
Note that we are not setting ListenPort
for the client. This means that this
port will be set randomly.
We are also configuring a single Peer
section on the client where we set the
values of the server that we want to connect to:
PublicKey
: The public key that we generated in the/etc/wireguard/publickey
file on the server.AllowedIPs
: Here we put the IP address used by the WireGuard interface of the server.Endpoint
: This is the IP or hostname of the server, followed by a colon and the port number that we set on the server'sListenPort
.
There are many other ways to configure a Linux WireGuard peer. If you have
NetworkManager, it may be more convenient for some people to create a new
connection using nmtui
or the GUI tool provided by the desktop environment
that you are using. Other operating systems have their own ways to configure a
WireGuard connection.
Once you connect the client, you should be able to ping the server on
10.10.0.1
, and the information displayed on the output of wg
on either the
client or the server should update.
WireGuard is very flexible, and you can tweak the configuration on each peer to talk to each other directly. You can also create network configurations like having a server acting as a hub where peers connect to, and then this hub will be in charge of forwarding packets between the clients or other networks, but this requires extra configuration for IP forwarding and masquerading on the server.