nixos/netbird: Allow running multiple netbird networks in parallel

This commit is contained in:
Tom Hubrecht 2024-01-23 16:24:26 +01:00
parent f032654298
commit c2d822e6b0
3 changed files with 215 additions and 46 deletions

View File

@ -165,6 +165,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
existing process, but will need to start that process from gdb (so it is a
child). Or you can set `boot.kernel.sysctl."kernel.yama.ptrace_scope"` to 0.
- The netbird module now allows running multiple tunnels in parallel through [`services.netbird.tunnels`](#opt-services.netbird.tunnels).
- [Nginx virtual hosts](#opt-services.nginx.virtualHosts) using `forceSSL` or
`globalRedirect` can now have redirect codes other than 301 through
`redirectCode`.

View File

@ -0,0 +1,56 @@
# Netbird {#module-services-netbird}
## Quickstart {#module-services-netbird-quickstart}
The absolute minimal configuration for the netbird daemon looks like this:
```nix
services.netbird.enable = true;
```
This will set up a netbird service listening on the port `51820` associated to the
`wt0` interface.
It is strictly equivalent to setting:
```nix
services.netbird.tunnels.wt0.stateDir = "netbird";
```
The `enable` option is mainly kept for backward compatibility, as defining netbird
tunnels through the `tunnels` option is more expressive.
## Multiple connections setup {#module-services-netbird-multiple-connections}
Using the `services.netbird.tunnels` option, it is also possible to define more than
one netbird service running at the same time.
The following configuration will start a netbird daemon using the interface `wt1` and
the port 51830. Its configuration file will then be located at `/var/lib/netbird-wt1/config.json`.
```nix
services.netbird.tunnels = {
wt1 = {
port = 51830;
};
};
```
To interact with it, you will need to specify the correct daemon address:
```bash
netbird --daemon-addr unix:///var/run/netbird-wt1/sock ...
```
The address will by default be `unix:///var/run/netbird-<name>`.
It is also possible to overwrite default options passed to the service, for
example:
```nix
services.netbird.tunnels.wt1.environment = {
NB_DAEMON_ADDR = "unix:///var/run/toto.sock"
};
```
This will set the socket to interact with the netbird service to `/var/run/toto.sock`.

View File

@ -1,60 +1,171 @@
{ config, lib, pkgs, ... }:
with lib;
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.netbird;
inherit (lib)
attrNames
getExe
literalExpression
maintainers
mapAttrs'
mkDefault
mkEnableOption
mkIf
mkMerge
mkOption
mkPackageOption
nameValuePair
optional
versionOlder
;
inherit (lib.types)
attrsOf
port
str
submodule
;
kernel = config.boot.kernelPackages;
interfaceName = "wt0";
in {
meta.maintainers = with maintainers; [ misuzu ];
cfg = config.services.netbird;
in
{
meta.maintainers = with maintainers; [
misuzu
thubrecht
];
meta.doc = ./netbird.md;
options.services.netbird = {
enable = mkEnableOption (lib.mdDoc "Netbird daemon");
package = mkPackageOption pkgs "netbird" { };
};
config = mkIf cfg.enable {
boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard;
tunnels = mkOption {
type = attrsOf (
submodule (
{ name, config, ... }:
{
options = {
port = mkOption {
type = port;
default = 51820;
description = ''
Port for the ${name} netbird interface.
'';
};
environment.systemPackages = [ cfg.package ];
environment = mkOption {
type = attrsOf str;
defaultText = literalExpression ''
{
NB_CONFIG = "/var/lib/''${stateDir}/config.json";
NB_LOG_FILE = "console";
NB_WIREGUARD_PORT = builtins.toString port;
NB_INTERFACE_NAME = name;
NB_DAMEON_ADDR = "/var/run/''${stateDir}"
}
'';
description = ''
Environment for the netbird service, used to pass configuration options.
'';
};
networking.dhcpcd.denyInterfaces = [ interfaceName ];
stateDir = mkOption {
type = str;
default = "netbird-${name}";
description = ''
Directory storing the netbird configuration.
'';
};
};
systemd.network.networks."50-netbird" = mkIf config.networking.useNetworkd {
matchConfig = {
Name = interfaceName;
};
linkConfig = {
Unmanaged = true;
ActivationPolicy = "manual";
};
};
systemd.services.netbird = {
description = "A WireGuard-based mesh network that connects your devices into a single private network";
documentation = [ "https://netbird.io/docs/" ];
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [
openresolv
];
serviceConfig = {
Environment = [
"NB_CONFIG=/var/lib/netbird/config.json"
"NB_LOG_FILE=console"
];
ExecStart = "${cfg.package}/bin/netbird service run";
Restart = "always";
RuntimeDirectory = "netbird";
StateDirectory = "netbird";
WorkingDirectory = "/var/lib/netbird";
};
unitConfig = {
StartLimitInterval = 5;
StartLimitBurst = 10;
};
stopIfChanged = false;
config.environment = builtins.mapAttrs (_: mkDefault) {
NB_CONFIG = "/var/lib/${config.stateDir}/config.json";
NB_LOG_FILE = "console";
NB_WIREGUARD_PORT = builtins.toString config.port;
NB_INTERFACE_NAME = name;
NB_DAEMON_ADDR = "unix:///var/run/${config.stateDir}/sock";
};
}
)
);
default = { };
description = ''
Attribute set of Netbird tunnels, each one will spawn a daemon listening on ...
'';
};
};
config = mkMerge [
(mkIf cfg.enable {
# For backwards compatibility
services.netbird.tunnels.wt0.stateDir = "netbird";
})
(mkIf (cfg.tunnels != { }) {
boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard;
environment.systemPackages = [ cfg.package ];
networking.dhcpcd.denyInterfaces = attrNames cfg.tunnels;
systemd.network.networks = mkIf config.networking.useNetworkd (
mapAttrs'
(
name: _:
nameValuePair "50-netbird-${name}" {
matchConfig = {
Name = name;
};
linkConfig = {
Unmanaged = true;
ActivationPolicy = "manual";
};
}
)
cfg.tunnels
);
systemd.services =
mapAttrs'
(
name:
{ environment, stateDir, ... }:
nameValuePair "netbird-${name}" {
description = "A WireGuard-based mesh network that connects your devices into a single private network";
documentation = [ "https://netbird.io/docs/" ];
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [ openresolv ];
inherit environment;
serviceConfig = {
ExecStart = "${getExe cfg.package} service run";
Restart = "always";
RuntimeDirectory = stateDir;
StateDirectory = stateDir;
StateDirectoryMode = "0700";
WorkingDirectory = "/var/lib/${stateDir}";
};
unitConfig = {
StartLimitInterval = 5;
StartLimitBurst = 10;
};
stopIfChanged = false;
}
)
cfg.tunnels;
})
];
}