kurtcms.org

Thinking. Writing. Philosophising.

Email Github LinkedIn

IP Networking: Fast and Secure VPN with WireGuard

Posted on January 18, 2021 — 9 Minutes Read

WireGuard despite its being 4-year young is widely acclaimed for its simple configuration and user-friendliness, its performance over the alternatives such as IPsec and OpenVPN, and its relatively lean 4,000-line work-of-art code base which is merely 1% of that of IPsec and OpenVPN, that translates directly to less room for software bug and security vulnerability. WireGuard as such was quickly incorporated in the Linux and the FreeBSD kernel and was rapidly adapted by a number of commercial VPN service vendors.

Security is also a major consideration for WireGuard with ChaCha20 selected for symmetric encryption and Poly1305 for authentication, the pair of which Google and Cloudflare have been adapting for some time now for Transport Layer Security. BLAKE2s is employed for cryptographic hashing for its performance over the likes such as MD5, SHA-1, SHA-2, and SHA-3. Handshaking is based on the Noise protocol framework that minimises the round-trip delay before the transmission of encrypted messages. The rest of the security details are listed on their website, together with results from various formal verifications covering all aspects of security. For its simplicity, performance advantage and tightened security, it is the excellent tunnelling protocol for a virtual private network (VPN), to proxy web traffic on your server for the clients connected to it.

Installing WireGuard

First, let’s install the WireGuard Linux module and the associated tools on the server and the client.

$ apt install wireguard

Configuring WireGuard

Next is configuration.

Private and Public Keys

A set of private and public keys for each of the WireGuard server and client will need to be generated. A good practice is to keep a copy of the keys in the WireGuard directory under /etc i.e. the host-specific system configuration hierarchy

$ cd /etc/wireguard && umask 077; wg genkey | tee privatekey | wg pubkey > publickey

The private key is of course private whereas the public key is needed for the WireGuard host on the other side of the VPN to verify that the instance on this side is the intended instance for the tunnelling.

Print and take note of the keys on your command line interface before proceeding.

$ cat /etc/wireguard/privatekey /etc/wireguard/publickey

Private IP Addresses

One private IP address for each of the WireGuard server and client in the same subnet will also need to be selected, the private IP addresses of 192.168.0.1/24 and 192.168.0.10/24 under the subnet of 192.168.0.0/24 will be used for the server and client respectively in this writing.

DNS Forwarding

In addition to being assigned a private IP, the WireGuard server will need to be able to accept incoming DNS query from the connected WireGuard client and forward the query to a valid DNS server. This requires the Berkeley Internet Name Domain (BIND) package installed on your WireGuard server.

$ apt install bind9 bind9utils bind9-doc

The WireGuard server will be configured to accept incoming DNS query from only the connected WireGuard client. This may be done by building an access control list in the BIND configuration that includes only the IP addresses of your WireGuard private IP subnet. Google and Cloudflare are excellent DNS servers to forward the DNS query from your WireGuard clients to once it is received. The rest of the the configuration is rather straightforward.

$ nano /etc/bind/named.conf.options

These should be added to the config file.

acl trusted {
  localhost;
  192.168.0.0/24;
};

options {
  directory "/var/cache/bind";
  recursion yes;
  allow-query { trusted; };

  forwarders {
    8.8.8.8;
    1.1.1.1;
  };
  forward only;

  dnssec-validation yes;
  auth-nxdomain no;
};

Control + x to exit, and y and enter to save.

Restart bind9 for the new setting to take effect.

$ service bind9 restart

Routing

Once the WireGuard VPN is established both the server and the client will have one logical network interface added to the list of interfaces that are already present, and both of them will need to know what type of traffic should be sent to this new WireGuard network path. For starter if the network traffic is destined to the assigned private IP address of the WireGuard host on the other side of the VPN, then it should be sent to the WireGuard network interface for the subsequent tunnelling, and this holds true for both the server and the client. In addition to this, for the purpose of leveraging the server as a web proxy for the client, all traffic from the client to the internet should also be sent to the server through the WireGuard network interface for its subsequent processing.

The routing table is a data table that consists of a number of defined IP subnets and a list of network interfaces that traffic destined to these defined subnets should be sent to. These are either automatically learnt with a dynamic routing protocol such as Open Shortest Path First (OSPF) or manually set with static route. WireGuard will manipulate this routing table once the VPN is established to ensure the IP subnet reachable through the remote host will be routed accordingly. It does this by referencing the AllowedIPs parameter in its configuration that lists the IP subnets available through the remote host. For the purpose of leveraging the server as a web proxy for the client, the WireGuard client should send all traffic represented by a network subnet of 0.0.0.0/0 to the WireGuard server whereas the WireGuard server should send traffic destined to 192.168.0.10/32, that is the private IP address of the WireGuard client, to the WireGuard client.

Network Address Translation (NAT)

Once the VPN is established and the routing set, the WireGuard client will begin sending all network traffic to the WireGuard server for its subsequent routing. Internet traffic for example will be sent from the WireGuard client to the WireGuard server and then off to the internet. For the return traffic to know the way back to the WireGuard server before being forwarded subsequently to the WireGuard client through the WireGuard tunnel, a rule in the routing table at the WireGuard server will need to be added to translate the source IP address of any outgoing network traffic originating from the WireGuard client, to the public IP address of the internet-facing network interface of the WireGuard server. This IP address conversion is called Network Address Translation (NAT) and it allows a great many of network devices to connect to the internet on a single shared public IP address.

The WireGuard server will as such be configured to add the said NAT rule to its routing table on the internet-facing network interface, which is eth0 in this writing, once the VPN is established and to remove the rule as soon as the VPN disconnects.

IP Forwarding

IP forwarding needs to be enabled on the WireGuard server as well to allow the connected WireGuard client to access the internet through it. This requires modifying a runtime kernel parameter stored in the sysctl config file.

Edit the sysctl.conf file with the nano text editor

$ nano /etc/sysctl.conf

Locate the net.ipv4.ip_forward parameter by control + w and type in the parameter name. Then change its value from 0 to 1.

net.ipv4.ip_forward = 1

Control + x to exit, and y and enter to save.

Once the config is saved, load the new setting with the sysctl command.

$ sysctl -p

IP forwarding should be enabled with the new setting. You can verify this by invoking the iptables command and have it list all of the rules under the forward chain in the routing table.

$ iptables --list FORWARD

An output similar to the below will be printed.

Chain FORWARD (policy ACCEPT)
target prot opt source destination 
ACCEPT all -- anywhere anywhere

Uncomplicated Firewall (UFW)

With Uncomplicated Firewall (UFW) installed and enabled by default on some Linux distros, for the WireGuard server and clients to establish a VPN on the default UDP port 51820, and to allow the WireGuard server to accept and subsequently forward DNS query on UDP port 53 with TCP as fallback, these protocol and ports will need to be allowed on the ufw. The config, as its name suggests, is rather straightforward.

For the WireGuard server.

$ ufw allow 51820/udp && ufw allow 53

For the WireGuard client.

$ ufw allow 51820/udp

Calling the ufw command again with the status argument will list all of the firewall rules in effect and an output similar to the below will be printed if all goes well.

Status: active

To Action From
-- ------ ----
51820/udp ALLOW Anywhere 
53 ALLOW Anywhere 
51820/udp (v6) ALLOW Anywhere (v6) 
53 (v6) ALLOW Anywhere (v6)

WireGuard Configurations

The public key, along with the private IP assignment and DNS server IP address, as well as the routing and the NAT config mentioned above, will all need to be edited into the WireGuard configuration.

$ nano /etc/wireguard/wg0.conf

For the WireGuard server.

[Interface]
PrivateKey = private-key-of-the-server
Address = 192.168.0.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
PublicKey = public-key-of-the-client
AllowedIPs = 192.168.0.10/32

For the WireGuard client.

[Interface]
PrivateKey = private-key-of-the-client
Address = 192.168.0.10/24
ListenPort = 51820
DNS = 192.168.0.1

[Peer]
PublicKey = public-key-of-the-server
AllowedIPs = 0.0.0.0/0
Endpoint = public-ip-address-of-the-server

Control + x to exit, and y and enter to save.

Enabling WireGuard

With the config built and set, the WireGuard interface may now be enabled by calling the wg-quick script that was installed alongside the WireGuard Linux module with an argument of up on the wg0 interface on both the WireGuard server and the client.

$ wg-quick up wg0

The wg command will list the WireGuard interface in operation.

$ wg

If all goes well, an output similar to the below will be printed.

For the WireGuard server.

interface: wg0
  public key: public-key-of-the-server
  private key: (hidden)
  listening port: 51820

peer: public-key-of-the-client
  endpoint: ip-address-of-the-client:51820
  allowed ips: 192.168.0.10/32
  latest handshake: 10 seconds ago
  transfer: 340 B received, 580 B sent

peer: public-key-of-the-client
allowed ips: 192.168.0.10/32

For the WireGuard client.

interface: wg0
  public key: public-key-of-the-client
  private key: (hidden)
  listening port: 51820

peer: public-key-of-the-server
  endpoint: ip-address-of-the-server:51820
  allowed ips: 0.0.0.0/0
  latest handshake: 10 seconds ago
  transfer: 580 B received, 340 B sent

Thoughts

The are countless applications for connecting your hosts in a VPN and manipulating traffic between them. With a secure, performant and simple VPN protocol such as WireGuard, more time and resources can be reserved for creating new and awesome use cases instead of configuring the troubleshooting the network.