Well - thanks for following along. I’m not sure I totally get what wasn’t working or now why it’s working just fine. Moving from the networking.wireguard.interfaces
to networking.wg-quick.interfaces
just fixed me.
Here is a recap - and I’ll tag this as the ‘solution’.
- I’m running the linuxserver.io wireguard container on a machine I’ll call the server
- I have a second machine running NIxOS which I will call the client
My server is configured as I wrote up here - having wireguard setup has been amazing, I would highly recommend it.
This post is about managing a NixOS client which lives at a different physical location than the server, and has a completely different internet connection. I first tried the documented client setup - and ran into problems - as you can read in this thread.
FWIW - the client is a desktop install of NixOS. While it is currently headless, if you plug in a monitor you’ll get the full gnome desktop experience.
My solution ended up being simple. I just followed the client setup for wg-quick
I take the generated peer_client.conf
file that I generate using the linuxserer.io container that is running on my server. It looks something like this:
$cat peer_client.conf
[Interface]
Address = 10.13.13.9
PrivateKey = SECRET111XXX=
ListenPort = 51820
DNS = 192.168.1.8
[Peer]
PublicKey = SECRET222XXX=
PresharedKey = SECRET333XXX=
Endpoint = example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
And map it into the client nixos configuration file
networking.firewall = {
allowedUDPPorts = [ 51820 ]; # wireguard
};
# Enable WireGuard
networking.wg-quick.interfaces = {
# "wg0" is the network interface name. You can name the interface arbitrarily.
wg0 = {
# Determines the IP address and subnet of the client's end of the tunnel interface.
address = [ "10.13.13.9/32" ];
listenPort = 51820; # to match firewall allowedUDPPorts (without this wg uses random port numbers)
privateKeyFile = "/etc/nixos/secrets/privatekey.wg";
peers = [
{
# Public key of the server (not a file path).
publicKey = "SECRET222XXX=";
# Pre-shared key file
presharedKeyFile = "/etc/nixos/secrets/preshare.wg";
# Forward only particular subnets
allowedIPs = [ "192.168.1.0/22" ];
# Set this to the server IP and port.
endpoint = "example.com:51820";
# Send keepalives every 25 seconds. Important to keep NAT tables alive.
persistentKeepalive = 25;
}
];
};
};
You will notice I’ve got the firewall allowedUDPPorts
I’m not sure this is required, but I had it from before and it seems to at least be harmless. I’m also specifying a listenPort
which may not be required, but again doesn’t seem to hurt.
At this point - it all just works. I don’t see any particular errors in the logs. I can reach back from the server to the client
me@server:~$ ping 10.13.13.9
PING 10.13.13.9 (10.13.13.9) 56(84) bytes of data.
64 bytes from 10.13.13.9: icmp_seq=1 ttl=63 time=30.7 ms
64 bytes from 10.13.13.9: icmp_seq=2 ttl=63 time=29.6 ms
^C
--- 10.13.13.9 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 29.633/30.188/30.743/0.555 ms
I can even ssh ‘back’ over the wireguard link. My client location has zero ports opened in the firewall – but from that remote client I can resolve things on my local (to the server) network.
This is pretty awesome. Sure I could just start using tailscale – but this is all 100% self hosted (yes, I’m also aware of headscale) – and it has only a few moving parts.
In the end - this setup does exactly what I set out to do. Woo hoo!