Picture of the author
Jarred Kenny
Published on

Secure Private Networking with Wireguard

Authors

I've seen a huge amount of hype online in the last few months about Wireguard.

WireGuard® is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPsec, while avoiding the massive headache. It intends to be considerably more performant than OpenVPN.

I've mostly skimmed the Wireguard articles and blog posts I've come across without ever actually taking it for a spin. However, I've recently deployed an entire stack of self-hosted services on Digital Ocean and at the same time have been building up the services I have running on my home server for serving media and handling home automation. For management and backup purposes I decided it would be beneficial for both of these hosts to share a private network so that they could connect to each other as if they were both on my home lan. In the past I would have created an IPSec tunnel between the hosts and given them a route to each other using that tunnel.

But, Wireguard!

Wireguard aims to be as simple to deploy as SSH and will allow us to accomplish this same peer to peer network over an encrypted tunnel. For the purpose of this guide I'll be referring to servers as HostA and HostB, they will be peers in our Wireguard network.

Before we begin:

  • Wireguard requires support from the Linux kernel. If your system is running kernel 5.6 or newer it has Wireguard support. If your kernel version is 5.5 or lower, there is likely a Wireguard module available in your distributions repositories.
  • Wireguard tools available from the wireguard-tools package in most Linux distributions.

1. Generate Keypairs

Each host in our WIreguard network needs a unique key pair which will identify the node and allow connections from the other nodes in our network.

As our network has two peers, we will generate two keypairs with the following using the wg command line utility:

umask 077
wg genkey | tee hosta_private_key | wg pubkey > hosta_public_key
wg genkey | tee hostb_private_key | wg pubkey > hostb_public_key

This will generate a private_key and a public_key file for each of our hosts. The contents of these files will be used in our WIreguard configuration and will allow for our peers to authenticate with one another.

2. Configure HostA

On HostA, create a file called /etc/wireguard/wg0.conf and add the following content:

[Interface]
Address = 10.0.0.1/24
SaveConfig = true
PrivateKey = <hosta_private_key>
ListenPort = 51820

[Peer]
PublicKey = <hostb_public_key>
AllowedIPs = 10.0.0.2/32

Address specifies the IP address of the host on the Wireguard network. For simplicity, HostA will be 10.0.0.1 and HostB will be 10.0.0.2 in our configuration. The addresses of each host can be modified as desired.

Be sure to replace <hosta_private_key> and <hostb_public_key> with the keys we generated in the step 1.

The name of the configuration file (wg0) is used as the of the WIreguard interface on your system. You can rename the configuration file if you desire a different interface name.

AllowedIPs in the [Peer] block specifies from what IP addresses (in the Wireguard network space) are allowed to connect to HostA.

3. Configure HostB

Create a file called /etc/wireguard/wg0.conf on HostB with the following content:

[Interface]
Address = 10.0.0.2/24p
PrivateKey = <hostb_private_key>

[Peer]
PublicKey = <hosta_public_key>
Endpoint = <hosta_ip_address>:51820
AllowedIPs = 10.0.0.1/32

Be sure to replace <hostb_private_key> and <hosta_public_key> with their respective keys from step 1.

<hosta_ip_address> should also be replaced with the public IP address of HostA so that the initial connection to the host can be made to establish and support our tunnel.

AllowedIPs once again specifies what range of IP addresses can be routed to over this interface. 10.0.0.1/32 allows traffic to HostA only. However, if you wanted all interface traffic to be routed via the Wireguard tunnel so that HostA acted as a VPN server you could set AllowedIPs to 0.0.0.0/0 which in CIDR notation simply means "all traffic".

4. Activate wg0 interfaces on both hosts

On HostA:

wg-quick up wg0

On HostB:

wg-quick up wg0

If all went well both hosts now have a wg0 network interface which can communicate with the other host over our secure tunnel.

hosta > ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=1.70 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=1.84 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=1.53 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=1.41 ms

If you wish to make the tunnel permanent so it returns after rebooting either host, you can enable the wg-quick systemd target which Wireguard provides. To do so, run this on both hosts:

systemctl enable wg-quick@wg0

What's next?

We covered how to setup a simple point to point Wireguard tunnel that allows 2 hosts to communicate securely as if they shared a local network. However, Wireguard can be used for much more than simple point to point networking. With only a few more lines in our configuration on either host we could have had HostA serve as a VPN server which securely routed all public traffic from HostB or vice versa.

In fact, running a Wireguard tunnel which connects your laptop to a remote server and routes all traffic is probably the most common use case for Wireguard. Because plenty of guides already exist on using Wireguard as a complete VPN server, I decided not to cover it here. If you want to use Wireguard in that manner Linode has an excellent guide.