Ramblings of an aging IT geek
← Ramblings of an aging IT geek
networking

wireguard finally killed my openvpn config

Moving my homelab VPN from a tangle of OpenVPN certs and config files to a handful of WireGuard peers that fit on one screen.

Networking cables in a patch panel

I have run OpenVPN at home for years, and I never once enjoyed it. It worked, mostly, but every change felt like defusing something. A client.ovpn here, a CA there, tls-auth keys, the cipher negotiation, the moment you realise the MTU is wrong and the connection is fine until you try to push more than a ping through it. The config was spread across half a dozen files and a folder of certificates I was frankly scared to touch.

Last weekend I moved the lot to WireGuard, and the whole thing now fits in my head.

The pitch is that WireGuard is small. The kernel module is a few thousand lines, the config is an INI file, and a peer is just a public key and an allowed IP range. There is no certificate authority, no separate key management daemon, no negotiation phase to debug. You generate a keypair, you tell each side about the other's public key, and that is the trust model. If a key is in the config, it is allowed. If it is not, it is silently dropped.

Here is the entire server config for my home box. This replaced a 40-line server.conf and a ccd directory:

[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = <server private key>

[Peer]
# laptop
PublicKey = <laptop public key>
AllowedIPs = 10.10.0.2/32

[Peer]
# phone
PublicKey = <phone public key>
AllowedIPs = 10.10.0.3/32

That is it. Bring it up with wg-quick up wg0, check it with wg show, and you get a tidy table of peers and their last handshake. The first time I ran wg show and saw the latest handshake tick over to a few seconds ago, I genuinely smiled.

A rack of networking equipment in a datacentre

A couple of things that caught me out, because nothing is ever entirely free.

The first is roaming. WireGuard does not have a persistent connection in the OpenVPN sense; it is just UDP, and a peer's endpoint is whatever address last sent a valid packet. That is brilliant for moving between wifi and mobile data, the phone just keeps working as the IP changes. But it also means the server needs to hear from the client to know where it is. For the laptop, which sits behind NAT, I added PersistentKeepalive = 25 so it pokes the server every 25 seconds and keeps the NAT mapping alive. Without it the connection goes quiet after a few minutes of idle and the first packet back in gets dropped.

The second is that there is no DNS or routing magic handed to you. OpenVPN would push redirect-gateway and DNS servers down the wire. WireGuard does none of that; you set AllowedIPs = 0.0.0.0/0 on the client side if you want to route everything, and you point DNS at whatever you like yourself. Once I understood that AllowedIPs does double duty, it is both the routing table and the access control list, it all clicked.

Performance is the part I did not expect to care about and now cannot ignore. Because it lives in the kernel and uses modern crypto with no per-packet overhead to speak of, my throughput roughly doubled over the old setup on the same hardware, and the latency is lower and steadier. The little ARM box that hosts this barely notices it now.

The old OpenVPN config is still on disk, commented out of systemd, because I am not quite brave enough to delete it. But I have not started it in a fortnight, and I do not expect to again. For a homelab VPN this is the rare upgrade that is simpler and faster at the same time.