Install and configure wireguard


WireGuard is according to Wikipedia “a communication protocol and free and open-source software that implements encrypted virtual private networks”, and per the Wiregard page “extremely simple yet fast and modern”. I don’t have experience with any other VPNs so don’t know what they are like, but if done the right way I think Wireguard is quite easy to set up and get working.

It is however lacking a little when it comes to documentation. The commandline animations on their page I find a little annoying, especially the one that can’t be paused. Fortunately there are plenty of pages with details on how to easily set it up.

So why this page then? I want to have a go-to place for the config file as that is my preferred way of setting up wireguard, instead of having to use a bunch of somewhat obscure commands I never remember in order to configure the interface manually.

I will detail setups for Ubuntu Linux, FreeBSD and OpenBSD. Linux and FreeBSD are quite similar while the setup for OpenBSD is only slightly different.

Vultr has a good guide for setting up Wireguard on FreeBSD 14 in case you want other words on how to set up and configure it. The configuration file is pretty much the same independent of the platform, so it’s just the commands to start and stop the service that can be different.

To install, please refer to the wireguard.com/install page as they are most likely more up to date than what this page will be when it comes to installation for your particular platform.

Wireguard is already in the FreeBSD base system, and you have the wg command available if you want to set it up that way. By installing the package you will get the wg-quick tool as well and the startup script using it will be added. Wg-quick is available on all platforms I’ve tested, but for OpenBSD but I found it easier to just use wg and hostname configuration. The advantage to using wg-quick is that it will set up the virtual interface for you, but configuration files for wg-quick and wg aren’t 100% compatible.

On OpenBSD then it is necessary to add a /etc/hostname.wg0 file to configure the interface, with the benefit that it will also function as the startup script.

I find it easiest to execute the rest of the commands as root as the wireguard directory has, and should have restrictive permissions. If you’re following this for FreeBSD remember to be in /usr/local/etc.

# cd /etc/wireguard
# umask 077

The umask command makes sure the files we create will also have restrictive permissions, meaning read and write of them is only available to the current user, in other words root.

One basis of Wireguard is that it uses key-value pairs. You will be creating a private and a public key, and the ones we connect with will do the same. The private key will be kept secret, while the public key will be exchanged. We will also create a pre-shared key, and if using that it should be done for each party you connect with. The private and public keys are generated only once.

To create the keys, use the wg utility:

# wg genkey >private.key
# wg pubkey < private.key >public.key
# wg genpsk >pre-shared.key

The wg pubkey < private.key means that the public key is derived from the private key, as the private key is input to the command. Another way to write it would be

# cat private.key | wg pubkey >public.key

Or if you know your commandline-foo everything can be done in one line:

# wg genkey | tee private.key | wg pubkey > public.key

The pre-shared key is not strictly necessary, but it adds a symmetric encryption key to the mix making this a little more future-proof should there ever be quantum-computers powerful enough to crack the asymetric encryption being used. It can probably be assumed that state-actors are storing most if not all internet traffic, with the purpose of decrypting everything when and if said computers become available so I think it’s just good practice.

Now why doesn’t everyone just use symmetric encryption then? Because transporting that symmetric key safely is a non-trivial problem.

In any case, let’s get started setting up the configuration on our side and add the first peer, and then do the same on the peer to establish communication between the two.

I’ve created the following keys to be used in this example, which you should absolutely not use! I am not using them myself, I just wanted to show actual values so it’s easy to see what everything should look like.

Peer A (us):
PrivateKey: qOncWvsVv7hTlgD2d++5Sx1ULjTa7zeKHKbuTl1J6GI=
PublicKey: q9jepA++1o/7bi2wbQaBOG2KUE8/2cz0i29aiv0MXlA=

PreSharedKey: kgC1FhcRNbKj3w5ew33mF5WvIKwMLGY7f5iuy3PBclY=

Peer B (them):
PrivateKey: CCfMo0ytoDPCPqxNPWAVAUka+no+c2NwivtBoNd21EM=
PublicKey: lmWvq5Gv8ZyV+9ruZF9APjffGEu0uEDJ5kANdeX6UWY=

Get the private key you created earlier and create the following file in /etc/wireguard: wg0.conf

[Interface]
PrivateKey = qOncWvsVv7hTlgD2d++5Sx1ULjTa7zeKHKbuTl1J6GI=
Address = 10.0.0.1/32
ListenPort = 51820

This will set up a virtual interface called wg0, it specifies that when we send traffic through the tunnel we will be using ip address 10.0.0.1 and it specifies that we listen on UDP port 51820 for anyone that wants to connect to us. The listen port can be changed to anything you want, just remember to open the firewall accordingly as well as letting the clients know about it.

A small note here about the difference between Linux/FreeBSD and OpenBSD is that the wg-quick tool being used expects the netmask, the /32 in this example in the address. For OpenBSD or when just using wg, the netmask is not understood and will throw an error, so it should be removed.

Now this configuration in itself isn’t terribly useful as it won’t actually let anyone connect to us. To allow connections, we have to specify [Peer] segments with the peer’s configuration. This should contain their public key and a pre-shared key if we are using that. As mentioned previously you can and probably should have different pre-shared keys for each peer.

We also need to give our public key to them as they have to add a [Peer] section on their side for us. In addition we need to know their public ip if we are to esablish connections to them and/or they need to know our public ip if they are to connect to us.

Extend wg0.conf so it now looks like this:

[Interface]
PrivateKey = qOncWvsVv7hTlgD2d++5Sx1ULjTa7zeKHKbuTl1J6GI=
Address = 10.0.0.1/32
ListenPort = 51820

[Peer]
PublicKey = lmWvq5Gv8ZyV+9ruZF9APjffGEu0uEDJ5kANdeX6UWY=
PreSharedKey = kgC1FhcRNbKj3w5ew33mF5WvIKwMLGY7f5iuy3PBclY=
AllowedIPs = 10.0.0.2/32
EndPoint: 2.2.2.2:51820

This specifies that we only allow traffic from ip 10.0.0.2 through the tunnel for this peer, and that we can initiate a connection to it on public ip 2.2.2.2 on port 51280. As soon as we’ve connected to the peer they will know our public ip and can connect back if needed, but only as long as there has been a connection recently.

On the peer, their configuration should look like this:

[Interface]
PrivateKey = CCfMo0ytoDPCPqxNPWAVAUka+no+c2NwivtBoNd21EM=
Address = 10.0.0.2/32
ListenPort = 51820

[Peer]
PublicKey = q9jepA++1o/7bi2wbQaBOG2KUE8/2cz0i29aiv0MXlA=
PreSharedKey = kgC1FhcRNbKj3w5ew33mF5WvIKwMLGY7f5iuy3PBclY=
AllowedIPs = 10.0.0.1/32
EndPoint: 1.1.1.1:51820

To start everything on Linux/FreeBSD, execute this command as root:

# wg-quick up wg0

You should now be able to see a new wg0 interface with command ip a, and you should see the configured peers with command wg. On peer A, us:

# wg
interface: wg0
  public key: q9jepA++1o/7bi2wbQaBOG2KUE8/2cz0i29aiv0MXlA=
  private key: (hidden)
  listening port: 51820

peer: lmWvq5Gv8ZyV+9ruZF9APjffGEu0uEDJ5kANdeX6UWY=
  preshared key: (hidden)
  endpoint: 2.2.2.2:51820
  allowed ips: 10.0.0.2/32
  latest handshake: 2 hours, 7 minutes, 37 seconds ago
  transfer: 2.10 MiB received, 2.34 MiB sent

On peer B, them:

# wg
interface: wg0
  public key: lmWvq5Gv8ZyV+9ruZF9APjffGEu0uEDJ5kANdeX6UWY=
  private key: (hidden)
  listening port: 51820

peer: q9jepA++1o/7bi2wbQaBOG2KUE8/2cz0i29aiv0MXlA=
  preshared key: (hidden)
  endpoint: 1.1.1.1:51820
  allowed ips: 10.0.0.1/32
  latest handshake: 2 hours, 4 minutes, 45 seconds ago
  transfer: 2.34 MiB received, 2.10 MiB sent

Then to bring it down again:

# wg-quick down wg0

Notice that you might have to allow traffic on the wg0 interface through the firewall of your system. Then you should be able to ping the other side using the private address, either 10.0.0.1 or 10.0.0.2 and see “latest handshake” and “traffic” update with the wg command.

For OpenBSD, to complete the setup, create the file /etc/hostname.wg0 and add ip configration to it:

inet 10.0.0.1 255.255.255.0 NONE
up

!/usr/local/bin/wg setconf wg0 /etc/wireguard/wg0.conf

Then start wireguard with sh /etc/netstart wg0. This file will also ensure that wireguard starts at boot.

The setup so far only allows reaching services on the peers, as defined by the firewall rules. If traffic should be routed through the peer and onto the Internet, the operating system needs to allow forwarding packets. This is configured with sysctl:

Linux:

# sysctl net.ipv4.ip_forward=1
# sysctl net.ipv6.conf.all.forwarding=1

To make this permanent, edit /etc/sysctl.conf and uncomment the lines that looks like the ones above, but without the sysctl command first. If there are no such lines, simply add them:

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1

FreeBSD and OpenBSD are similar:

# sysctl net.inet.ip.forwarding=1
# sysctl net.inet6.ip6.forwarding=1

Add the lines to /etc/sysctl.conf to make this permanent:

net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1

It is important to note that there should be no spaces here. In other words, net.inet.ip.forwarding = 1 won’t work.

You might also have to configure the firewall to allow forwarding packets and also do NAT for ipv4. That is out of scope for this document, but I might get back to it later.

To start wireguard with systemd and enable start at boot on Ubuntu Linux:

$ sudo systemctl enable wg-quick@wg0
$ sudo systemctl start wg-quick@wg0

On FreeBSD:

$ sudo sysrc wireguard_interfaces="wg0"
$ sudo sysrc wireguard_enable="YES"
$ sudo service wireguard start

To stop the service use the same scripts with stop instead of start.

On OpenBSD use the ifconfig command:

# ifconfig wg0 destroy

To add more peers to the network, simply add more [Peer] blocks like so:

[Interface]
PrivateKey = qOncWvsVv7hTlgD2d++5Sx1ULjTa7zeKHKbuTl1J6GI=
Address = 10.0.0.1/32
ListenPort = 51820

[Peer]
PublicKey = lmWvq5Gv8ZyV+9ruZF9APjffGEu0uEDJ5kANdeX6UWY=
PreSharedKey = kgC1FhcRNbKj3w5ew33mF5WvIKwMLGY7f5iuy3PBclY=
AllowedIPs = 10.0.0.2/32
EndPoint: 2.2.2.2:51820

[Peer]
PublicKey = 6D8qMzetUQH7dOUZeuPXYj/wdXiA4nQswaQMCXA3GXU=
PreSharedKey = KEYgEdBieJG4UwTQpz3xrElTHvoWSH/Xi2ox7XnfJxg=
AllowedIPs = 10.0.0.3/32

[Peer]
PublicKey = fzECqbQdzi1ce6DYjJiNISct9NW5n5nn7WyIrYqjvwM=
PreSharedKey = Jt2uGv80J+z5jCa0W7Js+s3pkzYM+gmTNiGm8nkB8r8=
AllowedIPs = 10.0.0.4/32
EndPoint: 3.3.3.3:51820

Then use the scripts to restart/reload the configuration.

On OpenBSD the configuration can be added from disk to the running service with wg:

# wg syncconf wg0 /etc/wireguard/wg0.conf

That should be it for now. Hope it was helpful!