OpenBSD Router : VPN

Make your own VPN with WireGuard on an OpenBSD router

Motivation

VPNs allow a device to connect to a private network from afar. For example, one could travel to a remote location yet still be able to act as if connected to the home LAN. As a secondary benefit, internet traffic can be tunnelled through the VPN to hide it from untrusted networks such as public WiFi.

IPSec and OpenVPN are the most well-known VPN protocols, but are difficult to understand and complex to configure. OpenSSH can provide some of the necessary features at the cost of convenience and performance.

WireGuard is a new VPN protocol intended to be simple, secure and performant. It has a small code-base, uses only strong cryptographic algorithms, and provides perfect forward secrecy.

This guide will demonstrate how to run WireGuard on an already functioning OpenBSD home router to let clients access the home network remotely.

Concepts

Networking

  1. Each device in the network is assigned a key pair (a public and private key) and an internal IP address

  2. Outbound traffic is encrypted with the target host’s public key and encapsulated in UDP before being transmitted

  3. Inbound encrypted traffic is decrypted with the receiving host’s private key

Topology

  1. Each host is configured with information about its peers: the public keys and the IP address ranges that they can route

  2. For tunnelling all internet traffic through one host (the VPN “server”), allow that device to route all addresses

  3. VPN clients need to know the public IP address of the server to initiate a connection

Server

Networking

In this example, the 10.0.0.0/24 subnet is used for the VPN. The home router will be assigned 10.0.0.1 and will be the server through which traffic is tunnelled.

On the router, create a WireGuard interface with the chosen private IP address. The wg utility is installed later – it sets the public and private key pairs.

/etc/hostname.wg0
inet 10.0.0.1 255.255.255.0
!/usr/local/bin/wg setconf wg0 /etc/wireguard/server.conf

Firewall

WireGuard already authenticates packets so you may not need any filtering.

set skip on { lo wg }

Allow connections from clients; open the port on which WireGuard will listen.

pass in quick on egress proto udp to port 51820

Traffic going out to the internet from the VPN will need NAT, but if you have a match rule performing NAT on !(egress:network), then this is already done.

DNS

If you run a local unbound DNS resolver and wish for VPN clients to be able to make use of it, add an access-control directive with the subnet of the VPN.

/var/unbound/etc/unbound.conf
server:
  […]
  access-control: 192.168.1.0/24 allow
  access-control: 192.168.2.0/24 allow
  access-control: 10.0.0.1/24 allow
[…]

WireGuard

Install the wireguard-tools package.

# pkg_add wireguard-tools

Each device in the VPN needs a key pair.

Create private keys by running wg genkey; the public key is derived from the private key by piping it to wg pubkey.

$ wg genkey
sEw/H2BjShZovIn5FeOun/sgjMsxl6hzBzEDvqIYrUk=
$ wg pubkey <<END
sEw/H2BjShZovIn5FeOun/sgjMsxl6hzBzEDvqIYrUk=
END
GhBU6Sqss+s/ZqMuJhVM1RBIDdG5YQ9bK0EwcZNxU2Q=

Once the keys are generated, create a configuration file.

/etc/wireguard/server.conf
[Interface]
PrivateKey = «server private key»
ListenPort = 51820

[Peer]
PublicKey = «client-1 public key»
AllowedIPs = 10.0.0.2/32

[Peer]
PublicKey = «client-2 public key»
AllowedIPs = 10.0.0.3/32

Verify

Load the wg0 interface or reboot.

# sh /etc/netstart wg0

Now run wg without any arguments (which is equivalent to the wg show all command) to print a list of interfaces and their attributes, including the public keys and allowed IPs of the peers. If the configuration has loaded correctly, configure the clients.

Clients

Mobile (Android & iOS)

Install the WireGuard application on your device and add a new connection. Set the private key and internal IP address to match the appropriate Peer section from the server configuration, and provide details of the public key and public IP address (or domain name) of the server – the syntax is of the wg-quick(8) configuration file format.

The allowed IPs of the server will be all addresses (0.0.0.0/0, ::/0) so that all traffic is tunnelled. If you only wish to access particular subnets in your home LAN, add the relevant addresses instead.

Setting the DNS parameter to the internal IP address of your router tells the mobile client to use the resolver that you presumably have running – useful if you perform DNS-based ad blocking. You may of course use a different resolver if you desire or if you don’t run a resolver.

client.conf
[Interface]
PrivateKey = «client-1 private key»
Address = 10.0.0.2/32
DNS = 192.168.1.1

[Peer]
PublicKey = «server public key»
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = «server public IP or FQDN»:51820

If you create the configuration file on an OpenBSD system, an easy way to transfer it to the mobile applications is by generating then scanning a QR code

# pkg_add libqrencode
$ qrencode -t ansiutf8 < client.conf

OpenBSD

The wg-quick script handles creating the network interface and adding routes. No manual networking steps are needed – just create a configuration file and call the script.

/etc/wireguard/client.conf
[Interface]
PrivateKey = «client-2 private key»
Address = 10.0.0.3

[Peer]
PublicKey = «server public key»
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = «server public IP or FQDN»:51820
# pkg_add wireguard-tools
# wg-quick up client

Remember to adjust the firewall to allow traffic on WireGuard interfaces.

Done

Connect to the VPN. Check that you can ping the router on its VPN internal address, access other hosts on the LAN, and that your external IP appears as that of your home network.

Other Guides

Before WireGuard was included in the kernel (in OpenBSD 6.8), you could use a userspace daemon.