:.: wireguard all the things

Hola, so this time as the title says, I will write about Wireguard, which is included in the base system of OpenBSD.

I can call myself paranoid about security and privacy, specially when it involves big companies. The less information I share with them, the better. That's why I use VPNs on almost every device I have: phone, ipad, laptops and servers.

The good thing about wireguard besides its security is that it keeps things very simple plus OpenBSD simplicity makes the whole environment easy to setup even with crossing platform, such us iOS, Android, Windows, Linux and so on.

For this setup you need only "libqrencode" and "wireguard-tools" to create qr codes to import the vpn configuration on your devices, so for this we do:

server$ doas pkg_add libqrencode wireguard-tools

I am assuming that your "server" and "openbsd clients" has no wireguard configurated, so your wireguard device should be wg0, regarless this, you can just create wg100 if you want/need.

I am gonna focus on the configuration of OpenBSD server/client, but I will also make examples for other devices and system.

The following is a remote OpenBSD server out there in some datacenter, this server will be my "default gateway" for all my devices in the vpn.

wg0

This means super-duper-privacy? NO

But it means that your ISP, coffee place, restaurant, hotel, subway station and so on, cannot see what are you doing while you use their connection.

We need for this setup a couple of entries on the pf.confs server, and a proper hostname.if, let's see the skeleton of the last one.

:. Server - /etc/hostname.wg0

wgkey $SERVER_PRIV_KEY
wgport $SERVER_PORT
wgpeer $CLIENT00_PUB_KEY wgaip $CLIENT00_IP
wgpeer $CLIENT01_PUB_KEY wgaip $CLIENT01_IP
wgpeer $CLIENT02_PUB_KEY wgaip $CLIENT02_IP
inet $SERVER_VPN_IP
up

:. Client00 - /etc/hostname.wg0

wgkey $CLIENT00_PRIV_KEY
wgpeer $SERVER_PUB_KEY wgrtable 1 wgendpoint $SERVER_PUB_IP $SERVER_PORT wgaip $NETWORK
inet $CLIENT00_VPN_IP
up

!route add default -link -iface wg0

As I said, the server will be my gateway, and that is why I added the wgrtable (you can see more about it on wg(4)), and also added "!route add default -link -iface wg0" to route all the traffic of my laptop (or desktop) through the wg0 interface even if we switch from ethernet to wireless or vice versa.

Time to fill those $VARIABLES with real data to make it works.

The server goes first, to create the $SERVER_PRIV_KEY as you can read on wg(4) you can do it with:

server# openssl rand -base64 32
EPLRZgqsVjixKiwguQ2FvZGGRwSapQkjrc+2IU8LkZs=

Keep that safe, it's your server private key. Easy right? yeah.

Now we need a free UDP port on your server ($SERVER_PORT), so let's pick 443.

Next, we run the openssl command again but this time for our client in the server or just run it on the client machine, I assume you own both things, so let's do it for it:

client$ openssl rand -base64 32
mNUVj2iKQi02NtepVEEIBzKdi1txOvOwa2r0C4+SAqc=

Time to decide the network for the vpn, try to take a not that common one, for example 10.102.0.0/24 if you go for something like 10.0.0.0/24 you can end up with conflicts on a public place with this same network. Since the server would be the gateway, the $SERVER_VPN_IP is going to be 10.102.0.1/24.

We have a port and a network, so we tell /etc/pf.conf to allow the traffic in:

...
pass out quick on egress from wg0:network to any nat-to (egress)
...
pass in quick on egress proto udp to port 443
...
server# pfctl -nf /etc/pf.conf
server# pfctl -f /etc/pf.conf

A lot of different setups are possible with PF, so in general those are the rules we need to make it work.

Be sure to have "net.inet.ip.forwarding=1" on your /etc/sysctl.conf, otherwise the traffic from your clients will not pass through the server to the internet:

server# sysctl net.inet.ip.forwarding=1
net.inet.ip.forwarding= 0 -> 1
server# echo 'net.inet.ip.forwarding=1' >> /etc/sysctl.conf

Yes, we are missing the $*_PUB_KEY, for that we need to start the hostname.if file with the missing $VARIABLES to get the $*_PUB_KEY (you can avoid this by installing wireguard-tools but is not really necesary), let's do that then:

server# cat /etc/hostname.wg0
wgkey EPLRZgqsVjixKiwguQ2FvZGGRwSapQkjrc+2IU8LkZs=
wgport 443
wgpeer wgaip 10.102.0.10/32 # client00
wgpeer wgaip 10.102.0.11/32 # client01
wgpeer wgaip 10.102.0.12/32 # client02
inet 10.102.0.1/24
up
server# sh /etc/netstart wg0
...
An error here is expected
...

Now we can get the $SERVER_PUB_KEY with:

server# ifconfig wg0 | grep wgpubkey | awk {'print $2'}
MWcDuAfc1xEvbC2OEt/X1O3PW3XrXo1sR0bbU2fo7gU=

The client00 /etc/hostname.wg0 now is complete, but won't work until we get the $CLIENT00_PUB_KEY to add it to the server:

client# cat /etc/hostname.wg0
up
wgkey mNUVj2iKQi02NtepVEEIBzKdi1txOvOwa2r0C4+SAqc=
## 0.0.0.0/0 all traffic goes over the vpn
## 10.102.0.0/24 only vpn network accesible
wgpeer MWcDuAfc1xEvbC2OEt/X1O3PW3XrXo1sR0bbU2fo7gU= wgrtable 1 wgendpoint $PUB_IP_SERVER 443 wgaip 0.0.0.0/0
inet 10.102.0.10/24
!route add default -link -iface wg0

We start the interface (fixing permission is expected) and we get the $CLIENT00_PUB_KEY:

client# sh /etc/netstart wg0
client# ifconfig wg0 | grep wgpubkey | awk {'print $2'}
YA5sB29u+NDUtOfrh7PePtW5Jw1wj2obiiDfRgLhoSE=

I will assume that you got the pub key from all your other clients, so the final /etc/hostname.wg0 will look like:

server# cat /etc/hostname.wg0
wgkey EPLRZgqsVjixKiwguQ2FvZGGRwSapQkjrc+2IU8LkZs=
wgport 443
wgpeer wgaip YA5sB29u+NDUtOfrh7PePtW5Jw1wj2obiiDfRgLhoSE= 10.102.0.10/32 # client00
wgpeer wgaip 0yx5UZhAsSAdG8QUxEajRTT1tGbOosEWVfTaVi0A4zk= 10.102.0.11/32 # client01
wgpeer wgaip qfgQxQNzZHuOY1Ek3mbztLsM4En7oxzanDdLzhVsB0A= 10.102.0.12/32 # client02
inet 10.102.0.1/24
up
server# sh /etc/netstart wg0
server#

Run sh /etc/netstart wg0 on both ends will bring the vpn up, and you will be able to see each other, you can confirm it with:

server# ifconfig wg0
wg0: flags=80c3<UP,BROADCAST,RUNNING,NOARP,MULTICAST> mtu 1420
        index 4 priority 0 llprio 3
        wgport 443
        wgpubkey MWcDuAfc1xEvbC2OEt/X1O3PW3XrXo1sR0bbU2fo7gU=
        wgpeer YA5sB29u+NDUtOfrh7PePtW5Jw1wj2obiiDfRgLhoSE=
                wgendpoint 325.9.118.351 63532
                tx: 28476, rx: 17428
                last handshake: 1750490 seconds ago
                wgaip 10.102.0.10/32
        wgpeer qfgQxQNzZHuOY1Ek3mbztLsM4En7oxzanDdLzhVsB0A=
                wgendpoint 325.9.118.351 63599
                tx: 5274216, rx: 594740
                last handshake: 1750253 seconds ago
                wgaip 10.102.0.12/32
        groups: wg
        inet 10.102.0.1 netmask 0xffffff00 broadcast 10.102.0.255

So far we configured all the points of the vpn with OpenBSD and the config files are OpenBSD friendly, but now we will add another client, this time generating a qr code with all the config to be scanned by the Wireguard app for iOS or Android and the origin of that qr code can be used with the rest of the systems such as Windows, macOS or Linux.

We'll store the other clients configuration files under /etc/wireguard and I will assume you read the above part of the article to fill the right places of the config file, but for this mobile device, I will use wireguard-tools:

server# mkdir /etc/wireguard
server# wg genkey | tee /etc/wireguard/iphone-pri.key | wg pubkey > /etc/wireguard/iphone-pub.key
server# export IPHONE_PRI=$(cat /etc/wireguard/iphone-pri.key)
server# export PUB_IP_SERVER=$(ifconfig wg0 | grep wgpubkey | awk {'print $2'})
server# cat <<EOF >/etc/wireguard/iphone.conf
[Interface]
Address = 10.102.0.15/32
PrivateKey = $IPHONE_PRI
DNS = 9.9.9.9 # some resolver (read my unbound article ;)

[Peer]
PublicKey = $PUB_IP_SERVER # pub server key
AllowedIPs = 0.0.0.0/0 # we want the iphone to navigate through the server
Endpoint = $PUB_IP_SERVER:443
EOF
server# export IPHONE_PUB=$(cat /etc/wireguard/iphone-pub.key)
server# export IPHONE_IP=$(grep Address /etc/wireguard/iphone.conf | awk '{print $3}')

:. Mobile devices

Now we have a config file for the iphone (/etc/wireguard/iphone*) with the format of Wireguard, we need to add the entry to our OpenBSD /etc/hostname.wg0 file, and then export the config file into a qr code to be scanned:

server# sed -i '/inet 10.102.0.1\/24/d' /etc/hostname.wg0
server# sed -i '/up/d' /etc/hostname.wg0
server# echo "wgpeer $(cat /etc/wireguard/iphone-pub.key) wgaip $(grep Address /etc/wireguard/iphone.conf | awk '{print $3}')" >> /etc/hostname.wg0
server# echo "inet 10.102.0.1/24" >> /etc/hostname.wg0
server# echo "up" >> /etc/hostname.wg0
server# sh /etc/netstart wg0
server# cat /etc/wireguard/iphone.conf | qrencode -t ansiutf8
█████████████████████████████████
█████████████████████████████████
████ ▄▄▄▄▄ █ ▄▄ █ ██▀█ ▄▄▄▄▄ ████
████ █   █ ██▄█▀▀  ▀██ █   █ ████
████ █▄▄▄█ █ ▀▀▄   ███ █▄▄▄█ ████
████▄▄▄▄▄▄▄█ ▀▄█▄█ ▀ █▄▄▄▄▄▄▄████
████  ▄▀█ ▄█ ▄█▄  ▄▀▄   ▄▀█  ████
████▀ ▀▄█ ▄▄█▄ ▀ █▄▄ ▄▄▀▀ ▄█▄████
█████▄▄▄  ▄▄▄  ██ ▄  █  ███▀ ████
████▄▄▄ ▀▄▄▀▀█▄▄█▀█ █▀▄▀▀ ▄█▄████
████▄▄▄██▄▄█ ██▄ ▄█  ▄▄▄ ██▄▀████
████ ▄▄▄▄▄ █▀▄▀▀ ▄ ▀ █▄█ ███▄████
████ █   █ ██▀ ██  ▄  ▄▄ █▀▀▄████
████ █▄▄▄█ █ █ ▄█▀█▄  ▀  ▄█▄▄████
████▄▄▄▄▄▄▄█▄█▄▄▄▄██▄▄███▄██▄████
█████████████████████████████████
█████████████████████████████████

Open the wireguard app on your phone, go to tunnels, and add one with the option "scan qr", now you will have all the conf in, ready to use, if you want to send an image of the qr code or just put it in a secure url of your domain to be scanned by someone else out there, you can do so with:

server# cat /etc/wireguard/iphone.conf | qrencode -s 10 -t png -o /var/www/htdocs/IpH0n3.png

:. Other systems

Remember that /etc/wireguard/iphone.conf can be use also to add the tunnel on other systems.

And that's all! I hope you can tunnel all the things now!

Have fun!