add netbird patch
This commit is contained in:
parent
086b90ba40
commit
7cb753b5ae
@ -116,7 +116,9 @@
|
||||
customRoles = import ./roles;
|
||||
secretsDir = ./secrets;
|
||||
|
||||
sharedPatches = patchesPath [ ];
|
||||
sharedPatches = patchesPath [
|
||||
|
||||
];
|
||||
sharedOverlays = [ flake-utils-plus.overlay inputs.sops-nix.overlays.default ];
|
||||
channelsConfig = {
|
||||
allowUnfree = true; android_sdk.accept_license = true;
|
||||
@ -126,9 +128,12 @@
|
||||
channels.unstable.patches = sharedPatches ++ patchesPath [
|
||||
"onlyoffice.patch" "vaultwarden.patch"
|
||||
"jaxlib.patch" "zen-kernels.patch"
|
||||
"netbird-24.11.patch"
|
||||
];
|
||||
channels.stable.input = inputs.nixpkgs-stable;
|
||||
channels.stable.patches = sharedPatches;
|
||||
channels.stable.patches = sharedPatches ++ patchesPath [
|
||||
"netbird-24.05.patch"
|
||||
];
|
||||
|
||||
hostDefaults.system = "x86_64-linux";
|
||||
hostDefaults.channelName = "unstable";
|
||||
|
603
patches/netbird-24.05.patch
Normal file
603
patches/netbird-24.05.patch
Normal file
@ -0,0 +1,603 @@
|
||||
diff --git a/nixos/modules/services/networking/netbird.nix b/nixos/modules/services/networking/netbird.nix
|
||||
index 7add37789..0160a8964 100644
|
||||
--- a/nixos/modules/services/networking/netbird.nix
|
||||
+++ b/nixos/modules/services/networking/netbird.nix
|
||||
@@ -1,73 +1,155 @@
|
||||
-{
|
||||
- config,
|
||||
- lib,
|
||||
- pkgs,
|
||||
- ...
|
||||
+{ config
|
||||
+, lib
|
||||
+, pkgs
|
||||
+, ...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
- attrNames
|
||||
+ attrValues
|
||||
+ concatLists
|
||||
+ concatStringsSep
|
||||
+ escapeShellArgs
|
||||
+ filterAttrs
|
||||
getExe
|
||||
literalExpression
|
||||
maintainers
|
||||
+ makeBinPath
|
||||
mapAttrs'
|
||||
+ mapAttrsToList
|
||||
mkDefault
|
||||
- mkEnableOption
|
||||
mkIf
|
||||
mkMerge
|
||||
mkOption
|
||||
+ mkOptionDefault
|
||||
mkPackageOption
|
||||
+ mkRemovedOptionModule
|
||||
nameValuePair
|
||||
optional
|
||||
+ optionalString
|
||||
+ toShellVars
|
||||
+ versionAtLeast
|
||||
versionOlder
|
||||
;
|
||||
|
||||
inherit (lib.types)
|
||||
attrsOf
|
||||
+ bool
|
||||
+ enum
|
||||
+ package
|
||||
port
|
||||
str
|
||||
submodule
|
||||
;
|
||||
|
||||
- kernel = config.boot.kernelPackages;
|
||||
+ inherit (config.boot) kernelPackages;
|
||||
+ inherit (config.boot.kernelPackages) kernel;
|
||||
|
||||
cfg = config.services.netbird;
|
||||
+
|
||||
+ toClientList = fn: map fn (attrValues cfg.clients);
|
||||
+ toClientAttrs = fn: mapAttrs' (_: fn) cfg.clients;
|
||||
+
|
||||
+ hardenedClients = filterAttrs (_: client: client.hardened) cfg.clients;
|
||||
+ toHardenedClientList = fn: map fn (attrValues hardenedClients);
|
||||
+ toHardenedClientAttrs = fn: mapAttrs' (_: fn) hardenedClients;
|
||||
+
|
||||
+ nixosConfig = config;
|
||||
in
|
||||
{
|
||||
meta.maintainers = with maintainers; [
|
||||
misuzu
|
||||
- thubrecht
|
||||
+ nazarewk
|
||||
];
|
||||
meta.doc = ./netbird.md;
|
||||
|
||||
+ imports = [
|
||||
+ (mkRemovedOptionModule [ "services" "netbird" "tunnels" ]
|
||||
+ "The option `services.netbird.tunnels` has been renamed to `services.netbird.clients`")
|
||||
+ ];
|
||||
+
|
||||
options.services.netbird = {
|
||||
- enable = mkEnableOption "Netbird daemon";
|
||||
+ enable = mkOption {
|
||||
+ type = bool;
|
||||
+ default = false;
|
||||
+ description = ''
|
||||
+ Enables backwards compatible Netbird client service.
|
||||
+
|
||||
+ This is strictly equivalent to:
|
||||
+
|
||||
+ ```nix
|
||||
+ services.netbird.clients.wt0 = {
|
||||
+ port = 51820;
|
||||
+ name = "netbird";
|
||||
+ interface = "wt0";
|
||||
+ hardened = false;
|
||||
+ };
|
||||
+ ```
|
||||
+ '';
|
||||
+ };
|
||||
package = mkPackageOption pkgs "netbird" { };
|
||||
|
||||
- tunnels = mkOption {
|
||||
+ ui.enable = mkOption {
|
||||
+ type = bool;
|
||||
+ default = config.services.displayManager.sessionPackages != [ ] || config.services.xserver.enable;
|
||||
+ defaultText = literalExpression ''
|
||||
+ config.services.displayManager.sessionPackages != [ ] || config.services.xserver.enable
|
||||
+ '';
|
||||
+ description = ''
|
||||
+ Controls presence `netbird-ui` wrappers, defaults to presence of graphical sessions.
|
||||
+ '';
|
||||
+ };
|
||||
+ ui.package = mkPackageOption pkgs "netbird-ui" { };
|
||||
+
|
||||
+ clients = mkOption {
|
||||
type = attrsOf (
|
||||
submodule (
|
||||
{ name, config, ... }:
|
||||
+ let client = config; in
|
||||
{
|
||||
options = {
|
||||
port = mkOption {
|
||||
type = port;
|
||||
- default = 51820;
|
||||
+ example = literalExpression "51820";
|
||||
description = ''
|
||||
- Port for the ${name} netbird interface.
|
||||
+ Port the Netbird client listens on.
|
||||
'';
|
||||
};
|
||||
|
||||
+ name = mkOption {
|
||||
+ type = str;
|
||||
+ default = name;
|
||||
+ description = ''
|
||||
+ Primary name for use (as a suffix) in:
|
||||
+ - systemd service name,
|
||||
+ - hardened user name and group,
|
||||
+ - [systemd `*Directory=`](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#RuntimeDirectory=) names,
|
||||
+ - desktop application identification,
|
||||
+ '';
|
||||
+ };
|
||||
+
|
||||
+ interface = mkOption {
|
||||
+ type = str;
|
||||
+ default = "nb-${client.name}";
|
||||
+ description = ''
|
||||
+ Name of the network interface managed by this client.
|
||||
+ '';
|
||||
+ apply = iface:
|
||||
+ lib.throwIfNot (builtins.stringLength iface <= 15) "Network interface name must be 15 characters or less"
|
||||
+ iface;
|
||||
+ };
|
||||
+
|
||||
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}"
|
||||
+ NB_CONFIG = "/var/lib/netbird-''${client.name}/config.json";
|
||||
+ NB_DAEMON_ADDR = "unix:///var/run/netbird-''${client.name}/sock";
|
||||
+ NB_INTERFACE_NAME = config.interface;
|
||||
+ NB_LOG_FILE = mkOptionDefault "console";
|
||||
+ NB_LOG_LEVEL = config.logLevel;
|
||||
+ NB_SERVICE = "netbird-''${client.name}";
|
||||
+ NB_WIREGUARD_PORT = toString config.port;
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
@@ -75,97 +157,361 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
- stateDir = mkOption {
|
||||
- type = str;
|
||||
- default = "netbird-${name}";
|
||||
+ autoStart = mkOption {
|
||||
+ type = bool;
|
||||
+ default = true;
|
||||
+ description = ''
|
||||
+ Start the service with the system.
|
||||
+
|
||||
+ As of 2024-02-13 it is not possible to start a Netbird client daemon without immediately
|
||||
+ connecting to the network, but it is [planned for a near future](https://github.com/netbirdio/netbird/projects/2#card-91718018).
|
||||
+ '';
|
||||
+ };
|
||||
+
|
||||
+ openFirewall = mkOption {
|
||||
+ type = bool;
|
||||
+ default = true;
|
||||
+ description = ''
|
||||
+ Opens up firewall `port` for communication between Netbird peers directly over LAN or public IP,
|
||||
+ without using (internet-hosted) TURN servers as intermediaries.
|
||||
+ '';
|
||||
+ };
|
||||
+
|
||||
+ hardened = mkOption {
|
||||
+ type = bool;
|
||||
+ default = true;
|
||||
description = ''
|
||||
- Directory storing the netbird configuration.
|
||||
+ Hardened service:
|
||||
+ - runs as a dedicated user with minimal set of permissions (see caveats),
|
||||
+ - restricts daemon configuration socket access to dedicated user group
|
||||
+ (you can grant access to it with `users.users."<user>".extraGroups = [ "netbird-${client.name}" ]`),
|
||||
+
|
||||
+ Even though the local system resources access is restricted:
|
||||
+ - `CAP_NET_RAW`, `CAP_NET_ADMIN` and `CAP_BPF` still give unlimited network manipulation possibilites,
|
||||
+ - older kernels don't have `CAP_BPF` and use `CAP_SYS_ADMIN` instead,
|
||||
+
|
||||
+ Known security features that are not (yet) integrated into the module:
|
||||
+ - 2024-02-14: `rosenpass` is an experimental feature configurable solely
|
||||
+ through `--enable-rosenpass` flag on the `netbird up` command,
|
||||
+ see [the docs](https://docs.netbird.io/how-to/enable-post-quantum-cryptography)
|
||||
+ '';
|
||||
+ };
|
||||
+
|
||||
+ logLevel = mkOption {
|
||||
+ type = enum [
|
||||
+ # logrus loglevels
|
||||
+ "panic"
|
||||
+ "fatal"
|
||||
+ "error"
|
||||
+ "warn"
|
||||
+ "warning"
|
||||
+ "info"
|
||||
+ "debug"
|
||||
+ "trace"
|
||||
+ ];
|
||||
+ default = "info";
|
||||
+ description = "Log level of the Netbird daemon.";
|
||||
+ };
|
||||
+
|
||||
+ ui.enable = mkOption {
|
||||
+ type = bool;
|
||||
+ default = nixosConfig.services.netbird.ui.enable;
|
||||
+ defaultText = literalExpression ''config.ui.enable'';
|
||||
+ description = ''
|
||||
+ Controls presence of `netbird-ui` wrapper for this Netbird client.
|
||||
+ '';
|
||||
+ };
|
||||
+
|
||||
+ wrapper = mkOption {
|
||||
+ type = package;
|
||||
+ internal = true;
|
||||
+ default =
|
||||
+ let
|
||||
+ makeWrapperArgs = concatLists (mapAttrsToList
|
||||
+ (key: value: [ "--set-default" key value ])
|
||||
+ config.environment
|
||||
+ );
|
||||
+ in
|
||||
+ pkgs.stdenv.mkDerivation {
|
||||
+ name = "${cfg.package.name}-wrapper-${client.name}";
|
||||
+ meta.mainProgram = "netbird-${client.name}";
|
||||
+ nativeBuildInputs = with pkgs; [ makeWrapper ];
|
||||
+ phases = [ "installPhase" ];
|
||||
+ installPhase = concatStringsSep "\n" [
|
||||
+ ''
|
||||
+ mkdir -p "$out/bin"
|
||||
+ makeWrapper ${lib.getExe cfg.package} "$out/bin/netbird-${client.name}" \
|
||||
+ ${escapeShellArgs makeWrapperArgs}
|
||||
+ ''
|
||||
+ (optionalString cfg.ui.enable ''
|
||||
+ # netbird-ui doesn't support envvars
|
||||
+ makeWrapper ${lib.getExe cfg.ui.package} "$out/bin/netbird-ui-${client.name}" \
|
||||
+ --add-flags '--daemon-addr=${config.environment.NB_DAEMON_ADDR}'
|
||||
+
|
||||
+ mkdir -p "$out/share/applications"
|
||||
+ substitute ${cfg.ui.package}/share/applications/netbird.desktop \
|
||||
+ "$out/share/applications/netbird-${client.name}.desktop" \
|
||||
+ --replace-fail 'Name=Netbird' "Name=Netbird @ netbird-${client.name}" \
|
||||
+ --replace-fail '${lib.getExe cfg.ui.package}' "$out/bin/netbird-ui-${client.name}"
|
||||
+ '')
|
||||
+ ];
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ # see https://github.com/netbirdio/netbird/blob/88747e3e0191abc64f1e8c7ecc65e5e50a1527fd/client/internal/config.go#L49-L82
|
||||
+ config = mkOption {
|
||||
+ type = (pkgs.formats.json { }).type;
|
||||
+ defaultText = literalExpression ''
|
||||
+ {
|
||||
+ DisableAutoConnect = !config.autoStart;
|
||||
+ WgIface = config.interface;
|
||||
+ WgPort = config.port;
|
||||
+ }
|
||||
+ '';
|
||||
+ description = ''
|
||||
+ Additional configuration that exists before the first start and
|
||||
+ later overrides the existing values in `config.json`.
|
||||
+
|
||||
+ It is mostly helpful to manage configuration ignored/not yet implemented
|
||||
+ outside of `netbird up` invocation.
|
||||
+
|
||||
+ WARNING: this is not an upstream feature, it could break in the future
|
||||
+ (by having lower priority) after upstream implements an equivalent.
|
||||
+
|
||||
+ It is implemented as a `preStart` script which overrides `config.json`
|
||||
+ with content of `/etc/netbird-${client.name}/config.d/*.json` files.
|
||||
+ This option manages specifically `50-nixos.json` file.
|
||||
+
|
||||
+ Consult [the source code](https://github.com/netbirdio/netbird/blob/88747e3e0191abc64f1e8c7ecc65e5e50a1527fd/client/internal/config.go#L49-L82)
|
||||
+ or inspect existing file for a complete list of available configurations.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
- 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";
|
||||
+ config.environment = {
|
||||
+ NB_CONFIG = "/var/lib/netbird-${client.name}/config.json";
|
||||
+ NB_DAEMON_ADDR = "unix:///var/run/netbird-${client.name}/sock";
|
||||
+ NB_INTERFACE_NAME = config.interface;
|
||||
+ NB_LOG_FILE = mkOptionDefault "console";
|
||||
+ NB_LOG_LEVEL = config.logLevel;
|
||||
+ NB_SERVICE = "netbird-${client.name}";
|
||||
+ NB_WIREGUARD_PORT = toString config.port;
|
||||
+ };
|
||||
+
|
||||
+ config.config = {
|
||||
+ DisableAutoConnect = !config.autoStart;
|
||||
+ WgIface = config.interface;
|
||||
+ WgPort = config.port;
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
default = { };
|
||||
description = ''
|
||||
- Attribute set of Netbird tunnels, each one will spawn a daemon listening on ...
|
||||
+ Attribute set of Netbird client daemons, by default each one will:
|
||||
+
|
||||
+ 1. be manageable using dedicated tooling:
|
||||
+ - `netbird-<name>` script,
|
||||
+ - `Netbird - netbird-<name>` graphical interface when appropriate (see `ui.enable`),
|
||||
+ 2. run as a `netbird-<name>.service`,
|
||||
+ 3. listen for incoming remote connections on the port `51820` (`openFirewall` by default),
|
||||
+ 4. manage the `netbird-<name>` wireguard interface,
|
||||
+ 5. use the `/var/lib/netbird-<name>/config.json` configuration file,
|
||||
+ 6. override `/var/lib/netbird-<name>/config.json` with values from `/etc/netbird-<name>/config.d/*.json`,
|
||||
+ 7. (`hardened`) be locally manageable by `netbird-<name>` system group,
|
||||
+
|
||||
+ With following caveats:
|
||||
+
|
||||
+ - multiple daemons will interfere with each other's DNS resolution of `netbird.cloud`, but
|
||||
+ should remain fully operational otherwise.
|
||||
+ Setting up custom (non-conflicting) DNS zone is currently possible only when self-hosting.
|
||||
+ '';
|
||||
+ example = lib.literalExpression ''
|
||||
+ {
|
||||
+ services.netbird.clients.wt0.port = 51820;
|
||||
+ services.netbird.clients.personal.port = 51821;
|
||||
+ services.netbird.clients.work1.port = 51822;
|
||||
+ }
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkMerge [
|
||||
- (mkIf cfg.enable {
|
||||
- # For backwards compatibility
|
||||
- services.netbird.tunnels.wt0.stateDir = "netbird";
|
||||
- })
|
||||
+ (mkIf cfg.enable (
|
||||
+ let name = "wt0"; client = cfg.clients."${name}"; in {
|
||||
+ services.netbird.clients."${name}" = {
|
||||
+ port = mkDefault 51820;
|
||||
+ name = mkDefault "netbird";
|
||||
+ interface = mkDefault "wt0";
|
||||
+ hardened = mkDefault false;
|
||||
+ };
|
||||
|
||||
- (mkIf (cfg.tunnels != { }) {
|
||||
- boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard;
|
||||
+ environment.systemPackages = [
|
||||
+ (lib.hiPrio (pkgs.runCommand "${client.name}-as-default" { } ''
|
||||
+ mkdir -p "$out/bin"
|
||||
+ for binary in netbird ${optionalString cfg.ui.enable "netbird-ui"} ; do
|
||||
+ ln -s "${client.wrapper}/bin/$binary-${client.name}" "$out/bin/$binary"
|
||||
+ done
|
||||
+ ''))
|
||||
+ ];
|
||||
+ }
|
||||
+ ))
|
||||
+ {
|
||||
+ boot.extraModulePackages = optional
|
||||
+ (cfg.clients != { } && (versionOlder kernel.version "5.6"))
|
||||
+ kernelPackages.wireguard;
|
||||
|
||||
- environment.systemPackages = [ cfg.package ];
|
||||
+ environment.systemPackages =
|
||||
+ toClientList (client: client.wrapper)
|
||||
+ # omitted due to https://github.com/netbirdio/netbird/issues/1562
|
||||
+ #++ optional (cfg.clients != { }) cfg.package
|
||||
+ # omitted due to https://github.com/netbirdio/netbird/issues/1581
|
||||
+ #++ optional (cfg.clients != { } && cfg.ui.enable) cfg.ui.package
|
||||
+ ;
|
||||
|
||||
- networking.dhcpcd.denyInterfaces = attrNames cfg.tunnels;
|
||||
+ networking.dhcpcd.denyInterfaces = toClientList (client: client.interface);
|
||||
+ networking.networkmanager.unmanaged = toClientList (client: "interface-name:${client.interface}");
|
||||
|
||||
- systemd.network.networks = mkIf config.networking.useNetworkd (
|
||||
- mapAttrs'
|
||||
- (
|
||||
- name: _:
|
||||
- nameValuePair "50-netbird-${name}" {
|
||||
- matchConfig = {
|
||||
- Name = name;
|
||||
- };
|
||||
- linkConfig = {
|
||||
- Unmanaged = true;
|
||||
- ActivationPolicy = "manual";
|
||||
- };
|
||||
- }
|
||||
- )
|
||||
- cfg.tunnels
|
||||
- );
|
||||
+ networking.firewall.allowedUDPPorts = concatLists (toClientList (client: optional client.openFirewall client.port));
|
||||
|
||||
- systemd.services =
|
||||
- mapAttrs'
|
||||
- (
|
||||
- name:
|
||||
- { environment, stateDir, ... }:
|
||||
- nameValuePair "netbird-${name}" {
|
||||
- description = "A WireGuard-based mesh network that connects your devices into a single private network";
|
||||
+ systemd.network.networks = mkIf config.networking.useNetworkd (toClientAttrs (client:
|
||||
+ nameValuePair "50-netbird-${client.interface}" {
|
||||
+ matchConfig = {
|
||||
+ Name = client.interface;
|
||||
+ };
|
||||
+ linkConfig = {
|
||||
+ Unmanaged = true;
|
||||
+ ActivationPolicy = "manual";
|
||||
+ };
|
||||
+ }
|
||||
+ ));
|
||||
|
||||
- documentation = [ "https://netbird.io/docs/" ];
|
||||
+ environment.etc = toClientAttrs (client: nameValuePair "netbird-${client.name}/config.d/50-nixos.json" {
|
||||
+ text = builtins.toJSON client.config;
|
||||
+ mode = "0444";
|
||||
+ });
|
||||
|
||||
- after = [ "network.target" ];
|
||||
- wantedBy = [ "multi-user.target" ];
|
||||
+ systemd.services = toClientAttrs (client: nameValuePair "netbird-${client.name}" {
|
||||
+ description = "A WireGuard-based mesh network that connects your devices into a single private network";
|
||||
|
||||
- path = with pkgs; [ openresolv ];
|
||||
+ documentation = [ "https://netbird.io/docs/" ];
|
||||
|
||||
- inherit environment;
|
||||
+ after = [ "network.target" ];
|
||||
+ wantedBy = [ "multi-user.target" ];
|
||||
|
||||
- serviceConfig = {
|
||||
- ExecStart = "${getExe cfg.package} service run";
|
||||
- Restart = "always";
|
||||
- RuntimeDirectory = stateDir;
|
||||
- StateDirectory = stateDir;
|
||||
- StateDirectoryMode = "0700";
|
||||
- WorkingDirectory = "/var/lib/${stateDir}";
|
||||
- };
|
||||
+ path = optional (!config.services.resolved.enable) pkgs.openresolv;
|
||||
|
||||
- unitConfig = {
|
||||
- StartLimitInterval = 5;
|
||||
- StartLimitBurst = 10;
|
||||
- };
|
||||
+ serviceConfig = {
|
||||
+ ExecStart = "${getExe client.wrapper} service run";
|
||||
+ Restart = "always";
|
||||
+
|
||||
+ RuntimeDirectory = "netbird-${client.name}";
|
||||
+ RuntimeDirectoryMode = mkDefault "0755";
|
||||
+ ConfigurationDirectory = "netbird-${client.name}";
|
||||
+ StateDirectory = "netbird-${client.name}";
|
||||
+ StateDirectoryMode = "0700";
|
||||
+
|
||||
+ WorkingDirectory = "/var/lib/netbird-${client.name}";
|
||||
+ };
|
||||
+
|
||||
+ unitConfig = {
|
||||
+ StartLimitInterval = 5;
|
||||
+ StartLimitBurst = 10;
|
||||
+ };
|
||||
+
|
||||
+ stopIfChanged = false;
|
||||
+ });
|
||||
+ }
|
||||
+ # Hardening section
|
||||
+ (mkIf (hardenedClients != { }) {
|
||||
+ users.groups = toHardenedClientAttrs (client: nameValuePair "netbird-${client.name}" { });
|
||||
+ users.users = toHardenedClientAttrs (client: nameValuePair "netbird-${client.name}" {
|
||||
+ isSystemUser = true;
|
||||
+ home = "/var/lib/netbird-${client.name}";
|
||||
+ group = "netbird-${client.name}";
|
||||
+ });
|
||||
+
|
||||
+ systemd.services = toHardenedClientAttrs (client: nameValuePair "netbird-${client.name}" (mkIf client.hardened {
|
||||
+ serviceConfig = {
|
||||
+ RuntimeDirectoryMode = "0750";
|
||||
+
|
||||
+ User = "netbird-${client.name}";
|
||||
+ Group = "netbird-${client.name}";
|
||||
+
|
||||
+ # settings implied by DynamicUser=true, without actully using it,
|
||||
+ # see https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#DynamicUser=
|
||||
+ RemoveIPC = true;
|
||||
+ PrivateTmp = true;
|
||||
+ ProtectSystem = "strict";
|
||||
+ ProtectHome = "yes";
|
||||
|
||||
- stopIfChanged = false;
|
||||
- }
|
||||
- )
|
||||
- cfg.tunnels;
|
||||
+ AmbientCapabilities = [
|
||||
+ # see https://man7.org/linux/man-pages/man7/capabilities.7.html
|
||||
+ # see https://docs.netbird.io/how-to/installation#running-net-bird-in-docker
|
||||
+ #
|
||||
+ # seems to work fine without CAP_SYS_ADMIN and CAP_SYS_RESOURCE
|
||||
+ # CAP_NET_BIND_SERVICE could be added to allow binding on low ports, but is not required,
|
||||
+ # see https://github.com/netbirdio/netbird/pull/1513
|
||||
+
|
||||
+ # failed creating tunnel interface wt-priv: [operation not permitted
|
||||
+ "CAP_NET_ADMIN"
|
||||
+ # failed to pull up wgInterface [wt-priv]: failed to create ipv4 raw socket: socket: operation not permitted
|
||||
+ "CAP_NET_RAW"
|
||||
+ ]
|
||||
+ # required for eBPF filter, used to be subset of CAP_SYS_ADMIN
|
||||
+ ++ optional (versionAtLeast kernel.version "5.8") "CAP_BPF"
|
||||
+ ++ optional (versionOlder kernel.version "5.8") "CAP_SYS_ADMIN"
|
||||
+ ;
|
||||
+ };
|
||||
+ }));
|
||||
+
|
||||
+ # see https://github.com/systemd/systemd/blob/17f3e91e8107b2b29fe25755651b230bbc81a514/src/resolve/org.freedesktop.resolve1.policy#L43-L43
|
||||
+ security.polkit.extraConfig = mkIf config.services.resolved.enable ''
|
||||
+ // systemd-resolved access for Netbird clients
|
||||
+ polkit.addRule(function(action, subject) {
|
||||
+ var actions = [
|
||||
+ "org.freedesktop.resolve1.set-dns-servers",
|
||||
+ "org.freedesktop.resolve1.set-domains",
|
||||
+ ];
|
||||
+ var users = ${builtins.toJSON (toHardenedClientList (client: "netbird-${client.name}"))};
|
||||
+
|
||||
+ if (actions.indexOf(action.id) >= 0 && users.indexOf(subject.user) >= 0 ) {
|
||||
+ return polkit.Result.YES;
|
||||
+ }
|
||||
+ });
|
||||
+ '';
|
||||
})
|
||||
+ # migration & temporary fixups section
|
||||
+ {
|
||||
+ systemd.services = toClientAttrs (client: nameValuePair "netbird-${client.name}" {
|
||||
+ preStart = ''
|
||||
+ set -eEuo pipefail
|
||||
+ ${optionalString (client.logLevel == "trace" || client.logLevel == "debug") "set -x"}
|
||||
+
|
||||
+ PATH="${makeBinPath (with pkgs; [coreutils jq diffutils])}:$PATH"
|
||||
+ export ${toShellVars client.environment}
|
||||
+
|
||||
+ # merge /etc/netbird-${client.name}/config.d' into "$NB_CONFIG"
|
||||
+ {
|
||||
+ test -e "$NB_CONFIG" || echo -n '{}' > "$NB_CONFIG"
|
||||
+
|
||||
+ # merge config.d with "$NB_CONFIG" into "$NB_CONFIG.new"
|
||||
+ jq -sS 'reduce .[] as $i ({}; . * $i)' \
|
||||
+ "$NB_CONFIG" \
|
||||
+ /etc/netbird-${client.name}/config.d/*.json \
|
||||
+ > "$NB_CONFIG.new"
|
||||
+
|
||||
+ echo "Comparing $NB_CONFIG with $NB_CONFIG.new ..."
|
||||
+ if ! diff <(jq -S <"$NB_CONFIG") "$NB_CONFIG.new" ; then
|
||||
+ echo "Updating $NB_CONFIG ..."
|
||||
+ mv "$NB_CONFIG.new" "$NB_CONFIG"
|
||||
+ else
|
||||
+ echo "Files are the same, not doing anything."
|
||||
+ rm "$NB_CONFIG.new"
|
||||
+ fi
|
||||
+ }
|
||||
+ '';
|
||||
+ });
|
||||
+ }
|
||||
];
|
||||
}
|
816
patches/netbird-24.11.patch
Normal file
816
patches/netbird-24.11.patch
Normal file
@ -0,0 +1,816 @@
|
||||
From dc09dca1f66c940060825868dbeeeaa865c79744 Mon Sep 17 00:00:00 2001
|
||||
From: Krzysztof Nazarewski <gpg@kdn.im>
|
||||
Date: Tue, 2 Apr 2024 12:04:11 +0200
|
||||
Subject: [PATCH 1/2] netbird-ui: fix incorrect meta.mainProgram
|
||||
|
||||
---
|
||||
pkgs/tools/networking/netbird/default.nix | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/pkgs/tools/networking/netbird/default.nix b/pkgs/tools/networking/netbird/default.nix
|
||||
index b10663216e035b..905247c2d4bdc1 100644
|
||||
--- a/pkgs/tools/networking/netbird/default.nix
|
||||
+++ b/pkgs/tools/networking/netbird/default.nix
|
||||
@@ -111,6 +111,6 @@ buildGoModule rec {
|
||||
description = "Connect your devices into a single secure private WireGuard®-based mesh network with SSO/MFA and simple access controls";
|
||||
license = licenses.bsd3;
|
||||
maintainers = with maintainers; [ misuzu ];
|
||||
- mainProgram = "netbird";
|
||||
+ mainProgram = if ui then "netbird-ui" else "netbird";
|
||||
};
|
||||
}
|
||||
|
||||
From 835617072b8bc1ffe1be551696d9e8d2ce193a60 Mon Sep 17 00:00:00 2001
|
||||
From: Krzysztof Nazarewski <gpg@kdn.im>
|
||||
Date: Tue, 2 Apr 2024 12:01:25 +0200
|
||||
Subject: [PATCH 2/2] nixos/netbird: harden and extend options
|
||||
|
||||
---
|
||||
.../manual/release-notes/rl-2405.section.md | 2 +-
|
||||
.../manual/release-notes/rl-2411.section.md | 3 +
|
||||
nixos/modules/services/networking/netbird.md | 72 ++-
|
||||
nixos/modules/services/networking/netbird.nix | 507 +++++++++++++++---
|
||||
nixos/tests/netbird.nix | 26 +-
|
||||
5 files changed, 503 insertions(+), 107 deletions(-)
|
||||
|
||||
diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md
|
||||
index b1b18b35e9c281..096bd6a2f2cc15 100644
|
||||
--- a/nixos/doc/manual/release-notes/rl-2405.section.md
|
||||
+++ b/nixos/doc/manual/release-notes/rl-2405.section.md
|
||||
@@ -698,7 +698,7 @@ Use `services.pipewire.extraConfig` or `services.pipewire.configPackages` for Pi
|
||||
and `services.kavita.settings.IpAddresses`. The file at `services.kavita.tokenKeyFile` now needs to contain a secret with
|
||||
512+ bits instead of 128+ bits.
|
||||
|
||||
-- `services.netbird` now allows running multiple tunnels in parallel through [`services.netbird.tunnels`](#opt-services.netbird.tunnels).
|
||||
+- `services.netbird` now allows running multiple tunnels in parallel through [`services.netbird.tunnels`](#opt-services.netbird.clients).
|
||||
|
||||
- `services.nginx.virtualHosts` using `forceSSL` or
|
||||
`globalRedirect` can now have redirect codes other than 301 through `redirectCode`.
|
||||
diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md
|
||||
index 2de4cf4d08af2d..a5d3566fe9bd87 100644
|
||||
--- a/nixos/doc/manual/release-notes/rl-2411.section.md
|
||||
+++ b/nixos/doc/manual/release-notes/rl-2411.section.md
|
||||
@@ -98,6 +98,9 @@
|
||||
support, which is the intended default behavior by Tracy maintainers.
|
||||
X11 users have to switch to the new package `tracy-x11`.
|
||||
|
||||
+- `services.netbird.tunnels` was renamed to [`services.netbird.clients`](#opt-services.netbird.clients),
|
||||
+ hardened (using dedicated less-privileged users) and significantly extended.
|
||||
+
|
||||
## Other Notable Changes {#sec-release-24.11-notable-changes}
|
||||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
diff --git a/nixos/modules/services/networking/netbird.md b/nixos/modules/services/networking/netbird.md
|
||||
index e1f6753cbd30cc..876c27cb0d22e7 100644
|
||||
--- a/nixos/modules/services/networking/netbird.md
|
||||
+++ b/nixos/modules/services/networking/netbird.md
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Quickstart {#module-services-netbird-quickstart}
|
||||
|
||||
-The absolute minimal configuration for the netbird daemon looks like this:
|
||||
+The absolute minimal configuration for the Netbird client daemon looks like this:
|
||||
|
||||
```nix
|
||||
{
|
||||
@@ -13,52 +13,76 @@ The absolute minimal configuration for the netbird daemon looks like this:
|
||||
This will set up a netbird service listening on the port `51820` associated to the
|
||||
`wt0` interface.
|
||||
|
||||
-It is strictly equivalent to setting:
|
||||
+Which is equivalent to:
|
||||
|
||||
```nix
|
||||
{
|
||||
- services.netbird.tunnels.wt0.stateDir = "netbird";
|
||||
+ services.netbird.clients.wt0 = {
|
||||
+ port = 51820;
|
||||
+ name = "netbird";
|
||||
+ interface = "wt0";
|
||||
+ hardened = false;
|
||||
+ };
|
||||
}
|
||||
```
|
||||
|
||||
-The `enable` option is mainly kept for backward compatibility, as defining netbird
|
||||
-tunnels through the `tunnels` option is more expressive.
|
||||
+This will set up a `netbird.service` listening on the port `51820` associated to the
|
||||
+`wt0` interface. There will also be `netbird-wt0` binary installed in addition to `netbird`.
|
||||
+
|
||||
+see [clients](#opt-services.netbird.clients) option documentation for more details.
|
||||
|
||||
## Multiple connections setup {#module-services-netbird-multiple-connections}
|
||||
|
||||
-Using the `services.netbird.tunnels` option, it is also possible to define more than
|
||||
+Using the `services.netbird.clients` option, it is 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`.
|
||||
+You must at least define a `port` for the service to listen on, the rest is optional:
|
||||
|
||||
```nix
|
||||
{
|
||||
- services.netbird.tunnels = {
|
||||
- wt1 = {
|
||||
- port = 51830;
|
||||
- };
|
||||
- };
|
||||
+ services.netbird.clients.wt1.port = 51830;
|
||||
+ services.netbird.clients.wt2.port = 51831;
|
||||
}
|
||||
```
|
||||
|
||||
-To interact with it, you will need to specify the correct daemon address:
|
||||
-
|
||||
-```bash
|
||||
-netbird --daemon-addr unix:///var/run/netbird-wt1/sock ...
|
||||
-```
|
||||
+see [clients](#opt-services.netbird.clients) option documentation for more details.
|
||||
|
||||
-The address will by default be `unix:///var/run/netbird-<name>`.
|
||||
+## Exposing services internally on the Netbird network {#module-services-netbird-firewall}
|
||||
|
||||
-It is also possible to overwrite default options passed to the service, for
|
||||
-example:
|
||||
+You can easily expose services exclusively to Netbird network by combining
|
||||
+[`networking.firewall.interfaces`](#opt-networking.firewall.interfaces) rules
|
||||
+with [`interface`](#opt-services.netbird.clients._name_.interface) names:
|
||||
|
||||
```nix
|
||||
{
|
||||
- services.netbird.tunnels.wt1.environment = {
|
||||
- NB_DAEMON_ADDR = "unix:///var/run/toto.sock";
|
||||
+ services.netbird.clients.priv.port = 51819;
|
||||
+ services.netbird.clients.work.port = 51818;
|
||||
+ networking.firewall.interfaces = {
|
||||
+ "${config.services.netbird.clients.priv.interface}" = {
|
||||
+ allowedUDPPorts = [ 1234 ];
|
||||
+ };
|
||||
+ "${config.services.netbird.clients.work.interface}" = {
|
||||
+ allowedTCPPorts = [ 8080 ];
|
||||
+ };
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
-This will set the socket to interact with the netbird service to `/var/run/toto.sock`.
|
||||
+### Additional customizations {#module-services-netbird-customization}
|
||||
+
|
||||
+Each Netbird client service by default:
|
||||
+
|
||||
+- runs in a [hardened](#opt-services.netbird.clients._name_.hardened) mode,
|
||||
+- starts with the system,
|
||||
+- [opens up a firewall](#opt-services.netbird.clients._name_.openFirewall) for direct (without TURN servers)
|
||||
+ peer-to-peer communication,
|
||||
+- can be additionally configured with environment variables,
|
||||
+- automatically determines whether `netbird-ui-<name>` should be available,
|
||||
+
|
||||
+[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.
|
||||
+See the option description for more information.
|
||||
+
|
||||
+[environment](#opt-services.netbird.clients._name_.environment) allows you to pass additional configurations
|
||||
+through environment variables, but special care needs to be taken for overriding config location and
|
||||
+daemon address due [hardened](#opt-services.netbird.clients._name_.hardened) option.
|
||||
diff --git a/nixos/modules/services/networking/netbird.nix b/nixos/modules/services/networking/netbird.nix
|
||||
index e68c39946fe3b5..0160a8964aecad 100644
|
||||
--- a/nixos/modules/services/networking/netbird.nix
|
||||
+++ b/nixos/modules/services/networking/netbird.nix
|
||||
@@ -1,72 +1,155 @@
|
||||
-{
|
||||
- config,
|
||||
- lib,
|
||||
- pkgs,
|
||||
- ...
|
||||
+{ config
|
||||
+, lib
|
||||
+, pkgs
|
||||
+, ...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
- attrNames
|
||||
+ attrValues
|
||||
+ concatLists
|
||||
+ concatStringsSep
|
||||
+ escapeShellArgs
|
||||
+ filterAttrs
|
||||
getExe
|
||||
literalExpression
|
||||
maintainers
|
||||
+ makeBinPath
|
||||
mapAttrs'
|
||||
+ mapAttrsToList
|
||||
mkDefault
|
||||
- mkEnableOption
|
||||
mkIf
|
||||
mkMerge
|
||||
mkOption
|
||||
+ mkOptionDefault
|
||||
mkPackageOption
|
||||
+ mkRemovedOptionModule
|
||||
nameValuePair
|
||||
optional
|
||||
+ optionalString
|
||||
+ toShellVars
|
||||
+ versionAtLeast
|
||||
versionOlder
|
||||
;
|
||||
|
||||
inherit (lib.types)
|
||||
attrsOf
|
||||
+ bool
|
||||
+ enum
|
||||
+ package
|
||||
port
|
||||
str
|
||||
submodule
|
||||
;
|
||||
|
||||
- kernel = config.boot.kernelPackages;
|
||||
+ inherit (config.boot) kernelPackages;
|
||||
+ inherit (config.boot.kernelPackages) kernel;
|
||||
|
||||
cfg = config.services.netbird;
|
||||
+
|
||||
+ toClientList = fn: map fn (attrValues cfg.clients);
|
||||
+ toClientAttrs = fn: mapAttrs' (_: fn) cfg.clients;
|
||||
+
|
||||
+ hardenedClients = filterAttrs (_: client: client.hardened) cfg.clients;
|
||||
+ toHardenedClientList = fn: map fn (attrValues hardenedClients);
|
||||
+ toHardenedClientAttrs = fn: mapAttrs' (_: fn) hardenedClients;
|
||||
+
|
||||
+ nixosConfig = config;
|
||||
in
|
||||
{
|
||||
meta.maintainers = with maintainers; [
|
||||
misuzu
|
||||
+ nazarewk
|
||||
];
|
||||
meta.doc = ./netbird.md;
|
||||
|
||||
+ imports = [
|
||||
+ (mkRemovedOptionModule [ "services" "netbird" "tunnels" ]
|
||||
+ "The option `services.netbird.tunnels` has been renamed to `services.netbird.clients`")
|
||||
+ ];
|
||||
+
|
||||
options.services.netbird = {
|
||||
- enable = mkEnableOption "Netbird daemon";
|
||||
+ enable = mkOption {
|
||||
+ type = bool;
|
||||
+ default = false;
|
||||
+ description = ''
|
||||
+ Enables backwards compatible Netbird client service.
|
||||
+
|
||||
+ This is strictly equivalent to:
|
||||
+
|
||||
+ ```nix
|
||||
+ services.netbird.clients.wt0 = {
|
||||
+ port = 51820;
|
||||
+ name = "netbird";
|
||||
+ interface = "wt0";
|
||||
+ hardened = false;
|
||||
+ };
|
||||
+ ```
|
||||
+ '';
|
||||
+ };
|
||||
package = mkPackageOption pkgs "netbird" { };
|
||||
|
||||
- tunnels = mkOption {
|
||||
+ ui.enable = mkOption {
|
||||
+ type = bool;
|
||||
+ default = config.services.displayManager.sessionPackages != [ ] || config.services.xserver.enable;
|
||||
+ defaultText = literalExpression ''
|
||||
+ config.services.displayManager.sessionPackages != [ ] || config.services.xserver.enable
|
||||
+ '';
|
||||
+ description = ''
|
||||
+ Controls presence `netbird-ui` wrappers, defaults to presence of graphical sessions.
|
||||
+ '';
|
||||
+ };
|
||||
+ ui.package = mkPackageOption pkgs "netbird-ui" { };
|
||||
+
|
||||
+ clients = mkOption {
|
||||
type = attrsOf (
|
||||
submodule (
|
||||
{ name, config, ... }:
|
||||
+ let client = config; in
|
||||
{
|
||||
options = {
|
||||
port = mkOption {
|
||||
type = port;
|
||||
- default = 51820;
|
||||
+ example = literalExpression "51820";
|
||||
description = ''
|
||||
- Port for the ${name} netbird interface.
|
||||
+ Port the Netbird client listens on.
|
||||
'';
|
||||
};
|
||||
|
||||
+ name = mkOption {
|
||||
+ type = str;
|
||||
+ default = name;
|
||||
+ description = ''
|
||||
+ Primary name for use (as a suffix) in:
|
||||
+ - systemd service name,
|
||||
+ - hardened user name and group,
|
||||
+ - [systemd `*Directory=`](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#RuntimeDirectory=) names,
|
||||
+ - desktop application identification,
|
||||
+ '';
|
||||
+ };
|
||||
+
|
||||
+ interface = mkOption {
|
||||
+ type = str;
|
||||
+ default = "nb-${client.name}";
|
||||
+ description = ''
|
||||
+ Name of the network interface managed by this client.
|
||||
+ '';
|
||||
+ apply = iface:
|
||||
+ lib.throwIfNot (builtins.stringLength iface <= 15) "Network interface name must be 15 characters or less"
|
||||
+ iface;
|
||||
+ };
|
||||
+
|
||||
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}"
|
||||
+ NB_CONFIG = "/var/lib/netbird-''${client.name}/config.json";
|
||||
+ NB_DAEMON_ADDR = "unix:///var/run/netbird-''${client.name}/sock";
|
||||
+ NB_INTERFACE_NAME = config.interface;
|
||||
+ NB_LOG_FILE = mkOptionDefault "console";
|
||||
+ NB_LOG_LEVEL = config.logLevel;
|
||||
+ NB_SERVICE = "netbird-''${client.name}";
|
||||
+ NB_WIREGUARD_PORT = toString config.port;
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
@@ -74,97 +157,361 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
- stateDir = mkOption {
|
||||
- type = str;
|
||||
- default = "netbird-${name}";
|
||||
+ autoStart = mkOption {
|
||||
+ type = bool;
|
||||
+ default = true;
|
||||
+ description = ''
|
||||
+ Start the service with the system.
|
||||
+
|
||||
+ As of 2024-02-13 it is not possible to start a Netbird client daemon without immediately
|
||||
+ connecting to the network, but it is [planned for a near future](https://github.com/netbirdio/netbird/projects/2#card-91718018).
|
||||
+ '';
|
||||
+ };
|
||||
+
|
||||
+ openFirewall = mkOption {
|
||||
+ type = bool;
|
||||
+ default = true;
|
||||
+ description = ''
|
||||
+ Opens up firewall `port` for communication between Netbird peers directly over LAN or public IP,
|
||||
+ without using (internet-hosted) TURN servers as intermediaries.
|
||||
+ '';
|
||||
+ };
|
||||
+
|
||||
+ hardened = mkOption {
|
||||
+ type = bool;
|
||||
+ default = true;
|
||||
description = ''
|
||||
- Directory storing the netbird configuration.
|
||||
+ Hardened service:
|
||||
+ - runs as a dedicated user with minimal set of permissions (see caveats),
|
||||
+ - restricts daemon configuration socket access to dedicated user group
|
||||
+ (you can grant access to it with `users.users."<user>".extraGroups = [ "netbird-${client.name}" ]`),
|
||||
+
|
||||
+ Even though the local system resources access is restricted:
|
||||
+ - `CAP_NET_RAW`, `CAP_NET_ADMIN` and `CAP_BPF` still give unlimited network manipulation possibilites,
|
||||
+ - older kernels don't have `CAP_BPF` and use `CAP_SYS_ADMIN` instead,
|
||||
+
|
||||
+ Known security features that are not (yet) integrated into the module:
|
||||
+ - 2024-02-14: `rosenpass` is an experimental feature configurable solely
|
||||
+ through `--enable-rosenpass` flag on the `netbird up` command,
|
||||
+ see [the docs](https://docs.netbird.io/how-to/enable-post-quantum-cryptography)
|
||||
+ '';
|
||||
+ };
|
||||
+
|
||||
+ logLevel = mkOption {
|
||||
+ type = enum [
|
||||
+ # logrus loglevels
|
||||
+ "panic"
|
||||
+ "fatal"
|
||||
+ "error"
|
||||
+ "warn"
|
||||
+ "warning"
|
||||
+ "info"
|
||||
+ "debug"
|
||||
+ "trace"
|
||||
+ ];
|
||||
+ default = "info";
|
||||
+ description = "Log level of the Netbird daemon.";
|
||||
+ };
|
||||
+
|
||||
+ ui.enable = mkOption {
|
||||
+ type = bool;
|
||||
+ default = nixosConfig.services.netbird.ui.enable;
|
||||
+ defaultText = literalExpression ''config.ui.enable'';
|
||||
+ description = ''
|
||||
+ Controls presence of `netbird-ui` wrapper for this Netbird client.
|
||||
+ '';
|
||||
+ };
|
||||
+
|
||||
+ wrapper = mkOption {
|
||||
+ type = package;
|
||||
+ internal = true;
|
||||
+ default =
|
||||
+ let
|
||||
+ makeWrapperArgs = concatLists (mapAttrsToList
|
||||
+ (key: value: [ "--set-default" key value ])
|
||||
+ config.environment
|
||||
+ );
|
||||
+ in
|
||||
+ pkgs.stdenv.mkDerivation {
|
||||
+ name = "${cfg.package.name}-wrapper-${client.name}";
|
||||
+ meta.mainProgram = "netbird-${client.name}";
|
||||
+ nativeBuildInputs = with pkgs; [ makeWrapper ];
|
||||
+ phases = [ "installPhase" ];
|
||||
+ installPhase = concatStringsSep "\n" [
|
||||
+ ''
|
||||
+ mkdir -p "$out/bin"
|
||||
+ makeWrapper ${lib.getExe cfg.package} "$out/bin/netbird-${client.name}" \
|
||||
+ ${escapeShellArgs makeWrapperArgs}
|
||||
+ ''
|
||||
+ (optionalString cfg.ui.enable ''
|
||||
+ # netbird-ui doesn't support envvars
|
||||
+ makeWrapper ${lib.getExe cfg.ui.package} "$out/bin/netbird-ui-${client.name}" \
|
||||
+ --add-flags '--daemon-addr=${config.environment.NB_DAEMON_ADDR}'
|
||||
+
|
||||
+ mkdir -p "$out/share/applications"
|
||||
+ substitute ${cfg.ui.package}/share/applications/netbird.desktop \
|
||||
+ "$out/share/applications/netbird-${client.name}.desktop" \
|
||||
+ --replace-fail 'Name=Netbird' "Name=Netbird @ netbird-${client.name}" \
|
||||
+ --replace-fail '${lib.getExe cfg.ui.package}' "$out/bin/netbird-ui-${client.name}"
|
||||
+ '')
|
||||
+ ];
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ # see https://github.com/netbirdio/netbird/blob/88747e3e0191abc64f1e8c7ecc65e5e50a1527fd/client/internal/config.go#L49-L82
|
||||
+ config = mkOption {
|
||||
+ type = (pkgs.formats.json { }).type;
|
||||
+ defaultText = literalExpression ''
|
||||
+ {
|
||||
+ DisableAutoConnect = !config.autoStart;
|
||||
+ WgIface = config.interface;
|
||||
+ WgPort = config.port;
|
||||
+ }
|
||||
+ '';
|
||||
+ description = ''
|
||||
+ Additional configuration that exists before the first start and
|
||||
+ later overrides the existing values in `config.json`.
|
||||
+
|
||||
+ It is mostly helpful to manage configuration ignored/not yet implemented
|
||||
+ outside of `netbird up` invocation.
|
||||
+
|
||||
+ WARNING: this is not an upstream feature, it could break in the future
|
||||
+ (by having lower priority) after upstream implements an equivalent.
|
||||
+
|
||||
+ It is implemented as a `preStart` script which overrides `config.json`
|
||||
+ with content of `/etc/netbird-${client.name}/config.d/*.json` files.
|
||||
+ This option manages specifically `50-nixos.json` file.
|
||||
+
|
||||
+ Consult [the source code](https://github.com/netbirdio/netbird/blob/88747e3e0191abc64f1e8c7ecc65e5e50a1527fd/client/internal/config.go#L49-L82)
|
||||
+ or inspect existing file for a complete list of available configurations.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
- 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";
|
||||
+ config.environment = {
|
||||
+ NB_CONFIG = "/var/lib/netbird-${client.name}/config.json";
|
||||
+ NB_DAEMON_ADDR = "unix:///var/run/netbird-${client.name}/sock";
|
||||
+ NB_INTERFACE_NAME = config.interface;
|
||||
+ NB_LOG_FILE = mkOptionDefault "console";
|
||||
+ NB_LOG_LEVEL = config.logLevel;
|
||||
+ NB_SERVICE = "netbird-${client.name}";
|
||||
+ NB_WIREGUARD_PORT = toString config.port;
|
||||
+ };
|
||||
+
|
||||
+ config.config = {
|
||||
+ DisableAutoConnect = !config.autoStart;
|
||||
+ WgIface = config.interface;
|
||||
+ WgPort = config.port;
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
default = { };
|
||||
description = ''
|
||||
- Attribute set of Netbird tunnels, each one will spawn a daemon listening on ...
|
||||
+ Attribute set of Netbird client daemons, by default each one will:
|
||||
+
|
||||
+ 1. be manageable using dedicated tooling:
|
||||
+ - `netbird-<name>` script,
|
||||
+ - `Netbird - netbird-<name>` graphical interface when appropriate (see `ui.enable`),
|
||||
+ 2. run as a `netbird-<name>.service`,
|
||||
+ 3. listen for incoming remote connections on the port `51820` (`openFirewall` by default),
|
||||
+ 4. manage the `netbird-<name>` wireguard interface,
|
||||
+ 5. use the `/var/lib/netbird-<name>/config.json` configuration file,
|
||||
+ 6. override `/var/lib/netbird-<name>/config.json` with values from `/etc/netbird-<name>/config.d/*.json`,
|
||||
+ 7. (`hardened`) be locally manageable by `netbird-<name>` system group,
|
||||
+
|
||||
+ With following caveats:
|
||||
+
|
||||
+ - multiple daemons will interfere with each other's DNS resolution of `netbird.cloud`, but
|
||||
+ should remain fully operational otherwise.
|
||||
+ Setting up custom (non-conflicting) DNS zone is currently possible only when self-hosting.
|
||||
+ '';
|
||||
+ example = lib.literalExpression ''
|
||||
+ {
|
||||
+ services.netbird.clients.wt0.port = 51820;
|
||||
+ services.netbird.clients.personal.port = 51821;
|
||||
+ services.netbird.clients.work1.port = 51822;
|
||||
+ }
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkMerge [
|
||||
- (mkIf cfg.enable {
|
||||
- # For backwards compatibility
|
||||
- services.netbird.tunnels.wt0.stateDir = "netbird";
|
||||
- })
|
||||
+ (mkIf cfg.enable (
|
||||
+ let name = "wt0"; client = cfg.clients."${name}"; in {
|
||||
+ services.netbird.clients."${name}" = {
|
||||
+ port = mkDefault 51820;
|
||||
+ name = mkDefault "netbird";
|
||||
+ interface = mkDefault "wt0";
|
||||
+ hardened = mkDefault false;
|
||||
+ };
|
||||
|
||||
- (mkIf (cfg.tunnels != { }) {
|
||||
- boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard;
|
||||
+ environment.systemPackages = [
|
||||
+ (lib.hiPrio (pkgs.runCommand "${client.name}-as-default" { } ''
|
||||
+ mkdir -p "$out/bin"
|
||||
+ for binary in netbird ${optionalString cfg.ui.enable "netbird-ui"} ; do
|
||||
+ ln -s "${client.wrapper}/bin/$binary-${client.name}" "$out/bin/$binary"
|
||||
+ done
|
||||
+ ''))
|
||||
+ ];
|
||||
+ }
|
||||
+ ))
|
||||
+ {
|
||||
+ boot.extraModulePackages = optional
|
||||
+ (cfg.clients != { } && (versionOlder kernel.version "5.6"))
|
||||
+ kernelPackages.wireguard;
|
||||
|
||||
- environment.systemPackages = [ cfg.package ];
|
||||
+ environment.systemPackages =
|
||||
+ toClientList (client: client.wrapper)
|
||||
+ # omitted due to https://github.com/netbirdio/netbird/issues/1562
|
||||
+ #++ optional (cfg.clients != { }) cfg.package
|
||||
+ # omitted due to https://github.com/netbirdio/netbird/issues/1581
|
||||
+ #++ optional (cfg.clients != { } && cfg.ui.enable) cfg.ui.package
|
||||
+ ;
|
||||
|
||||
- networking.dhcpcd.denyInterfaces = attrNames cfg.tunnels;
|
||||
+ networking.dhcpcd.denyInterfaces = toClientList (client: client.interface);
|
||||
+ networking.networkmanager.unmanaged = toClientList (client: "interface-name:${client.interface}");
|
||||
|
||||
- systemd.network.networks = mkIf config.networking.useNetworkd (
|
||||
- mapAttrs'
|
||||
- (
|
||||
- name: _:
|
||||
- nameValuePair "50-netbird-${name}" {
|
||||
- matchConfig = {
|
||||
- Name = name;
|
||||
- };
|
||||
- linkConfig = {
|
||||
- Unmanaged = true;
|
||||
- ActivationPolicy = "manual";
|
||||
- };
|
||||
- }
|
||||
- )
|
||||
- cfg.tunnels
|
||||
- );
|
||||
+ networking.firewall.allowedUDPPorts = concatLists (toClientList (client: optional client.openFirewall client.port));
|
||||
|
||||
- systemd.services =
|
||||
- mapAttrs'
|
||||
- (
|
||||
- name:
|
||||
- { environment, stateDir, ... }:
|
||||
- nameValuePair "netbird-${name}" {
|
||||
- description = "A WireGuard-based mesh network that connects your devices into a single private network";
|
||||
+ systemd.network.networks = mkIf config.networking.useNetworkd (toClientAttrs (client:
|
||||
+ nameValuePair "50-netbird-${client.interface}" {
|
||||
+ matchConfig = {
|
||||
+ Name = client.interface;
|
||||
+ };
|
||||
+ linkConfig = {
|
||||
+ Unmanaged = true;
|
||||
+ ActivationPolicy = "manual";
|
||||
+ };
|
||||
+ }
|
||||
+ ));
|
||||
|
||||
- documentation = [ "https://netbird.io/docs/" ];
|
||||
+ environment.etc = toClientAttrs (client: nameValuePair "netbird-${client.name}/config.d/50-nixos.json" {
|
||||
+ text = builtins.toJSON client.config;
|
||||
+ mode = "0444";
|
||||
+ });
|
||||
|
||||
- after = [ "network.target" ];
|
||||
- wantedBy = [ "multi-user.target" ];
|
||||
+ systemd.services = toClientAttrs (client: nameValuePair "netbird-${client.name}" {
|
||||
+ description = "A WireGuard-based mesh network that connects your devices into a single private network";
|
||||
|
||||
- path = with pkgs; [ openresolv ];
|
||||
+ documentation = [ "https://netbird.io/docs/" ];
|
||||
|
||||
- inherit environment;
|
||||
+ after = [ "network.target" ];
|
||||
+ wantedBy = [ "multi-user.target" ];
|
||||
|
||||
- serviceConfig = {
|
||||
- ExecStart = "${getExe cfg.package} service run";
|
||||
- Restart = "always";
|
||||
- RuntimeDirectory = stateDir;
|
||||
- StateDirectory = stateDir;
|
||||
- StateDirectoryMode = "0700";
|
||||
- WorkingDirectory = "/var/lib/${stateDir}";
|
||||
- };
|
||||
+ path = optional (!config.services.resolved.enable) pkgs.openresolv;
|
||||
|
||||
- unitConfig = {
|
||||
- StartLimitInterval = 5;
|
||||
- StartLimitBurst = 10;
|
||||
- };
|
||||
+ serviceConfig = {
|
||||
+ ExecStart = "${getExe client.wrapper} service run";
|
||||
+ Restart = "always";
|
||||
+
|
||||
+ RuntimeDirectory = "netbird-${client.name}";
|
||||
+ RuntimeDirectoryMode = mkDefault "0755";
|
||||
+ ConfigurationDirectory = "netbird-${client.name}";
|
||||
+ StateDirectory = "netbird-${client.name}";
|
||||
+ StateDirectoryMode = "0700";
|
||||
+
|
||||
+ WorkingDirectory = "/var/lib/netbird-${client.name}";
|
||||
+ };
|
||||
+
|
||||
+ unitConfig = {
|
||||
+ StartLimitInterval = 5;
|
||||
+ StartLimitBurst = 10;
|
||||
+ };
|
||||
+
|
||||
+ stopIfChanged = false;
|
||||
+ });
|
||||
+ }
|
||||
+ # Hardening section
|
||||
+ (mkIf (hardenedClients != { }) {
|
||||
+ users.groups = toHardenedClientAttrs (client: nameValuePair "netbird-${client.name}" { });
|
||||
+ users.users = toHardenedClientAttrs (client: nameValuePair "netbird-${client.name}" {
|
||||
+ isSystemUser = true;
|
||||
+ home = "/var/lib/netbird-${client.name}";
|
||||
+ group = "netbird-${client.name}";
|
||||
+ });
|
||||
+
|
||||
+ systemd.services = toHardenedClientAttrs (client: nameValuePair "netbird-${client.name}" (mkIf client.hardened {
|
||||
+ serviceConfig = {
|
||||
+ RuntimeDirectoryMode = "0750";
|
||||
+
|
||||
+ User = "netbird-${client.name}";
|
||||
+ Group = "netbird-${client.name}";
|
||||
+
|
||||
+ # settings implied by DynamicUser=true, without actully using it,
|
||||
+ # see https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#DynamicUser=
|
||||
+ RemoveIPC = true;
|
||||
+ PrivateTmp = true;
|
||||
+ ProtectSystem = "strict";
|
||||
+ ProtectHome = "yes";
|
||||
|
||||
- stopIfChanged = false;
|
||||
- }
|
||||
- )
|
||||
- cfg.tunnels;
|
||||
+ AmbientCapabilities = [
|
||||
+ # see https://man7.org/linux/man-pages/man7/capabilities.7.html
|
||||
+ # see https://docs.netbird.io/how-to/installation#running-net-bird-in-docker
|
||||
+ #
|
||||
+ # seems to work fine without CAP_SYS_ADMIN and CAP_SYS_RESOURCE
|
||||
+ # CAP_NET_BIND_SERVICE could be added to allow binding on low ports, but is not required,
|
||||
+ # see https://github.com/netbirdio/netbird/pull/1513
|
||||
+
|
||||
+ # failed creating tunnel interface wt-priv: [operation not permitted
|
||||
+ "CAP_NET_ADMIN"
|
||||
+ # failed to pull up wgInterface [wt-priv]: failed to create ipv4 raw socket: socket: operation not permitted
|
||||
+ "CAP_NET_RAW"
|
||||
+ ]
|
||||
+ # required for eBPF filter, used to be subset of CAP_SYS_ADMIN
|
||||
+ ++ optional (versionAtLeast kernel.version "5.8") "CAP_BPF"
|
||||
+ ++ optional (versionOlder kernel.version "5.8") "CAP_SYS_ADMIN"
|
||||
+ ;
|
||||
+ };
|
||||
+ }));
|
||||
+
|
||||
+ # see https://github.com/systemd/systemd/blob/17f3e91e8107b2b29fe25755651b230bbc81a514/src/resolve/org.freedesktop.resolve1.policy#L43-L43
|
||||
+ security.polkit.extraConfig = mkIf config.services.resolved.enable ''
|
||||
+ // systemd-resolved access for Netbird clients
|
||||
+ polkit.addRule(function(action, subject) {
|
||||
+ var actions = [
|
||||
+ "org.freedesktop.resolve1.set-dns-servers",
|
||||
+ "org.freedesktop.resolve1.set-domains",
|
||||
+ ];
|
||||
+ var users = ${builtins.toJSON (toHardenedClientList (client: "netbird-${client.name}"))};
|
||||
+
|
||||
+ if (actions.indexOf(action.id) >= 0 && users.indexOf(subject.user) >= 0 ) {
|
||||
+ return polkit.Result.YES;
|
||||
+ }
|
||||
+ });
|
||||
+ '';
|
||||
})
|
||||
+ # migration & temporary fixups section
|
||||
+ {
|
||||
+ systemd.services = toClientAttrs (client: nameValuePair "netbird-${client.name}" {
|
||||
+ preStart = ''
|
||||
+ set -eEuo pipefail
|
||||
+ ${optionalString (client.logLevel == "trace" || client.logLevel == "debug") "set -x"}
|
||||
+
|
||||
+ PATH="${makeBinPath (with pkgs; [coreutils jq diffutils])}:$PATH"
|
||||
+ export ${toShellVars client.environment}
|
||||
+
|
||||
+ # merge /etc/netbird-${client.name}/config.d' into "$NB_CONFIG"
|
||||
+ {
|
||||
+ test -e "$NB_CONFIG" || echo -n '{}' > "$NB_CONFIG"
|
||||
+
|
||||
+ # merge config.d with "$NB_CONFIG" into "$NB_CONFIG.new"
|
||||
+ jq -sS 'reduce .[] as $i ({}; . * $i)' \
|
||||
+ "$NB_CONFIG" \
|
||||
+ /etc/netbird-${client.name}/config.d/*.json \
|
||||
+ > "$NB_CONFIG.new"
|
||||
+
|
||||
+ echo "Comparing $NB_CONFIG with $NB_CONFIG.new ..."
|
||||
+ if ! diff <(jq -S <"$NB_CONFIG") "$NB_CONFIG.new" ; then
|
||||
+ echo "Updating $NB_CONFIG ..."
|
||||
+ mv "$NB_CONFIG.new" "$NB_CONFIG"
|
||||
+ else
|
||||
+ echo "Files are the same, not doing anything."
|
||||
+ rm "$NB_CONFIG.new"
|
||||
+ fi
|
||||
+ }
|
||||
+ '';
|
||||
+ });
|
||||
+ }
|
||||
];
|
||||
}
|
||||
diff --git a/nixos/tests/netbird.nix b/nixos/tests/netbird.nix
|
||||
index 7342e8d04a39c3..063fff6d42f031 100644
|
||||
--- a/nixos/tests/netbird.nix
|
||||
+++ b/nixos/tests/netbird.nix
|
||||
@@ -12,10 +12,32 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
|
||||
};
|
||||
};
|
||||
|
||||
+ # TODO: confirm the whole solution is working end-to-end when netbird server is implemented
|
||||
testScript = ''
|
||||
start_all()
|
||||
- node.wait_for_unit("netbird-wt0.service")
|
||||
+ node.wait_for_unit("netbird.service")
|
||||
node.wait_for_file("/var/run/netbird/sock")
|
||||
- node.succeed("netbird status | grep -q 'Daemon status: NeedsLogin'")
|
||||
+ output = node.succeed("netbird status")
|
||||
+ # used to print `Daemon status: NeedsLogin`, but not anymore `Management: Disconnected`
|
||||
+ assert "Disconnected" in output or "NeedsLogin" in output
|
||||
'';
|
||||
+
|
||||
+ /*
|
||||
+ `netbird status` used to print `Daemon status: NeedsLogin`
|
||||
+ https://github.com/netbirdio/netbird/blob/23a14737974e3849fa86408d136cc46db8a885d0/client/cmd/status.go#L154-L164
|
||||
+ as the first line, but now it is just:
|
||||
+
|
||||
+ Daemon version: 0.26.3
|
||||
+ CLI version: 0.26.3
|
||||
+ Management: Disconnected
|
||||
+ Signal: Disconnected
|
||||
+ Relays: 0/0 Available
|
||||
+ Nameservers: 0/0 Available
|
||||
+ FQDN:
|
||||
+ NetBird IP: N/A
|
||||
+ Interface type: N/A
|
||||
+ Quantum resistance: false
|
||||
+ Routes: -
|
||||
+ Peers count: 0/0 Connected
|
||||
+ */
|
||||
})
|
Loading…
x
Reference in New Issue
Block a user