# OpenBSD rdomains and wireguard 2020-11-17 This post is a bit wider than usual. This is to preserve the examples intact and paste-able. Hopefully it looks allright in your client. Let me know otherwise. The last few weeks I've been experimenting with Wireguard on OpenBSD in various ways. Since I saw that support showed up in the kernel a while ago, I was curious what it could be used for. Turns out that it is nicely integrated, so that utilities like ifconfig and pf can be used to control wireguard-interfaces, and hence they can also be defined in "hostname.if" to be started at system boot and so on. The obvious first thing was to try to set up a server, which I did quite easily on a VPS: server# cat /etc/hostname.wg0 wgkey $SERVER_PRIVKEY_HERE wgport 4343 # Peer 1 wgpeer $PEER_PUBKEY_HERE wgaip 192.168.100.100/32 wgaip fda8:9887:eecb:9051::11/128 # Peer 2 wgpeer $PEER_PUBKEY_HERE wgaip 192.168.100.101/32 wgaip fda8:9887:eecb:9051::12/128 # Peer 3 wgpeer $PEER_PUBKEY_HERE wgaip 192.168.100.102/32 wgaip fda8:9887:eecb:9051::13/128 # Peer 4 wgpeer $PEER_PUBKEY_HERE wgaip 192.168.100.103/32 wgaip fda8:9887:eecb:9051::14/128 inet 192.168.100.1/24 inet6 fda8:9887:eecb:9051::1/64 up This is all it really takes to set up a server on OpenBSD. You can start it by doing: sh /etc/netstart wg0 Now for the peers. There are multiple ways to set these up. A common use-case is to set the default route via the wg-interface: peer1# cat /etc/hostname.wg0 wgkey $PEER_PRIVKEY_HERE wgpeer $SERVER_PUBKEY_HERE wgendpoint server.example.net 4343 wgaip 0.0.0.0/0 wgaip ::/0 inet 192.168.100.100/24 inet6 fda8:9887:eecb:9051::11/64 #!route add -inet -priority 7 default 192.168.100.1 #!route add -inet6 -priority 7 default fda8:9887:eecb:9051::1/64 This can be a bit clunky though. What I opted for instead is to use rdomains and rtables to set up a separate routing table for the wg-interface on the peer. That way, I can utilize pf on the peer to control which traffic goes through the tunnel, or use "route -Tn exec" to run specific applications in that specific rdomain, making it only go out via the wg-interface. This is what I ended up with: peer1# cat /etc/hostname.wg0 wgkey $PEER_PRIVKEY_HERE wgpeer $SERVER_PUBKEY_HERE wgendpoint server.example.net 4343 wgaip 0.0.0.0/0 wgaip ::/0 rdomain 1 inet 192.168.100.100/24 inet6 fda8:9887:eecb:9051::11/64 !route -T 1 add -inet -priority 7 default 192.168.100.1 !route -T 1 add -inet6 -priority 7 default fda8:9887:eecb:9051::1/64 !ifconfig lo1 rdomain 1 127.0.0.1 Here, I set the interface to belong to the rdomain 1, and I also add the default routes, but to the rtable 1 in rdomain 1. I also set up a loopback-interface so stuff that expects to be able to bind to it can do that. This may or may not be desired. In my specific use-case, I used it because the tor-browser-bundle expects to be able to bind to 127.0.0.1, which it cannot if it's not available in the rdomain. Next, I used some rules in pf to re-direct DNS to specific servers reachable through wireguard. In this case, it's an unbound-instance running on the wireguard-server, and I wanted all DNS-requests to go to that server. So I did something along these lines on the peer: pass out on rdomain 1 proto { udp tcp } from any to any port 53 \ rdr-to 192.168.100.1 port 53 This will make any traffic towards anything on port 53 (either tcp or udp) be redirected to 192.168.100.1. This will force DNS-requests happening in the rdomain go to a server on the other side of the tunnel. Once this is done and the pf-config and brought up the interfaces, you should be able to do stuff like this on the peer: route exec curl ifconfig.io/all route -T 1 exec curl ifconfig.io/all The first example should go through the default routing table, showing your public IP. The second should go through the tunnel, and show the corresponding public IP for that system. Success! (Hopefully). With this, you can do all sorts of interesting things. You could for example run: route -T 1 exec ksh This will give you a shell belonging to rdomain 1, and hence, any process spawned in that shell will inherit that. This is a simple way to not have to prepend the route/exec-combination to run stuff through the tunnel. So I hope that you enjoyed this little post. It may be a bit unclear, but it's also a bit of a mental note to myself, even though the idea of sharing it here is to give others a way to utilize similar solutions as well. I've had a lot of fun with this during the last few days, and hopefully it can be meaningful for others as well. Please send me some feedback if you enjoyed it! The content for this site is CC-BY-SA-4.0.