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:

Common Use Cases

VPNs are commonly used for:

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:

  1. Add the official Wireguard repository to your sources list.

    sudo add-apt-repository ppa:wireguard/wireguard
    
  2. Install Wireguard.

    sudo apt install wireguard
    
  3. 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
    
  4. 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
  5. 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:

  1. Install the Wireguard userspace tools:

    sudo pacman -S wireguard-tools
    
  2. 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
    
  3. 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
    
  4. Optionally, generate a pre-shared key that will add additional security.

    wg genpsk > preshared
    chmod 600 preshared
    
  5. 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 to 172.16.0.0/16, then all IPs in the range 172.16.0.0 to 172.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.

  1. 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.

  2. Start the Wireguard server.

    sudo systemctl enable [email protected]
    sudo systemctl start [email protected]
    
  3. 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.

  1. Start the Wireguard Client.

    sudo systemctl start [email protected]
    
  2. 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
    
  3. 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:

  1. Enable packet forwarding using the following command:

    sysctl net.ipv4.ip_forward=1
    
  2. 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
  3. Configure Wireguard to setup the firewall for forwarding when a client connects by adding the PostUp and PostDown 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
  4. Restart the Wireguard server, or reboot the server.

    sudo systemctl restart [email protected]
    

The Server is now ready for routing Internet traffic. We must now tell the client to forward all Internet traffic through the VPN.

  1. Open /etc/wireguard/wg0.conf and under the [Peer] section, change the AllowedIPs value to 0.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
  2. Restart the Wireguard client.

    sudo systemctl restart [email protected]
    
  3. 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.

  1. The Address and DNS directives under the Interface section in /etc/wireguard/wg0.conf are extensions used by wg-quick. Since we are not using wg-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
  2. 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
  3. Run the script.

    chmod +x mkvpn.sh
    ./mkvpn.sh
    
  4. 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) "[email protected]"; }

    In a new terminal session you can now launch an application that will exclusively use the VPN as so:

    vpnify firefox
    
  5. 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 with vpnify 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!