Setting up a Wireguard VPN
Wireguard is a fast and modern Virtual Private Network (VPN) tunnel for Linux and other operating systems. This guide will describe the setup of a Wireguard server on a VPS, and how to connect to it from a PC.
Background
When two devices on a network (local, or otherwise) communicate with each other, the messages they send back and forth are split into small discrete chunks of data known as packets. These packets are sent by the sending device and routed through the network till they eventually reach the receiving device. The receiving device puts the packets back together to reconstruct the sent message.
Messages usually have structure to them. For example, when you loaded this page, the browser on your device sent a “GET” message to my server, and the server responded with the contents of this page. Your browser may send many different types of messages to the server, and it’ll respond accordingly. This is known as a protocol: a defined set of requests a device may make, and how the receiver will respond to them.
Unless the protocol encrypts the messages before sending, the data in these messages are readable by all the intermediary devices routing the packets that form the messages. This is not a problem if the two devices are on the same private home network, but over the Internet this is a real security concern. This is where VPN may help.
A VPN allows you to securely route the packets from one device (or network) to another via an encrypted channel over an insecure network such as the public Internet. Before a packet is sent, the VPN software on the sender will take the original packet, encrypt the contents, and wrap it so that the VPN software on the receiver will be able to decrypt it. This is known as tunneling.
VPN tunnels can exist between between:
- Peer-to-Peer – securely connect two devices to each other
- Peer-to-Network – securely connect a device to another network
- Network-to-Network – securely join two networks together
Common Use Cases
VPNs are commonly used for:
- Securely connecting two devices over the Internet
- Securing open or “Free WiFi” access points
- Bypassing censorship and geo-blocking restrictions by tunneling your Internet traffic to a server in another country and accessing the Internet from within that country rather than yours
Introducing Wireguard
Wireguard is a fast and modern VPN that was originally built for Linux. Since then, it has been ported to support other operating systems. Unlike other VPN solutions, Wireguard is simple to setup and supports modern use cases such as roaming between Cellular and WiFi networks without interruption.
Goals
This guide will focus on the Peer-to-Peer and Peer-to-Network use cases mentioned above as they’re the most useful for the average person.
We will start by setting up a secure tunnel between a server hosted on an Ubuntu-based VPS, and connect to it via an Arch Linux client. Then we will extend that to support tunneling all internet traffic on the client through the server. Then we will describe how to use the VPN for only certain applications using network namespaces.
Setup
Preliminary Server Setup
On the server, presumably over SSH:
Add the official Wireguard repository to your sources list.
sudo add-apt-repository ppa:wireguard/wireguard
Install Wireguard.
sudo apt install wireguard
Generate the public and private keys for the server. The private key must never be shared. The public key can be safely be shared.
wg genkey | tee privatekey | wg pubkey > publickey
The previous command generates two files: privatekey, and publickey, which contain the private and public keys respectively. Secure the privatekey by changing its permissions.
chmod 600 privatekey
Create the file
/etc/wireguard/wg0.conf
as root, add the following contents to it, changing where necessary, and save the file.1 2 3 4 5
[Interface] Address = 172.16.0.1/16 ListenPort = 51820 SaveConfig = true PrivateKey = <Contents of server's privatekey file>
An explanation of each configuration directive is provided below:
- Address – The address and subnet of the VPN server. The VPN server will be assigned the specified address,
and all clients must be given IPs within this subnet. Any private subnet is useable, however,
the private
/16
subnet was chosen since it is not commonly found on home networks. - ListenPort – The port the Wireguard server will use for connections
- SaveConfig – Updates this configuration when peers are added via the command line
- Address – The address and subnet of the VPN server. The VPN server will be assigned the specified address,
and all clients must be given IPs within this subnet. Any private subnet is useable, however,
the private
Configure the firewall to allow Wireguard (and SSH if not previously allowed).
sudo ufw allow 22/tcp sudo ufw allow 51820/udp sudo ufw enable
Client Setup
The client is assumed to be an Arch Linux machine. If the client is also Ubuntu, follow the first two steps from the Server Setup and skip to step 3:
Install the Wireguard userspace tools:
sudo pacman -S wireguard-tools
Select, and install one Wireguard kernel module specific your kernel:
sudo pacman -S wireguard-arch # For linux kernel sudo pacman -S wireguard-lts # For linux-lts kernel sudo pacman -S wireguard-dkms # For other kernels
Generate the public and private keys for the client. Like the server, the privatekey should never be shared.
wg genkey | tee privatekey | wg pubkey > publickey chmod 600 privatekey
Optionally, generate a pre-shared key that will add additional security.
wg genpsk > preshared chmod 600 preshared
Like the server, create the file
/etc/wireguard/wg0.conf
as root, add the following contents to it, changing where necessary.1 2 3 4 5 6 7 8 9 10
[Interface] Address = 172.16.0.2/16 PrivateKey = <Contents of client's privatekey> DNS = 172.16.0.1 [Peer] PublicKey = <Contents of server's publickey file> PresharedKey = <Contents of client's preshared file> AllowedIPs = 172.16.0.1/32 Endpoint = <Server IP or Domain>:51820
In this file:
- Address – Assigns a static IP for the client on the VPN network adapter. This IP must be unique and part of the subnet specified by the Address directive in the server’s configuration file.
- DNS – The IP address of the DNS server to use, we will proxy DNS to the VPN server to prevent DNS leaks.
- AllowedIPs – The IP address(es) that will be routed through the VPN. In this case, we only want to talk to
the server itself, so only the server’s IP address,
172.16.0.1
with the/32
subnet, is specified. Routing entire subnets, or all IPs is also possible by using the proper IP and subnet. For example, if Address is set to172.16.0.0/16
, then all IPs in the range172.16.0.0
to172.16.255.255
will be routed through the VPN, useful if you want multiple devices on the same VPN to be able to talk to each other.
Final Server Configuration
We must now add a Peer section to the server’s configuration.
Once again, open
/etc/wireguard/wg0.conf
as root, and add the following section to the configuration.1 2 3 4
[Peer] PublicKey = <Contents of clients's publickey file> PresharedKey= <Contents of client's preshared file> AllowedIPs = 172.16.0.2/32
In this file, AllowedIPs indicates the expected IP address of the client. The IP address must be the same as the one specified in the Address directive in the client’s
/etc/wireguard/wg0.conf
. However, the subnet is/32
here since we are specifying a single address. Much like the client’s AllowedIPs directive, you may specify an entire subnet if you do not want to lock the client to single IP address.Start the Wireguard server.
sudo systemctl enable [email protected] sudo systemctl start [email protected]
Check the server is running by running
sudo wg show
, the output should appear similiar to this:interface: wg0 public key: <Server's publickey> private key: (hidden) listening port: 51820 peer: <Client's publickey> preshared key: (hidden) allowed ips: 172.16.0.2/32
Connecting the Client
The client can now connect to the server.
Start the Wireguard Client.
sudo systemctl start [email protected]
Check the client is running by running
sudo wg show
, the output should appear similiar to this:interface: wg0 public key: <Client publickey> private key: (hidden) listening port: 57819 peer: <Server publickey> preshared key: (hidden) endpoint: <Server IP>:51820 allowed ips: 172.16.0.1/16 latest handshake: 0 minute, 5 seconds ago transfer: 0.0 MiB received, 0.0 MiB sent
Test the connection by pinging the server.
ping 172.16.0.1
If you run the command from step 2 again, the
transfer
amounts should have increased.
Next Steps
Now that we have a working tunnel to the server, we can make it more useful by extending our previous configurations.
Routing all Internet Traffic over the VPN
Routing all Internet traffic over the VPN is by far one of the most useful applications of a VPN. To accomplish this, we’ll have tell the server to act as a router. Make the following changes on the server:
Enable packet forwarding using the following command:
sysctl net.ipv4.ip_forward=1
Packet forwarding will only be enabled until the server is rebooted, to enable it permanently, open
/etc/sysctl.d/99-sysctl.conf
and uncomment the following line:1
net.ipv4.ip_forward=1
Configure Wireguard to setup the firewall for forwarding when a client connects by adding the
PostUp
andPostDown
directives shown below to/etc/wireguard/wg0.conf
under the[Interface]
section.Important:
en0
must be replaced by your server’s WAN interface.1 2 3 4 5 6 7 8 9 10 11 12 13 14
[Interface] Address = 172.16.0.1/16 ListenPort = 51820 SaveConfig = true PrivateKey = <Contents of server's privatekey file> # Add the next two directives. Change en0 to your server's WAN interface. PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o en0 -j MASQUERADE PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o en0 -j MASQUERADE [Peer] PublicKey = <Contents of clients's publickey file> PresharedKey= <Contents of client's preshared file> AllowedIPs = 172.16.0.2/32
Restart the Wireguard server, or reboot the server.
sudo systemctl restart wg-quick@wg0
The Server is now ready for routing Internet traffic. We must now tell the client to forward all Internet traffic through the VPN.
Open
/etc/wireguard/wg0.conf
and under the[Peer]
section, change theAllowedIPs
value to0.0.0.0/0
. Your configuration should look similar to the example below.1 2 3 4 5 6 7 8 9 10
[Interface] Address = 172.16.0.2/16 PrivateKey = <Contents of client's privatekey> DNS = 172.16.0.1 [Peer] PublicKey = <Contents of server's publickey file> PresharedKey = <Contents of client's preshared file> AllowedIPs = 0.0.0.0/0 Endpoint = <Server IP or Domain>:51820
Restart the Wireguard client.
sudo systemctl restart wg-quick@wg0
All your Internet traffic should now be routed through the server. Verify this by checking your public IP address. The IP should be that of your server.
Using the VPN for Some Applications Only
Sometimes, it’s useful to only have certain applications using a VPN. For example, you want to use your normal Internet connection for everything except a single browser instance to bypass a geo-block. This can be accomplished on Linux by using a network namespace.
Simply put, a network namespace is a parallel networking stack. Normally, all network interfaces, routes, etc. live in the default network namespace. By default, applications access the network through this default namespace. However, we can create a new network namespace, one that will solely contain the Wireguard network interface. If we then launch an application within that new network namespace, it will only be able to see the Wireguard network interface, and thus be forced to access the Internet through the VPN.
Unfortunately, the wg-quick
tool we’ve been using to start our client does not recognize namespaces at the moment, so
we will have to create a script to create the namespace and setup the Wireguard network interface.
The
Address
andDNS
directives under theInterface
section in/etc/wireguard/wg0.conf
are extensions used bywg-quick
. Since we are not usingwg-quick
these directives must be deleted or commented out.1 2 3 4 5 6 7 8 9 10
[Interface] # Address = 172.16.0.2/16 PrivateKey = <Contents of client's privatekey> # DNS = 172.16.0.1 [Peer] PublicKey = <Contents of server's publickey file> PresharedKey = <Contents of client's preshared file> AllowedIPs = 0.0.0.0/0 Endpoint = <Server IP or Domain>:51820
Create the script
mkvpn.sh
with the contents below.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
#!/bin/bash NETNS_NAME="vpn" DEV_NAME="wg0" # Create a Wireguard network interface in the default namespace. sudo ip link add $DEV_NAME type wireguard # Load the Wireguard configuration. sudo wg setconf $DEV_NAME /etc/wireguard/$DEV_NAME.conf # Create a new network namespace. sudo ip netns add $NETNS_NAME # Move the Wireguard interface to the network namespace. sudo ip link set $DEV_NAME netns $NETNS_NAME # Set the IP address of the Wireguard interface. sudo ip -n $NETNS_NAME addr add 172.16.0.2/32 dev $DEV_NAME # Bring up the Wireguard interface. sudo ip -n $NETNS_NAME link set $DEV_NAME up # Make the Wireguard interface the default route. sudo ip -n $NETNS_NAME route add default dev $DEV_NAME
Run the script.
chmod +x mkvpn.sh ./mkvpn.sh
At this point we have created a new network namespace, and the Wireguard network interface is up and running within it. However, any application you normally launch will use the default network namespace and bypass the VPN altogether. In fact, any application running in the default namespace can’t even see the Wireguard interface! To actually see and use the VPN we must launch an application in the network namespace. This is usually done using the
ip
command:sudo ip netns exec vpn <command>
This, however, is sub-par. First it runs the command as root, and second, it’s cumbersome. We can create a wrapper that launches an application as you in the network namespace. Append the following to your
~/.bashrc
or~/.zshrc
.1
vpnify() { sudo -E ip netns exec vpn sudo -E -u \#$(id -u) -g \#$(id -g) "$@"; }
In a new terminal session you can now launch an application that will exclusively use the VPN as so:
vpnify firefox
Check your public IP address by launching your favourite browser normally and with the
vpnify
command. When launched normally, your public IP should be your home or office IP, but when launched withvpnify
it should be your server’s public IP.
In a future article, we will convert vpnify
into a proper script that automatically brings up and tears down the
network namespace and VPN on-demand.
Conclusion
Wireguard is a modern, fast, and simple to setup VPN. In this article you learned how to route secure traffic between two devices securely, how to route all internet traffic to another network, and how to use a VPN for certain applications only.
While this tutorial was longer than I expected, the actual setup was relatively straightforward and we didn’t do anything too cryptic or arcane. Coming from an OpenVPN, or worse, IPSec setup, Wireguard really is a breath of fresh-air.
I hope you found this tutorial helpful and learned something new!