One great thing about OpenBSD is that it comes with many services included. Wireguard is one of them, meaning it can be set up without installing anything.

Here is my initial and quite basic setup of Wireguard on OpenBSD without using anything other than what is included in the base system. I already feel I will have to get back to this and improve the setup for maintainability, but for a first setup it works and that is good enough right now.

I normally use doas to execute commands as root, but for this setup it’s easier to just su to root and execute them directly.

First off I set a strict umask to make sure everything is created with permissions for root only, then create the directory that will hold the keys and configuration, and finally create our private key as well as a preshared key for our first peer:

umask 077
mkdir /etc/wireguard
openssl rand -base64 32 > /etc/wireguard/wg0.private
openssl rand -base64 32 > /etc/wireguard/peer-a-wg0.preshared

Then I create the /etc/hostname.wg0 file which creates the interface, sets up the listening port, adds peers and adds our IP for this interface and network:

# Set our private key
wgkey `cat /etc/wireguard/$1.private`

# Set the listen port
wgport 51820

# Add peers
!/etc/wireguard/add-peers.sh $1

# Add our IP
inet 10.0.0.1 255.255.255.0

The add-peers script looks like this:

#!/bin/sh

INTERFACE=$1

PEER="peer-a"
PUBKEY=$(cat /etc/wireguard/${PEER}-${INTERFACE}.pubkey)
PRESHARED=$(cat /etc/wireguard/${PEER}-${INTERFACE}.preshared)
ifconfig ${INTERFACE} wgpeer ${PUBKEY} \
        wgdescr ${PEER} \
        wgpsk ${PRESHARED} \
        wgendpoint peerIP peerPORT \
        wgaip "10.0.0.10/32" \

This is very simple and probably a little naive. If I were to add another peer I would have to duplicate everything from PEER and down, which sounds like something a loop of some kind should solve.

For this to work I will also need to get the public key from the peer and put it in its respective file, as well as giving them my public key and the preshared key that is unique to our connection.

I am using preshared keys to add another layer of symmetric-key encryption to the connection, to make it more secure against future quantum computers.

Given this preshared key needs to be kept secret, how do I transport it safely from my server to the peer or vice versa? If someone is recording my internet traffic and storing it in order to crack current encryption when quantum computers become powerful enough, what then?

It seems OpenSSH is ahead of the curve here and implemented a fix for this already in April 2022 with the release of version 9.0:

 * ssh(1), sshd(8): use the hybrid Streamlined NTRU Prime + x25519 key
   exchange method by default ("sntrup761x25519-sha512@openssh.com").
   The NTRU algorithm is believed to resist attacks enabled by future
   quantum computers and is paired with the X25519 ECDH key exchange
   (the previous default) as a backstop against any weaknesses in
   NTRU Prime that may be discovered in the future. The combination
   ensures that the hybrid exchange offers at least as good security
   as the status quo.

   We are making this change now (i.e. ahead of cryptographically-
   relevant quantum computers) to prevent "capture now, decrypt
   later" attacks where an adversary who can record and store SSH
   session ciphertext would be able to decrypt it once a sufficiently
   advanced quantum computer is available.

I’ve made changes to my ssh config so only sntrup (or now in 2025, mlkem) is used, so it should be safe to transfer the preshared keys with ssh. I’ll write about that later.

Now then to start my interface: sh /etc/netstart wg0

When the same has been done on the peer, I can try to connect:

ping 10.0.0.10
PING 10.0.0.10 (10.0.0.10): 56 data bytes
64 bytes from 10.0.0.10: icmp_seq=0 ttl=64 time=12.413 ms
64 bytes from 10.0.0.10: icmp_seq=1 ttl=64 time=12.507 ms
64 bytes from 10.0.0.10: icmp_seq=2 ttl=64 time=12.304 ms
64 bytes from 10.0.0.10: icmp_seq=3 ttl=64 time=12.294 ms
64 bytes from 10.0.0.10: icmp_seq=4 ttl=64 time=13.072 ms
64 bytes from 10.0.0.10: icmp_seq=5 ttl=64 time=12.660 ms
^C
--- 10.0.0.10 ping statistics ---
6 packets transmitted, 6 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 12.294/12.542/13.072/0.268 ms

Success!

ifconfig wg0 will give more information when run as root:

wg0: flags=80c3<UP,BROADCAST,RUNNING,NOARP,MULTICAST> mtu 1420
        index 9 priority 0 llprio 3
        wgport 51820
        wgpubkey <redacted>
        wgpeer <redacted>
                wgdescr: peer-a
                wgpsk (present)
                wgendpoint <peerIP> <peerPORT>
                tx: 11880, rx: 10184
                last handshake: 23 seconds ago
                wgaip 10.0.0.10/32
        groups: wg
        inet 10.0.0.1 netmask 0xffffff00 broadcast 10.0.0.255