What They Don’t Tell You About Setting Up A WireGuard VPN

Greg Schafer
Jan 12 · 14 min read

WireGuard is a relatively new VPN implementation that was added to the Linux 5.6 kernel in 2020 and is faster and simpler than other popular VPN options like IPsec and OpenVPN.

We’ll walk through setting up an IPv4-only WireGuard VPN server on DigitalOcean, and I’ll highlight tips and tricks and educational asides that should help you build a deeper understanding and, ultimately, save you time compared to “just copy these code blocks” WireGuard tutorials.

Let’s get a server!

To set up a VPN, we need two computers that we want to connect. One of these is typically a desktop/laptop/phone in your possession. If you’re looking to remotely access company intranet sites and services, the other computer would be a server in an office or on a company cloud network. If you’re looking to remotely access your own home network, privately network with family/friends, or encrypt all of your internet traffic, then the other computer would be a personal server on a cloud provider like DigitalOcean or AWS.

Image for post
Image for post
VPN connectivity overview. CC BY-SA 4.0, Image attribution: https://en.wikipedia.org/wiki/File:VPN_overview-en.svg

For this walkthrough, we’ll use a new Ubuntu 20.04 server on DigitalOcean, though you could follow similar steps using any cloud provider. To create a new DigitalOcean server, follow their guide to creating a droplet. A “droplet” is the term DigitalOcean uses for a “server” or a “VM” or an “instance”.

VPCs and Private Networks

DigitalOcean servers are automatically created in a Virtual Private Cloud aka VPC (most cloud providers have VPC or private networking functionality), meaning they have an additional network interface (eth1 in addition to eth0) and an additional private IP address. All servers, databases, and load balancers created in the same VPC can communicate with each other via their private IP addresses, which is a boost to security because all inbound traffic from the public internet (on eth0) can be blocked with a firewall.

You can use your VPN server as a sort of bastion host to access other resources inside your VPC using their private IP addresses. That is, your VPN server can route traffic to any IP address in the VPC and all the servers in your VPC can accept traffic only to their private IP addresses (to eth1), which protects those servers and the services they run from all sorts of attacks. The server configuration section below will mention how to set up this sort of architecture.

How can I keep my VPN server up?

Given the importance of VPN uptime — especially if it serves as the only way to access important servers in a VPC or remote company network — it’s worth considering how to handle or avoid downtime. There is a range of options and tradeoffs to consider, ordered below in increasing complexity/effort:

Set up a WireGuard server

With your shiny new server running, let’s install and configure WireGuard. For non-Linux platforms, follow the WireGuard website’s instructions and links. For this walkthrough, I’ll show instructions for Ubuntu 20.04, starting with installing the wireguard package:

sudo apt update
sudo apt install wireguard

The wireguard package installs two binaries:

I encourage reading the manpages (man wg and man wg-quick), because they are concise, well-written, and contain a lot of information that is glossed over in most WireGuard tutorials!

To encrypt and decrypt packets, we need keys. 🔑

# Change to the root user
sudo -s
# Make sure files created after this point are accessible only to the root user
umask 077
# Generate keys in /etc/wireguard
cd /etc/wireguard
wg genkey | tee privatekey | wg pubkey > publickey

Now we have a private key (which only the server should possess and know about) and a public key (which should be shared to all VPN clients that will connect to this server).

Next, create a configuration file at /etc/wireguard/wg0.conf.

If we use wg-quick (spoiler: we will) to start/stop the VPN interface, it will create the interface with wg0 as the name. You can create other interface config files with other names, such as wg1.conf, my-company-vpn.conf, or us_east_1.conf. The wg-quick script will create interfaces with names that match the config filename (minus the .conf part), as long as the name fits the regex tested in /usr/bin/wg-quick.

Print out your private key with cat /etc/wireguard/privatekey and then add the following to the configuration file:

# /etc/wireguard/wg0.conf on the server
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
# Use your own private key, from /etc/wireguard/privatekey
PrivateKey = WCzcoJZaxurBVM/wO1ogMZgg5O5W12ON94p38ci+zG4=

We’ll add the public keys of clients that are allowed to connect to the VPN later, but the above is all you need to run the VPN server for now. Here’s what it means:

At this point, you can start the VPN!

# This will run a few commands with "ip" and "wg" to
# create the interface and configure it
wg-quick up wg0
# To see the WireGuard-specific details of the interface
wg
# To start the VPN on boot
systemctl enable wg-quick@wg0

Find more example commands for inspecting the interface at https://github.com/pirate/wireguard-docs#inspect.

Relaying traffic

Recall from above that Address = 10.0.0.1/24 means the server will relay traffic to peers in the subnet. That is, if you connect to the VPN and ping 10.0.0.14 (and a server exists on the VPN at that address), then your ping will go to the VPN server at 10.0.0.1 and be forwarded on to the machine at 10.0.0.14. However, this won't work without one additional piece of configuration: IP Forwarding.

To enable IP Forwarding, open /etc/sysctl.conf and uncomment or add the line:

net.ipv4.ip_forward=1

Then apply the settings by running:

sysctl -p

Now, the VPN server should be able to relay traffic to other VPN hosts. From my understanding, running ping 10.0.0.14 will follow the left-to-right path shown in the diagram below. The diagram doesn't show the ping response from Peer C to Peer A, but you can mentally reverse all the arrows to see what the returning response path would look like.

Image for post
Image for post
The path of network packets from a ping command on Peer A to the destination server, Peer C. The packets enter the VPN at Peer A and route to the VPN server (Peer B), which relays the packets to Peer C via the VPN.

Troubleshooting relayed traffic

There are many places where something could go wrong, especially when relaying traffic between multiple servers as in the diagram above. When network requests are failing, tcpdump is a great tool for finding the source of failures and misconfigurations. If you wanted a complete view of the flow in the diagram above, you could run the following tcpdump commands on each machine:

sudo tcpdump -nn -i wg0
sudo tcpdump -nn -i eth0 udp and port 51820

Just be aware that clocks on servers might be slightly out-of-sync, so comparing timestamps in tcpdump output between servers could be misleading!

If you’re debugging network packets on a machine with a display like your desktop or laptop, you can use Wireshark, which is a graphical, user-friendly alternative to tcpdump.

For more insight into WireGuard itself, you can enable debug logging by following the instructions at https://www.wireguard.com/quickstart/#debug-info and then running tail -f /var/log/syslog to see the log messages.

Relaying traffic to a VPC or the internet

In addition to using a VPN server to relay traffic between VPN clients, you can use a VPN server as a way to access servers in a VPC (on DigitalOcean or AWS, for example) that are firewalled off from the public internet. This approach requires no change in WireGuard configuration on the server, but you will need to enable masquerading so that responses on one network (e.g. the VPC) can be mapped to the requesting machine on the other network (e.g. the VPN). If you’re unfamiliar with masquerading, check out this brief explanation. Assuming your VPN server is connected to the VPC on its eth1 interface, you can enable masquerading on the VPN server with:

iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth1 -j MASQUERADE

Now, a VPN client such as your laptop should be able to ping servers in the VPC, as in the diagram below.

Image for post
Image for post
The path of network packets from a ping command on Peer A to the destination server, Peer C. The packets enter the VPN at Peer A and route to the VPN server (Peer B), which terminates the VPN connection and relays the packets to Peer C via the VPC.

If you want to relay traffic through the VPN server to the internet (in which case, the VPN server is often labeled a bounce server), enable masquerading on the public-internet-facing interface (e.g. eth0) of the VPN server:

iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE

Now, a VPN client such as your laptop can visit public internet sites via your VPN — if you’re on an unsecured coffeeshop wifi connection or you don’t trust your ISP, all they’ll see is an encrypted VPN connection.

Image for post
Image for post
The path of network packets from a ping command on Peer A to the destination server on the internet. The packets enter the VPN at Peer A and route to the VPN server (Peer B), which terminates the VPN connection and relays the packets over the public internet to the destination server.

Firewall rules

We’ve used iptables above for masquerading, but iptables is also important for managing the VPN server's firewall. You can use ufw instead, but learn and use iptables if you have the time — iptables is more foundational and powerful. Regardless of how you manage your firewall (I like this sort of approach), you'll need to:

The iptables commands for those changes are:

iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPTiptables -A FORWARD -i wg0 -j ACCEPT
iptables -A FORWARD -o wg0 -j ACCEPT

Many WireGuard tutorials suggest putting these iptables commands in the PostUp lines of the server WireGuard configuration, meaning the commands will be run when the wg0 interface is created. Be warned that, depending on how you manage your firewall, you may end up erasing these commands if you restart your firewall while the WireGuard interface is running, thereby making the VPN unreachable. Consider managing WireGuard firewall rules in the same place and with the same tool that you manage all your other firewall rules.

Set up a WireGuard client

Similar to the server setup, install WireGuard (follow the WireGuard website’s instructions and links for non-Linux platforms):

sudo apt update
sudo apt install wireguard

Generate keys, similar to server setup:

# Change to the root user
sudo -s
# Make sure files created after this point are accessible only to the root user
umask 077
# Generate keys in /etc/wireguard
cd /etc/wireguard
wg genkey | tee privatekey | wg pubkey > publickey

Next, create a configuration file at /etc/wireguard/wg0.conf with the following content:

# /etc/wireguard/wg0.conf on the client
[Interface]
# The address your computer will use on the VPN
Address = 10.0.0.8/32
# Load your privatekey from file
PostUp = wg set %i private-key /etc/wireguard/privatekey
# Also ping the vpn server to ensure the tunnel is initialized
PostUp = ping -c1 10.0.0.1
[Peer]
# VPN server's wireguard public key (USE YOURS!)
PublicKey = CcZHeaO08z55/x3FXdsSGmOQvZG32SvHlrwHnsWlGTs=
# Public IP address of your VPN server (USE YOURS!)
# Use the floating IP address if you created one for your VPN server
Endpoint = 123.123.123.123:51820
# 10.0.0.0/24 is the VPN subnet
AllowedIPs = 10.0.0.0/24
# To also accept and send traffic to a VPC subnet at 10.110.0.0/20
# AllowedIPs = 10.0.0.0/24,10.110.0.0/20
# To accept traffic from and send traffic to any IP address through the VPN
# AllowedIPs = 0.0.0.0/0
# To keep a connection open from the server to this client
# (Use if you're behind a NAT, e.g. on a home network, and
# want peers to be able to connect to you.)
# PersistentKeepalive = 25

There’s lots to talk about here!

Before starting the VPN on the client, the VPN server needs to be configured to allow connections from the client. Open /etc/wireguard/wg0.conf on the VPN server again and update the contents to match:

# /etc/wireguard/wg0.conf on the server
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
# Use your own private key, from /etc/wireguard/privatekey
PrivateKey = WCzcoJZaxurBVM/wO1ogMZgg5O5W12ON94p38ci+zG4=
[Peer]
# VPN client's public key
PublicKey = lIINA9aXWqLzbkApDsg3cpQ3m4LnPS0OXogSasNW5RY=
# VPN client's IP address in the VPN
AllowedIPs = 10.0.0.8/32

The added [Peer] section enables the VPN server to coordinate encryption keys with the client and validate that traffic from and to the client is allowed. To apply these changes, you can restart the WireGuard interface on the server:

wg-quick down wg0 && wg-quick up wg0

If you want to avoid disrupting or dropping active VPN connections, reload the config with:

wg syncconf wg0 <(wg-quick strip wg0)

At this point, you can start the VPN on the client!

# This will run a few commands with "ip" and "wg" to
# create the interface and configure it
wg-quick up wg0
# To see the WireGuard-specific details of the interface
wg

Connecting from a Chromebook

If you’re connecting to a WireGuard VPN from a Chromebook, I suggest using the official Android WireGuard app. My efforts to run WireGuard under crouton failed, because crouton uses a chroot, so I was stuck with the Chromebook’s old Linux kernel (4.19) and unable to add kernel modules or network interfaces from within crouton. Similarly, crostini doesn’t allow updating or using custom kernel modules, but it does provide a great way to SSH into VPN-accessible servers while the Android WireGuard app is active.

Connecting from other devices

If you want to connect to a VPN from devices where you don’t have root access, you can try installing a userspace implementation of WireGuard such as wireguard-go.

If you want to connect to a VPN from devices you don’t control (e.g. smart TVs, IoT sensors), look into setting up WireGuard on your router (e.g. instructions for OpenWRT), so you can route all those devices’ outbound traffic through a VPN.

Thanks for reading! Hopefully, I’ve saved you time by passing on some of the insights and tips that I learned while digging deeper into the many facets of setting up a WireGuard VPN. If you have any suggestions or corrections, please let me know or send us a tweet, and if you’re curious to learn more about how we improve perception sensors, visit us at Tangram Vision.

Corrections

References

Tangram Visions

The Official Blog for Tangram Vision

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store