DEV Community

Greg Schafer for Tangram Vision

Posted on • Updated on

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

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.

VPN Connectivity Overview

VPN connectivity overview. CC BY-SA 4.0, Image attribution: Creative Commons License

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:

  • Do nothing! If you set up a server on DigitalOcean, install and configure the VPN, and take no further actions, then your VPN will go down when the server does. It's not uncommon for DigitalOcean to migrate droplets between physical machines due to hardware issues, and the VPN will be unavailable if the migration can't be performed without downtime. If a more serious issue causes downtime (e.g. accidental rm -rf /, networking misconfiguration, or a successful attack), then you'll need to set up and configure a new server from scratch to bring your VPN back up. If you didn't save the VPN server's private key offline, you'll need to generate a new private key and reconfigure all VPN clients to be able to connect to the new VPN server.
  • Enable droplet backups. You can enable backups for an extra +20% of the droplet price, which will take weekly snapshots of the server. If the droplet ends up horribly broken or unresponsive, you can restore the latest backup and your VPN will be working again (in about 1 minute for a 1 GB droplet).
  • Set up manual failover. Set up the VPN server and take a snapshot, then restore the snapshot to a new droplet. Point a floating IP to one of the servers and use that IP address when connecting to the VPN. When the primary/active VPN server goes down for any reason, you can update the floating IP to point to the secondary/standby VPN server and your VPN will work again!
  • Set up automatic failover / high-availability. The next step up in sophistication is to either:
    • detect when the VPN server goes down and automatically switch (point a floating IP address) to a healthy standby using something like Pacemaker, or
    • put a UDP load balancer in front of multiple VPN servers, but... you might need some network trickery to allow multiple active VPN servers with the same IP address and you might also need sticky sessions, which breaks down for roaming clients without some protocol-level changes like Cloudflare made for WARP.

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
Enter fullscreen mode Exit fullscreen mode

The wireguard package installs two binaries:

  • wg — a tool for managing configuration of WireGuard interfaces
  • wg-quick — a convenience script for easily starting and stopping WireGuard interfaces

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
Enter fullscreen mode Exit fullscreen mode

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=
Enter fullscreen mode Exit fullscreen mode

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:

  • Address = 10.0.0.1/24 — The server will have an IP address in the VPN of 10.0.0.1. The /24 at the end of the IP address is a CIDR mask and means that the server will relay other traffic in the 10.0.0.1-10.0.0.254 range to peers in the VPN.
  • ListenPort = 51820 — The port that WireGuard will listen to for inbound UDP packets.
  • PrivateKey = ... — The private key of the VPN server, used for encryption/decryption.

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Then apply the settings by running:

sysctl -p
Enter fullscreen mode Exit fullscreen mode

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.

Network Packet Paths

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

Network Packets Path A

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
Enter fullscreen mode Exit fullscreen mode

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.

Network Packets Path B

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:

  • allow UDP traffic to the WireGuard ListenPort (51820 in the sample server config above)
  • allow traffic forwarded to or from the WireGuard interface wg0

The iptables commands for those changes are:

iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT

iptables -A FORWARD -i wg0 -j ACCEPT
iptables -A FORWARD -o wg0 -j ACCEPT
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

There's lots to talk about here!

  • Address = ... — Set the IP address of this client in the VPN. Packets sent to the VPN server with a destination of this address will be sent to whatever public IP address (endpoint) this client was last seen at.
  • PostUp = wg set %i private-key ... — Load the private key from the file after the wg0 interface is up. You can copy-paste the contents of the private key file into a PrivateKey line directly (as in the server config) if you prefer. I suggest not loading the private key via PostUp in the VPN server config however, because reloading the config (e.g. after adding a new client/peer) does not re-run PostUp commands, so the VPN will no longer know its private key and the VPN won't work as a result.
  • PostUp = ping -c1 10.0.0.1 — Ping the VPN server after the wg0 interface is up to test that the VPN connection was successful. If the ping fails, wg-quick will take the interface back down. In my testing, sending traffic from the VPN server to the client didn't work until something was sent from the client to the server — sending 1 ping packet to the server with PostUp does the trick.
  • [Peer] — There can be multiple peer sections in the config, one for each VPN peer you wish to connect directly to. Often, the VPN server will be the only peer in a client's config file. Lines under the [Peer] header define how and where the client will connect to the peer.
  • PublicKey = ... — The public key of the VPN server.
  • EndPoint = ... — The (usually publicly-accessible) IP address of your VPN server. This could be a floating IP address if you're using a cloud provider like DigitalOcean or AWS.
  • AllowedIPs = ... — For incoming packets from the VPN server, their source IP address must match the addresses or ranges in AllowedIPs. For outgoing packets, the AllowedIPs is the mapping that tells WireGuard what peer (specifically their public key and endpoint) should be used when encrypting and sending. The last example (AllowedIPs = 0.0.0.0/0) would enable WireGuard to send traffic destined for any IP address to the VPN server. With AllowedIPs = 0.0.0.0/0, wg-quick up will conveniently run ip route and ip rule commands to route all your traffic through the VPN (useful in the aforementioned unsecured coffeeshop wifi or malicious ISP scenarios). For more info on how AllowedIPs works, check out WireGuard's documentation.
  • PersistentKeepalive = 25 — Send a packet to the VPN server every 25 seconds, to ensure that the server can successfully route traffic to the client when the client doesn't have a public or stable IP address. Without this setting, the client can still send traffic to the VPN server and receive responses, but routers between the client and the server only keep their NAT/masquerade mapping for a few dozen seconds. After the mapping expires, the server won't be able to send anything to the client until the client sends something first. You typically won't enable this setting, unless you want to allow new connections from other devices on the VPN — for example, you would enable this on your home desktop if you wanted to connect to it from your laptop or phone while traveling.

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

wg syncconf wg0 <(wg-quick strip wg0)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

If you're setting up multiple VPNs or multiple VPN clients — or if you're interested in learning about infrastructure and configuration automation — check out the next tutorial I wrote: Exploring Ansible via Setting Up a WireGuard VPN.

Corrections

  • 2020-01-13: Previously, my explanation of what AllowedIPs does and how to route all traffic through the VPN was incomplete/misleading. Thanks to Chris Siebenmann on Twitter for catching that!

References

Latest comments (8)

Collapse
 
jdrch profile image
jdrch

What they don't tell you about WireGuard you can't resolve private hostnames over it. So, for example, if your workflow relies on a remote client having access to local hostnameA, you're SOL. OpenVPN supports that right out of the box, and that's why I've stuck with it instead.

Collapse
 
thirupathicys profile image
Thirupathi K

@greg

You have mentioned "UDP load balancer in front of multiple VPN servers, but... you might need some network trickery to allow multiple active VPN servers with the same IP address and you might also need sticky sessions"

Please help me out as, how to configure multiple wireguard vpn servers in active-active mode with the same configuration. I tried to establish the vpn connection between peers through multiple wireguard vpn servers in acitve-acitve mode by using UDP load balancer like keepalived.
Kindly let me know the network trickery to allow multiple active VPN servers with the same IP address

Collapse
 
iplayroblox87 profile image
Trisha Dua • Edited

we got such way. Thanks a lot thedigitalbin.com

Collapse
 
rafaone profile image
rafaone

thanks for the tutorial, I'm around into the blogs and web site to try to get help.
I have a Wireguard VPN on my VPS, using a generic tutorial config, i added some peers like my phone, and laptop and working good. When I enable the vpn on cellphone its show that my IP is same of my VPS server.
But I have raspberry pi that use pi hole, and when I enable the vpn on this device my DNS queries goes trough the VPS
and really I would like to access my raspberry but this device dont need to access the internet of wg0 only visible to another devices
I dont know how to configure my raspberry pi peers to connect on VPN but do not outgoing to internet

In other words I would like to access my raspberry but do not allow this peer to navigate to internet trough VPN.

Collapse
 
tomyo profile image
Tomas Hayes • Edited

Thanks! Clearly written, I just gave it a go and it works!
Question:

  • So, every time I want to add a client, I have to add an new [Peer] entry in the server's /etc/wireguard/wg0.conf file?
  • I tried sshing into the server with vpn ip address (when connected), and I get a me@10.0.0.1: Permission denied (publickey)., what's the problem here?

I mostly want the VPN to allow me to access devices with ssh.

Collapse
 
grschafer profile image
Greg Schafer
  1. That is my understanding, yes. Regardless of the flavor of public-key cryptography being used, one side of the communication needs a private key and the other side needs a public key. So, for any peer you want to send a message to, you need possession of their public key in advance. (Keep in mind that the VPN "server" is really just another peer and it can initiate connections to its peers.)
  2. Hard to say without more info! A few ideas and things to check:
    • You might try SSHing with extra verbosity (e.g. ssh -vvv me@10.0.0.1) to see what SSH keys your computer is offering.
    • Make sure the corresponding public key exists in ~/.ssh/authorized_keys on the server, for the user account you're SSHing into.
    • Look in /var/log/auth.log on the server and see what messages sshd is emitting when you try to log in.
    • Make sure permissions on that ~/.ssh folder on the server are correct (sudo chmod 0700 ~/.ssh && sudo chmod 0600 ~/.ssh/*). Same for the ~/.ssh folder on your local machine.
Collapse
 
catermelon profile image
Rachelle

You are the absolute best. Thank you so much for this tutorial and writeup. I've been working off and on for like a week to get the VPC thing working and, more importantly, understand what all the wierd arcane iptables magic was doing. Thanks again <3

Collapse
 
sohahm profile image
Sohail Ahmed

This is the best guide on Wireguard.