nixos/netbird: add useRoutingFeatures option

Similar to what the NixOS Tailscale service has.
Hope this will help a bunch of users struggling to make the "exit node" feature working.
This commit is contained in:
Pol Dellaiera 2025-08-07 10:02:38 +02:00
commit 8b5c57b674
2 changed files with 46 additions and 12 deletions

View file

@ -76,6 +76,11 @@ Each Netbird client service by default:
peer-to-peer communication,
- can be additionally configured with environment variables,
- automatically determines whether `netbird-ui-<name>` should be available,
- does not enable [routing features](#opt-services.netbird.useRoutingFeatures) by default
If you plan to use routing features, you must explicitly enable them. By enabling them, the service will
configure the firewall and enable IP forwarding on the system.
When set to `client` or `both`, reverse path filtering will be set to loose instead of strict.
When set to `server` or `both`, IP forwarding will be enabled.
[autoStart](#opt-services.netbird.clients._name_.autoStart) allows you to start the client (an actual systemd service)
on demand, for example to connect to work-related or otherwise conflicting network only when required.

View file

@ -24,6 +24,7 @@ let
mkMerge
mkOption
mkOptionDefault
mkOverride
mkPackageOption
nameValuePair
optional
@ -112,6 +113,23 @@ in
};
ui.package = mkPackageOption pkgs "netbird-ui" { };
useRoutingFeatures = mkOption {
type = enum [
"none"
"client"
"server"
"both"
];
default = "none";
example = "server";
description = ''
Enables settings required for Netbird's routing features like subnet routers and exit nodes.
When set to `client` or `both`, reverse path filtering will be set to loose instead of strict.
When set to `server` or `both`, IP forwarding will be enabled.
'';
};
clients = mkOption {
type = attrsOf (
submodule (
@ -467,19 +485,30 @@ in
networking.dhcpcd.denyInterfaces = toClientList (client: client.interface);
networking.networkmanager.unmanaged = toClientList (client: "interface-name:${client.interface}");
networking.firewall.allowedUDPPorts = concatLists (
toClientList (client: optional client.openFirewall client.port)
);
# Required for the routing ("Exit node") feature(s) to work
boot.kernel.sysctl = mkIf (cfg.useRoutingFeatures == "server" || cfg.useRoutingFeatures == "both") {
"net.ipv4.conf.all.forwarding" = mkOverride 97 true;
"net.ipv6.conf.all.forwarding" = mkOverride 97 true;
};
# Ports opened on a specific
networking.firewall.interfaces = listToAttrs (
toClientList (client: {
name = client.interface;
value.allowedUDPPorts = optionals client.openFirewall [
5353 # required for the DNS forwarding/routing to work
];
})
);
networking.firewall = {
allowedUDPPorts = concatLists (toClientList (client: optional client.openFirewall client.port));
# Required for the routing ("Exit node") feature(s) to work
checkReversePath = mkIf (
cfg.useRoutingFeatures == "client" || cfg.useRoutingFeatures == "both"
) "loose";
# Ports opened on a specific
interfaces = listToAttrs (
toClientList (client: {
name = client.interface;
value.allowedUDPPorts = optionals client.openFirewall [
5353 # required for the DNS forwarding/routing to work
];
})
);
};
systemd.network.networks = mkIf config.networking.useNetworkd (
toClientAttrs (