Setting up Wireguard

Set up your own VPN on your own server, in just about half an hour. This post describes the process of setting up a WireGuard VPN and client.

Setting up Wireguard

Yesterday, I set up a Wireguard VPN. I did find some scripts to automate the process, but since I like fine grained control over my system and would not like to install more packages or files than required, I decided to do things manually. Here's a quick summary of what I did, so you can follow suit.

First things first

If you know what you're doing, you can skip this part.

What we're going to do requires a server. You can get a server in many ways - there exist tons of VPS providers, for example Digital Ocean, Amazon AWS, Google Cloud, Hetzner, and so on. Pick one you like, and buy a server. You don't need a powerful server since Wireguard isn't exactly resource intensive, so whatever the cheapest plan is should do just fine.

The way this works is that all the traffic from your computer will be routed through the server and then reach its destination, and all the traffic back to your computer will first go through the server. This means that all the data exchange between the target server and your machine goes through your server - It's as if you were physically where your server is and browsing the internet through a monitor connected to your server.

This can be helpful in case your ISP has shady privacy policies, or if they block totally innocent websites like Telegram, DigitalOcean, or DuckDuckGo (yes, this actually happened to someone I know) and you want to get around it.

Installing Wireguard

You first need to install Wireguard on both the server and the client. If you're on Ubuntu, this is a simple process:

$ sudo apt install wireguard

If you're using Debian Bullseye, the command above works, since it's in the official repos. However, if you're using Debian Buster or below, you'll need to enable the backports repo this way:

$ sudo sh -c "echo 'deb http://deb.debian.org/debian buster-backports main' > /etc/apt/sources.list.d/buster-backports.list"

$ sudo apt update

and then install it using:

$ sudo apt install wireguard

In case the client machine is not using Linux, you can find Wireguard for Windows and macOS here. On most other Linux distros, you should be able to just install the wireguard package. If that doesn't apply for you or you have other questions, have a look at the official installation instructions.

Generating Keys

These keys are used for encryption and decryption of data. We'll need to generate a private and public key on both the server and the client machine, so let's do it now.

Note that if you get Permission denied while trying to cd into /etc/wireguard, that's because you need root permissions, you can become root using sudo -s.

# cd /etc/wireguard
# wg genkey | tee privatekey | wg pubkey > publickey
# chmod 600 privatekey publickey

The commands are simple - we first go to /etc/wireguard, use the wg genkey command to generate a private key, pipe the private key to wg pubkey to generate a public key, and then set their permissions to 600. This means that we can read or write to the file, but no other user except the superuser can. Make sure to run these commands on both the server and the client machine.

Once that's done, you now have two sets of keys - a public and private key on the server and a public and private key on the client.

Now, you need to find your network interface name, which you can do using:
$ ip route list default | awk '{print $5}'

Let's refer to

  • Server Public Key as SRV_PUB

  • Server Private Key as SRV_PRIV

  • Client Public Key as CLT_PUB

  • Client Private Key as CLT_PRIV

  • Network Interface Name as NET_INTF

from now on.

Creating the config file

On the server

Create the file /etc/wireguard/wg0.conf, and insert the following into it:

[Interface]
Address = 192.168.10.1/24
SaveConfig = true
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o <NET_INTF> -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o <NET_INTF> -j MASQUERADE; iptables -A FORWARD -o wg0 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o <NET_INTF> -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -o <NET_INTF> -j MASQUERADE; iptables -D FORWARD -o wg0 -j ACCEPT
ListenPort = 51820
PrivateKey = <SRV_PRIV>

[Peer]
PublicKey = <CLT_PUB>
AllowedIPs = 192.168.10.2/32

Replace <SRV_PRIV> and <CLT_PUB> with the server private key and the client public key respectively.

On the client

Create the file /etc/wireguard/wg0.conf, and insert the following into it:

[Interface]
PrivateKey = <CLT_PRIV>
Address = 192.168.10.2/24
 
[Peer]
PublicKey = <SRV_PUB>
AllowedIPs = 0.0.0.0/0
AllowedIPs = ::0/0
Endpoint = <SRV_IP>:51820
PersistentKeepalive = 25

Replace <CLT_PRIV>, <SRV_PUB>, and <NET_INTF> with the client private key, the server public key, and the network interface name respectively.

Replace <SRV_IP> with the public IP address of the server. If you don't know what this is, you can execute $ curl icanhazip.com on the server to get it.

Finishing up

Setting up UFW rule

First, you need to check if ufw is installed by running:

$ which ufw

If it says ufw not found, you don't need to do anything.

If not, you need to tell UFW to allow udp traffic to pass through the port 51820. To do this, execute:

$ sudo ufw allow 51820/udp

Firing it up

Server

To start Wireguard on the server, execute:
$ sudo systemctl start wg-quick@wg0

Verify that starting the service was successful using:
$ systemctl status wg-quick@wg0
the output should be something like:

[email protected] - WireGuard via wg-quick(8) for wg0
   Loaded: loaded (/lib/systemd/system/[email protected]; enabled; vendor preset: enabled)
   Active: active (exited) since Tue 2021-03-02 00:16:28 CET; 15h ago
   .....
   Process: 30685 ExecStart=/usr/bin/wg-quick up wg0 (code=exited, status=0/SUCCESS)

In case you use a different init system, you can run sudo wg-quick up wg0.

Client

To start Wireguard on the client, execute:

$ sudo wg-quick up wg0

Verify that you're connected to the VPN by executing:

$ sudo wg

The output should be something like:

interface: wg0
  public key: <CLT_PUB>
  private key: (hidden)
  listening port: 38520

peer: <SRV_PUB>
  endpoint: <SRV_IP>:51820
  allowed ips: 0.0.0.0/0
  latest handshake: 15 seconds ago
  transfer: 3.58 MiB received, 761.63 KiB sent
  persistent keepalive: every 25 seconds

Verify that you can access the internet by executing:
curl icanhazip.com -- the output should be the same as the external IP of your server.

If you can't access the internet but ping <SRV_IP> works while connected with Wireguard, you might need to add the following lines to /etc/sysctl.conf:

net.ipv6.conf.all.forwarding = 1
net.ipv4.ip_forward = 1

Now, to load the values, run $ sudo sysctl -p

Hopefully, everything should be working perfectly now. Have fun! :)