WireGuard on OpenBSD


Earlier this week I imported a port for WireGuard into the OpenBSD ports tree. At the moment we have the userland daemon and the tools available. The in-kernel implementation is only available for Linux. At the time of writing there are packages available for -current.

Jason A. Donenfeld (WireGuard author) has worked to support OpenBSD in WireGuard and as such his post on ports@ last year got me interested in WireGuard, since then others have toyed with WireGuard on OpenBSD before and as such I've used Ted's article as a reference. Note however that some of the options mentioned there are no longer valid. Also, I'll be using two OpenBSD peers here.

The setup will be as follows: two OpenBSD peers, of which we'll dub wg1 the server and wg2 the client. The WireGuard service on wg1 is listening on 100.64.4.3:51820.

Within the VPN subnet the nodes will use the following addresses:

  • wg1: 10.0.0.1
  • wg2: 10.0.0.2

On both nodes we'll use the tun2 device to tunnel WireGuard traffic.

Setup

First we need to install the required packages on both nodes:

pkg_add wireguard-go wireguard-tools

Currently this installs wireguard-go-0.0.20190409 and wireguard-tools-0.0.20190406.

wg1 (server)

On the "server" node we'll generate the key pairs for both nodes:

wg genkey | tee server-private.key | wg pubkey > server-public.key
wg genkey | tee client-private.key | wg pubkey > client-public.key

In order for the server to forward packets we need the to allow it via sysctl:

sysctl net.inet.ip.forwarding=1

And depending on your exact firewall configuration, you can get away with adding something like the following to pf.conf of the server node to NAT the traffic from the tunnel:

pass out on egress inet from (tun2:network) nat-to (egress:0)

You may also need to allow incoming traffic to 51820/udp (or whicever port you choose for ListenPort).

Now create the tunnel interface:

ifconfig tun2 up 10.0.0.1 10.0.0.2 netmask 255.255.255.0
rcctl enable wireguard_go
rcctl set wireguard_go flags tun2
rcctl start wireguard_go

Finally we'll need the actual configuration in which we declare the private key for the node, and the public key of its peer. server.conf:

[Interface]
PrivateKey = sEw/H2BjShZovIn5FeOun/sgjMsxl6hzBzEDvqIYrUk=
ListenPort = 51820

[Peer]
PublicKey = jCmA1Kvq/0/uhi1+uX4OLWIoctqhiyMfJ4DTqbWDWWs=
AllowedIPs = 10.0.0.2/32

Obviously you must use the keys you generated previously instead of re-using these :-)

Now apply the configuration to the tun2 interface as such:

wg setconf tun2 server.conf

You can run wg to ensure it's applied correctly.

wg2 (client)

For wg2 the required setup is:

ifconfig tun2 up 10.0.0.2 10.0.0.1 netmask 255.255.255.0
rcctl enable wireguard_go
rcctl set wireguard_go flags tun2
rcctl start wireguard_go

And the client.conf configuration is basically the same as for wg1, except that we omit the ListenPort (so the client will use a random port to listen on) and we set the Endpoint to match the IP/port on which we can reach wg1:

[Interface]
PrivateKey = oNqEAeMDpnCVVU+lz/G0zEAR3/OlssGg87/Hruy5WVg=

[Peer]
PublicKey = GhBU6Sqss+s/ZqMuJhVM1RBIDdG5YQ9bK0EwcZNxU2Q=
AllowedIPs = 0.0.0.0/0
Endpoint = 100.64.3.2:51820

Apply the configuration:

wg setconf tun2 client.conf

And now the WireGuard tunnel has been established, try pinging the other end of the tunnel to verify and run wg:

# wg
interface: tun2
  public key: jCmA1Kvq/0/uhi1+uX4OLWIoctqhiyMfJ4DTqbWDWWs=
  private key: (hidden)
  listening port: 21373

peer: GhBU6Sqss+s/ZqMuJhVM1RBIDdG5YQ9bK0EwcZNxU2Q=
  endpoint: 100.64.3.2:51820
  allowed ips: 0.0.0.0/0
  latest handshake: 22 minutes, 17 seconds ago
  transfer: 2.54 KiB received, 3.25 KiB sent

Routing

What we can now do is to route all traffic over the WireGuard tunnel. For this to work we'll first need to add a more specific route to our server on the client:

route add -priority 2 100.64.3.2 $IP_OF_DEFAULT_GATEWAY

Finally we add the default route to the other endpoint of the tunnel using a lower priority to ensure we don't try to route traffic to the endpoint itself over the tunnel which is still needed to go to the address listed at Endpoint.

route add -priority 7 default 10.0.0.1

Priority 7 is lower than the default of 8. When routing all traffic via WireGuard you may need to adjust the AllowedIPs field for the peer as also traffic originating from its non-tunnel IP will be routed over the tunnel and the following message will be displayed by the server:

IPv4 packet with disallowed source address from peer

Conclusion

WireGuard (cl)aims to be easier to setup and faster than OpenVPN and while I haven't been able to verify the latter, the first is certainly true...once you've figured it out. Most documentation out there is for Linux so I had to figure out the wireguard_go service and the tun parameters. But all in all, sure, it's easier. Especially the client configuration on iOS which I didn't cover here because it's essentially pkg_add libqrencode ; cat client.conf | qrencode -t ansiutf8, scan the code with the WireGuard app and you're good to go. What is particularly neat is that WireGuard on iOS supports Always-on.