2023-11-16
Setting up an IPv6 gateway on an Ubuntu 22.04 server with WireGuard
Recently we enabled IPv6 on one of our networks here (for initial testing purposes), but not the network that my office workstation is on. Naturally I decided that I wanted my office workstation to have IPv6 anyway, by using WireGuard to tunnel IPv6 to it from an IPv6 enabled Ubuntu 22.04 server on that network. For my sins, I also decided to do this the more or less proper way, which is to say through systemd-networkd, instead of through hand-rolled scripts.
(The absolutely proper way would be through Canonical's netplan, but netplan doesn't currently support WireGuard or some of the other features that I need, so I have to use systemd-networkd directly.)
The idea of the configuration is straightforward. My office workstation has an IPv6-only WireGuard connection to the Ubuntu server, a static IPv6 address in the subnet's regular /64 that's on the WireGuard interface, and a default IPv6 route through the WireGuard interface. The server does proxy NDP for my office workstation's static IPv6 address and then forwards traffic back and forth as applicable.
On the server, we have three pieces of configuration. First, we need to configure the WireGuard interface itself, in a networkd .netdev file:
[NetDev] Name=ipv6-wg0 Kind=wireguard [WireGuard] PrivateKey=[... no ...] ListenPort=51821 [WireGuardPeer] PublicKey=[... also no ...] AllowedIPs=<workstation IPv6>/128,fe80::/64 Endpoint=<workstation IPv4>:51821
We have to allow fe80::/64 as well as the global IPv6 address because in the end I decided to give this interface some IPv6 link local IPs.
The second thing we need is a networkd .network file to configure the server's side of the WireGuard interface. This must both set our local parameters and configure a route to the global IPv6 address of my workstation:
[Match] Name=ipv6-wg0 [Network] # Or make up a random 64 bit address Address=fe80::1/64 IPForward=yes # Disable things we don't want # Some of this may be unnecessary. DHCP=no IPv6AcceptRouterAdvertisements=no LLMNR=false [Route] Destination=<workstation IPv6>/128 [Link] # Not sure of this value, safety precaution MTUBytes=1359 RequiredForOnline=no
(If I was doing this for multiple machines, I think I would need one [Route] section per machine.)
The one thing left to do is make the server do proxy NDP, which has to be set on the Ethernet interface, not the WireGuard interface. In Ubuntu 22.04, server Ethernet interfaces are managed through netplan, but netplan has no support for setting up proxy NDP, although networkd .network files support this. So we must go behind netplan's back. In Ubuntu 22.04, netplan on servers creates systemd-networkd control files in /run/systemd/network, and these files have standard names; for example, if your server's active network interface is 'eno1', netplan will write a '10-netplan-eno1.network' file. Armed with this we can create a networkd dropin file in /etc/systemd/network/10-netplan-eno1.network.d that sets up proxy NDP, which we can call, say, 'ipv6-wg0-proxyndp.conf':
[Network] IPForward=yes IPv6ProxyNDP=yes IPv6ProxyNDPAddress=<workstation IPv6>
With all of this set up (and appropriate configuration on my office workstation), everything appears to work fine.
(On my office workstation, the WireGuard interface is configured with both the workstation's link local IPv6 address, with a peer address of the server's link-local address, and its global IPv6 address.)
All of this is pretty simple once I write it out here, but getting to this simple version took a surprising amount of experimentation and a number of attempts. Although it didn't help that I decided to switch to link local addresses after I'd already gotten a version without them working.