Creating a Wireguard VPN on OpenBSD


According to the website, “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.“

While userland support for Wireguard has been in OpenBSD for quite some time, a native implementation that works out-of-the-box has just recently been committed. The following article explains how to set up a Wireguard VPN tunnel between two endpoints.

Before you read further

Please note that this article is about wireguard's native implementation, so you need to have a recent version of OpenBSD 6.7-current running. If you don't want to compile stuff on your own, you need at least a snapshot version as of Tue Jun 23 22:59:09 MDT 2020. Use sysctl kern.version to find out which snapshot is installed.

Roadwarrior Setup

The following section describes a roadwarrior setup, i.e. one or more roaming clients connect and establish a VPN tunnel to a static endpoint. A point-to-point setup is even easier to configure so it's left out on purpose.

The tunnel endpoint is called server in the examples, has the host name, the IP address is and the Wireguard Tunnel listens on Port 443 UDP. The port number can be arbitrarily chosen, however, Port 443 is seldomly firewalled on public networks like Hotel or public transport networks.

Create a wg interface and set a private key. Since this is the server's side, the port is statically set to 443.

server# ifconfig wg0 create wgkey $(openssl rand -base64 32) wgport 443

ifconfig shows now the public key (wgpubkey) that needs to be configured at client's side. Note that root permissions are needed to see the key, otherwise, ifconfig won't show it:

server# ifconfig wg0
wg0: flags=8082<BROADCAST,NOARP,MULTICAST> mtu 1420
    index 9 priority 0 llprio 3
    wgport 443
    wgpubkey RF8qxBg7HwWoeGvKzkSh3oV42TG32HT5gVV75k1UWiI=
    groups: wg

Repeat the same steps as above on the client, set a private key and get the public key that needs to be configured on the server:

client# ifconfig wg0 create wgkey $(openssl rand -base64 32)
client# ifconfig wg0
wg0: flags=8082<BROADCAST,NOARP,MULTICAST> mtu 1420
        index 13 priority 0 llprio 3
        wgpubkey bmUgcaOtlEFcIoSlwPQ3qO/c14nJHYr+a7Ms/kJmOFA=
        groups: wg

Now set the allowed IP addresses on the server and specify the client's public key. The allowed IP addresses indicate the IP addresses a peer is allowed to send from. That is, in order for an incoming packet from a peer to reach the host, the decrypted IP source address must be in the peer's allowed-ip ranges.

server# ifconfig wg0 wgpeer bmUgcaOtlEFcIoSlwPQ3qO/c14nJHYr+a7Ms/kJmOFA= wgaip
server# ifconfig wg0
wg0: flags=8082<BROADCAST,NOARP,MULTICAST> mtu 1420
        index 9 priority 0 llprio 3
        wgport 443
        wgpubkey RF8qxBg7HwWoeGvKzkSh3oV42TG32HT5gVV75k1UWiI=
        wgpeer bmUgcaOtlEFcIoSlwPQ3qO/c14nJHYr+a7Ms/kJmOFA=
                tx: 0, rx: 0
        groups: wg

As above, do the same on the client's side. Add the server's pubkey, specify the allowed IP addresses and also set the FQDN/IP address and port of your endpoint (here, Port 443). To send all traffic over the VPN tunnel, the allowed IP addresses are set to If that's not desired a narrower IP address range can be set here.

client# ifconfig wg0 wgpeer RF8qxBg7HwWoeGvKzkSh3oV42TG32HT5gVV75k1UWiI= wgendpoint 443 wgaip
client# ifconfig wg0
wg0: flags=8082<BROADCAST,NOARP,MULTICAST> mtu 1420
        index 13 priority 0 llprio 3
        wgpubkey bmUgcaOtlEFcIoSlwPQ3qO/c14nJHYr+a7Ms/kJmOFA=
        wgpeer RF8qxBg7HwWoeGvKzkSh3oV42TG32HT5gVV75k1UWiI=
                wgendpoint 443
                tx: 0, rx: 0
        groups: wg

Finally, the wg interfaces on both server and the client need an IP address. While this example uses IPv4 only, setting IPv6 addresses is also possible.

server# ifconfig wg0 up

client# ifconfig wg0 up

A ping can be used to test if the connection works as expected:

client$ ping -c 2 
PING ( 56 data bytes
64 bytes from icmp_seq=0 ttl=255 time=40.210 ms
64 bytes from icmp_seq=1 ttl=255 time=16.928 ms

Route Traffic over the Tunnel

The setup above is good to get an encrypted connection between peers, however, the roadwarriors cannot send their traffic over the tunnel, yet. To do so, the server needs to forward the traffic to the Internet (via NAT) and the client needs to route the traffic towards the tunnel.

On the server side, enable IPv4 forwarding the NAT the traffic via pf. Setting the sysctl to 1 enables it immediately and setting it in /etc/sysctl.conf will make the setting persistent over reboot:

server# sysctl net.inet.ip.forwarding=1

server# cat /etc/sysctl.conf

Now instruct pf to NAT the traffic. Add the following lines to /etc/pf.conf.

# Allow connection to UDP 443
pass in proto udp from any to any port 443 keep state
# NAT the traffic from the wg0 interface
match out on egress from (wg0:network) to any nat-to (egress:0)

Verify the config and then add it to pf.

server# pfctl -f /etc/pf.conf -n
server# pfctl -f /etc/pf.conf

On the client side two additional routes are needed:

  1. A more specific route to the server ( over the local default gateway (ww.xx.yy.zz)
  2. A default route towards the IP address of the tunnel endpoint

Set the rules as follows:

client# route add -priority 2 ww.xx.yy.zz
client# route add -priority 7 default

Start the VPN Tunnel on Boot

To start the Wireguard tunnel upon boot a hostname.if file must be created. Since both peers need to know each others public key, the secret key cannot generated with openssl as seen above. The key has to be created before and then manually added to the file. wgpeer contains the public key of the server's side so you need to create a secret key there before.

client# openssl rand -base64 32

client# cat /etc/hostname.wg0
wgkey k1yKSr6UxjUaz5jztdnxmq6OH7zvkYUfcuLPQ1dUKq8=
wgpeer fyTpRR6zhLXyebxkfGMEgEEORqLT74GWlkF+mT51qng= wgendpoint 443 wgaip
!route add -priority 2 ww.xx.yy.zz
!route add -priority 7 default
server# openssl rand -base64 32

server# cat /etc/hostname.wg0
wgkey 5/rge6grBrKv8M3ybM9Nqor82XeHaFfblZ/5Ue/qhfQ=
wgport 443
wgpeer zWjBPaEA/Qpzi6QQQauDeC6Ulk7fIPTX1EEcpvEDkxg= wgaip

Working with multiple Clients

Adding more roadwarriors to the server is as simple as adding another wgpeer. Follow the instructions above to set up another client. Once that is done, the new client's public key needs to be added to the server.

server# ifconfig wg0 wgpeer mi238gX8Jwcwm1kHyuuDleUF/h98ZoW/ORgIQZ7B9hk= wgaip

The number of configured peers can be seen in the ifconfig output:

server# ifconfig wg0
        index 16 priority 0 llprio 3
        wgport 443
        wgpubkey fyTpRR6zhLXyebxkfGMEgEEORqLT74GWlkF+mT51qng=
        wgpeer r1u7ITBMFdIBU8P2IByHIpS847LSznTy1PlG5tL560g=
                wgendpoint 12666
                tx: 135692, rx: 88500
                last handshake: 208 seconds ago
        wgpeer mi238gX8Jwcwm1kHyuuDleUF/h98ZoW/ORgIQZ7B9hk=
                wgendpoint 12766
                tx: 124, rx: 532
                last handshake: 1 seconds ago
        groups: wg
        inet netmask 0xffffff00 broadcast

Debug the Connection

In case things didn't work out, debugging can be enabled/disabled with:

# ifconfig wg0 debug
# ifconfig wg0 -debug

$Id:,v 1.3 2020/06/25 17:10:45 cvs Exp $