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.
As of June 2020 support for WireGuard has been committed to the kernel as wg(4) along with support in ifconfig(8). Please see these two posts on the WireGuard mailinglist on how to set it up or how migrate from a setup as described below: setup and migrate from Linux.
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.1wg2
: 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.