diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md index 158257f20a7a7..a8fc721e6416f 100644 --- a/nixos/doc/manual/release-notes/rl-2405.section.md +++ b/nixos/doc/manual/release-notes/rl-2405.section.md @@ -709,7 +709,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 1ec8c7ff90660..3fb434f92beda 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -285,6 +285,9 @@ Refer to upstream [upgrade instructions](https://goteleport.com/docs/management/operations/upgrading/) and [release notes for v16](https://goteleport.com/docs/changelog/#1600-061324). +- `services.netbird.tunnels` was renamed to [`services.netbird.clients`](#opt-services.netbird.clients), + hardened (using dedicated less-privileged users) and significantly extended. + - `tests.overriding` has its `passthru.tests` restructured as an attribute set instead of a list, making individual tests accessible by their names. - `vaultwarden` lost the capability to bind to privileged ports. If you rely on diff --git a/nixos/modules/services/networking/netbird.md b/nixos/modules/services/networking/netbird.md index e1f6753cbd30c..876c27cb0d22e 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-`. +## 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-` 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 d39c373dbc94c..3b57e55380518 100644 --- a/nixos/modules/services/networking/netbird.nix +++ b/nixos/modules/services/networking/netbird.nix @@ -1,70 +1,177 @@ -{ - 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 + optionalAttrs + optionalString + toShellVars + versionAtLeast versionOlder ; inherit (lib.types) attrsOf + bool + enum + nullOr + 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; [ ]; + meta.maintainers = with maintainers; [ + 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 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, + ''; + }; + + dns-resolver.address = mkOption { + type = nullOr str; + default = null; + example = "127.0.0.123"; + description = '' + An explicit address that Netbird will serve `*.netbird.cloud.` (usually) entries on. + + Netbird serves DNS on it's own (dynamic) client address by default. + ''; + }; + + dns-resolver.port = mkOption { + type = port; + default = 53; description = '' - Port for the ${name} netbird interface. + A port to serve DNS entries on when `dns-resolver.address` is enabled. ''; }; + 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 = client.interface; + NB_LOG_FILE = mkOptionDefault "console"; + NB_LOG_LEVEL = client.logLevel; + NB_SERVICE = "netbird-''${client.name}"; + NB_WIREGUARD_PORT = toString client.port; + } // optionalAttrs (client.dns-resolver.address != null) { + NB_DNS_RESOLVER_ADDRESS = "''${client.dns-resolver.address}:''${builtins.toString client.dns-resolver.port}"; } ''; description = '' @@ -72,97 +179,371 @@ 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 = '' + 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."".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 ''client.ui.enable''; description = '' - Directory storing the netbird configuration. + 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 ]) + client.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=${client.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 = !client.autoStart; + WgIface = client.interface; + WgPort = client.port; + } // optionalAttrs (client.dns-resolver.address != null) { + CustomDNSAddress = "''${client.dns-resolver.address}:''${builtins.toString client.dns-resolver.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 = { + NB_CONFIG = "/var/lib/netbird-${client.name}/config.json"; + NB_DAEMON_ADDR = "unix:///var/run/netbird-${client.name}/sock"; + NB_INTERFACE_NAME = client.interface; + NB_LOG_FILE = mkOptionDefault "console"; + NB_LOG_LEVEL = client.logLevel; + NB_SERVICE = "netbird-${client.name}"; + NB_WIREGUARD_PORT = toString client.port; + } // optionalAttrs (client.dns-resolver.address != null) { + NB_DNS_RESOLVER_ADDRESS = "${client.dns-resolver.address}:${builtins.toString client.dns-resolver.port}"; }; - 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.config = { + DisableAutoConnect = !client.autoStart; + WgIface = client.interface; + WgPort = client.port; + } // optionalAttrs (client.dns-resolver.address != null) { + CustomDNSAddress = "${client.dns-resolver.address}:${builtins.toString client.dns-resolver.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-` script, + - `Netbird - netbird-` graphical interface when appropriate (see `ui.enable`), + 2. run as a `netbird-.service`, + 3. listen for incoming remote connections on the port `51820` (`openFirewall` by default), + 4. manage the `netbird-` wireguard interface, + 5. use the `/var/lib/netbird-/config.json` configuration file, + 6. override `/var/lib/netbird-/config.json` with values from `/etc/netbird-/config.d/*.json`, + 7. (`hardened`) be locally manageable by `netbird-` 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}"; - stopIfChanged = false; - } - ) - cfg.tunnels; + # 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"; + + 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" + ++ optional (client.dns-resolver.address != null && client.dns-resolver.port < 1024) "CAP_NET_BIND_SERVICE" + ; + }; + })); + + # see https://github.com/systemd/systemd/blob/17f3e91e8107b2b29fe25755651b230bbc81a514/src/resolve/org.freedesktop.resolve1.policy#L43-L43 + # see all actions used at https://github.com/netbirdio/netbird/blob/13e7198046a0d73a9cd91bf8e063fafb3d41885c/client/internal/dns/systemd_linux.go#L29-L32 + security.polkit.extraConfig = mkIf config.services.resolved.enable '' + // systemd-resolved access for Netbird clients + polkit.addRule(function(action, subject) { + var actions = [ + "org.freedesktop.resolve1.revert", + "org.freedesktop.resolve1.set-default-route", + "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 887747437c22c..9f3681897aba2 100644 --- a/nixos/tests/netbird.nix +++ b/nixos/tests/netbird.nix @@ -10,10 +10,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 + */ })