[SOLVED] Minimal firewall setup for Wireguard client?

Edit: Solved for wg-quick. See first reply.

I can’t seem to get Wireguard traffic through NixOS default firewall rules without doing full networking.firewall.enable = false;.

Currently I’ve tested the same Wireguard configuration with:

  • fresh NixOS install, firewall enabled: doesn’t work
  • fresh NixOS isntall, firewall disabled: works
  • fresh Ubuntu install: works
  • OSX: works

I do have the wg ListenPort 51820 in my allowedUDPPorts but nothing seems to be hitting the rule:

0 0 nixos-fw-accept udp -- any any anywhere anywhere udp dpt:51820

also when Wireguard interface is up almost no traffic is hitting the nixos-fw table.

FWIW I’m doing my testing using manual wg-quick up in order to have identical setup on every test machine, but didn’t have luck earlier with networking.wireguard.interfaces either.

So my question is: starting from out-of-the-box NixOS (20.03) installation, what’s the minimal setup needed to route all traffic through a Wireguard interface without disabling the firewall?

1 Like

After debugging this a bit more I’ve found the culcript but I’m still unsure what would be the idiomatic way to fix this.

I noticed that the packets are actually being dropped in the raw table and the rule that’s dropping them is -A nixos-fw-rpfilter -j DROP. Once that’s clear it’s easy enough to create a rule that fixes the problem:

iptables -I nixos-fw-rpfilter 2 -t raw -p udp -m udp --dport 51820 -j RETURN

And indeed it works. Traffic is now allowed to go through and Wireguard works as intended. I’m quite sure I can just automate this rather easily by adding pre/post hooks to my Wireguard configuration, but that doesn’t seem intended. Looking at it more closely it seems that before enabling Wireguard traffic is going through this rule:

-A nixos-fw-rpfilter -m rpfilter --validmark -j RETURN

But Wireguard traffic seems to bypass that rule? Looking at firewall.nix it seems that there’s this: ${optionalString (cfg.checkReversePath == "loose") "--loose"}.

Turns out enabling that actually does the trick. I wouldn’t mind an explanation why, but for anyone looking for a solution to the same issue, here it is. (I’ll probably try to add it to the wiki as well)

Still not completely there though. I’d like to get this working for interfaces created with networking.wireguard.interfaces.

Those seem to create identical interface to what wg-quick created, but generate different routes. wg-quick does the fwmark trick from Routing & Network Namespaces - WireGuard but interfaces created with networking.wireguard.interfaces just seem to add new default route as well as route for the assigned ip:

< default dev wg0 scope link 
< dev wg0 proto kernel scope link src

While this is active I’m unable to get any packets through. By removing the default route that was added traffic bypasses Wireguard and everything works normally. If I add a rule ip route add dev wg0 traffic does indeed successfully go through the tunnel when connecting to that ip.

So why is the default route not working?

Found the final missing piece.


Adding proper ip route add <endpoint ip> via <gateway> dev <network interface> for the endpoint allows the default route to work as intended. Now both the networking.wireguard.interfaces and manual wg-quick work as intended.

“this only works for a static network setup and fails if gateways or devices change (e.g. using ethernet or wifi on a laptop).”

1 Like

Hmm potentially seem to be running into the same issue.

What does endpoint ip and gateway get set to? I’ve tried the public ip of the wireguard server, and the ‘usual’ gateway ip with seemingly no change in behavior.

Strangely if I set allowed_ips to a set of ips that is not, then it works correctly.

1 Like

Same here. The ip route add way doesn’t make sense for a non-static IP setup, like on a laptop. Haven’t figured out how to resolve this yet