diff --git a/nixos/doc/manual/release-notes/rl-2511.section.md b/nixos/doc/manual/release-notes/rl-2511.section.md index c3aa117cdaa3..cd6b9b10644b 100644 --- a/nixos/doc/manual/release-notes/rl-2511.section.md +++ b/nixos/doc/manual/release-notes/rl-2511.section.md @@ -187,6 +187,8 @@ - `services.monero` now includes the `environmentFile` option for adding secrets to the Monero daemon config. +- `services.netbird.server` now uses dedicated packages split out due to relicensing of server components to AGPLv3 with version `0.53.0`, + - The new option [networking.ipips](#opt-networking.ipips) has been added to create IP within IP kind of tunnels (including 4in6, ip6ip6 and ipip). With the existing [networking.sits](#opt-networking.sits) option (6in4), it is now possible to create all combinations of IPv4 and IPv6 encapsulation. diff --git a/nixos/modules/services/networking/netbird/management.nix b/nixos/modules/services/networking/netbird/management.nix index f05adb671e99..1026cf4fc9c1 100644 --- a/nixos/modules/services/networking/netbird/management.nix +++ b/nixos/modules/services/networking/netbird/management.nix @@ -139,7 +139,7 @@ in options.services.netbird.server.management = { enable = mkEnableOption "Netbird Management Service"; - package = mkPackageOption pkgs "netbird" { }; + package = mkPackageOption pkgs "netbird-management" { }; domain = mkOption { type = str; diff --git a/nixos/modules/services/networking/netbird/signal.nix b/nixos/modules/services/networking/netbird/signal.nix index 3122b6c9fe5f..ac13bdb6d6ba 100644 --- a/nixos/modules/services/networking/netbird/signal.nix +++ b/nixos/modules/services/networking/netbird/signal.nix @@ -31,7 +31,7 @@ in options.services.netbird.server.signal = { enable = mkEnableOption "Netbird's Signal Service"; - package = mkPackageOption pkgs "netbird" { }; + package = mkPackageOption pkgs "netbird-signal" { }; enableNginx = mkEnableOption "Nginx reverse-proxy for the netbird signal service"; diff --git a/nixos/tests/netbird.nix b/nixos/tests/netbird.nix index 873f5405935e..962b5377cc9d 100644 --- a/nixos/tests/netbird.nix +++ b/nixos/tests/netbird.nix @@ -7,7 +7,7 @@ ]; nodes = { - clients = + node = { ... }: { services.netbird.enable = true; @@ -15,34 +15,16 @@ }; }; - # TODO: confirm the whole solution is working end-to-end when netbird server is implemented - testScript = '' - start_all() - def did_start(node, name, interval=0.5, timeout=10): - node.wait_for_unit(f"{name}.service") - node.wait_for_file(f"/var/run/{name}/sock") - # `netbird status` returns a full "Disconnected" status during initialization - # only after a while passes it starts returning "NeedsLogin" help message - - start = time.time() - output = node.succeed(f"{name} status") - while "Disconnected" in output and (time.time() - start) < timeout: - time.sleep(interval) - output = node.succeed(f"{name} status") - assert "NeedsLogin" in output - - did_start(clients, "netbird") - did_start(clients, "netbird-custom") - ''; - /* - `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: + Historically waiting for the NetBird client daemon initialization helped catch number of bugs with the service, + so we keep try to keep it here in as much details as it makes sense. - Daemon version: 0.26.3 - CLI version: 0.26.3 - Management: Disconnected + Initially `netbird status` returns a "Disconnected" messages: + OS: linux/amd64 + Daemon version: 0.54.0 + CLI version: 0.54.0 + Profile: default + Management: Disconnected, reason: rpc error: code = FailedPrecondition desc = failed connecting to Management Service : context deadline exceeded Signal: Disconnected Relays: 0/0 Available Nameservers: 0/0 Available @@ -50,7 +32,69 @@ NetBird IP: N/A Interface type: N/A Quantum resistance: false - Routes: - + Lazy connection: false + Networks: - + Forwarding rules: 0 Peers count: 0/0 Connected + + After a while passes it should start returning "NeedsLogin" help message. + + As of ~0.53.0+ in ~30 second intervals the `netbird status` instead of "NeedsLogin" it briefly (for under 2 seconds) crashes with: + + Error: status failed: failed connecting to Management Service : context deadline exceeded + + This might be related to the following log line: + + 2025-08-11T15:03:25Z ERRO shared/management/client/grpc.go:65: failed creating connection to Management Service: context deadline exceeded */ + # TODO: confirm the whole solution is working end-to-end when netbird server is implemented + testScript = '' + import textwrap + import time + + start_all() + + def run_with_debug(node, cmd, check=True, display=True, **kwargs): + cmd = f"{cmd} 2>&1" + start = time.time() + ret, output = node.execute(cmd, **kwargs) + duration = time.time() - start + txt = f">>> {cmd=} {ret=} {duration=:.2f}:\n{textwrap.indent(output, '... ')}" + if check: + assert ret == 0, txt + if display: + print(txt) + return ret, output + + def wait_until_rcode(node, cmd, rcode=0, retries=30, **kwargs): + def check_success(_last_try): + nonlocal output + ret, output = run_with_debug(node, cmd, **kwargs) + return ret == rcode + + kwargs.setdefault('check', False) + output = None + with node.nested(f"waiting for {cmd=} to exit with {rcode=}"): + retry(check_success, retries) + return output + + instances = ["netbird", "netbird-custom"] + + for name in instances: + node.wait_for_unit(f"{name}.service") + node.wait_for_file(f"/var/run/{name}/sock") + + for name in instances: + wait_until_rcode(node, f"{name} status |& grep -C20 Disconnected", 0, retries=5) + '' + # The status used to turn into `NeedsLogin`, but recently started crashing instead. + # leaving the snippets in here, in case some update goes back to the old behavior and can be tested again + + lib.optionalString false '' + for name in instances: + #wait_until_rcode(node, f"{name} status |& grep -C20 NeedsLogin", 0, retries=20) + output = wait_until_rcode(node, f"{name} status", 1, retries=61) + msg = "Error: status failed: failed connecting to Management Service : context deadline exceeded" + assert output.strip() == msg, f"expected {msg=}, got {output=} instead" + wait_until_rcode(node, f"{name} status |& grep -C20 Disconnected", 0, retries=10) + ''; } diff --git a/pkgs/by-name/ne/netbird-management/package.nix b/pkgs/by-name/ne/netbird-management/package.nix new file mode 100644 index 000000000000..e56743ab82fe --- /dev/null +++ b/pkgs/by-name/ne/netbird-management/package.nix @@ -0,0 +1,5 @@ +{ netbird }: + +netbird.override { + componentName = "management"; +} diff --git a/pkgs/by-name/ne/netbird-relay/package.nix b/pkgs/by-name/ne/netbird-relay/package.nix new file mode 100644 index 000000000000..7eb4459c9dd1 --- /dev/null +++ b/pkgs/by-name/ne/netbird-relay/package.nix @@ -0,0 +1,5 @@ +{ netbird }: + +netbird.override { + componentName = "relay"; +} diff --git a/pkgs/by-name/ne/netbird-signal/package.nix b/pkgs/by-name/ne/netbird-signal/package.nix new file mode 100644 index 000000000000..6e994d7cbd72 --- /dev/null +++ b/pkgs/by-name/ne/netbird-signal/package.nix @@ -0,0 +1,5 @@ +{ netbird }: + +netbird.override { + componentName = "signal"; +} diff --git a/pkgs/by-name/ne/netbird-ui/package.nix b/pkgs/by-name/ne/netbird-ui/package.nix index 8a5ad19846b0..297e40c870d0 100644 --- a/pkgs/by-name/ne/netbird-ui/package.nix +++ b/pkgs/by-name/ne/netbird-ui/package.nix @@ -1,5 +1,5 @@ { netbird }: netbird.override { - ui = true; + componentName = "ui"; } diff --git a/pkgs/by-name/ne/netbird-upload/package.nix b/pkgs/by-name/ne/netbird-upload/package.nix new file mode 100644 index 000000000000..5ca47f5a59f6 --- /dev/null +++ b/pkgs/by-name/ne/netbird-upload/package.nix @@ -0,0 +1,5 @@ +{ netbird }: + +netbird.override { + componentName = "upload"; +} diff --git a/pkgs/by-name/ne/netbird/package.nix b/pkgs/by-name/ne/netbird/package.nix index 2ae8e43b8ac2..2f894ee8b180 100644 --- a/pkgs/by-name/ne/netbird/package.nix +++ b/pkgs/by-name/ne/netbird/package.nix @@ -12,39 +12,72 @@ libX11, libXcursor, libXxf86vm, - ui ? false, netbird-ui, versionCheckHook, + componentName ? "client", }: let - modules = - if ui then - { - "client/ui" = "netbird-ui"; - } - else - { - client = "netbird"; - management = "netbird-mgmt"; - signal = "netbird-signal"; - }; + /* + License tagging is based off: + - https://github.com/netbirdio/netbird/blob/9e95841252c62b50ae93805c8dfd2b749ac95ea7/LICENSES/REUSE.toml + - https://github.com/netbirdio/netbird/blob/9e95841252c62b50ae93805c8dfd2b749ac95ea7/LICENSE#L1-L2 + */ + availableComponents = { + client = { + module = "client"; + binaryName = "netbird"; + license = lib.licenses.bsd3; + versionCheckProgramArg = "version"; + hasCompletion = true; + }; + ui = { + module = "client/ui"; + binaryName = "netbird-ui"; + license = lib.licenses.bsd3; + }; + upload = { + module = "upload-server"; + binaryName = "netbird-upload"; + license = lib.licenses.bsd3; + }; + management = { + module = "management"; + binaryName = "netbird-mgmt"; + license = lib.licenses.agpl3Only; + versionCheckProgramArg = "--version"; + hasCompletion = true; + }; + signal = { + module = "signal"; + binaryName = "netbird-signal"; + license = lib.licenses.agpl3Only; + hasCompletion = true; + }; + relay = { + module = "relay"; + binaryName = "netbird-relay"; + license = lib.licenses.agpl3Only; + }; + }; + isUI = componentName == "ui"; + component = availableComponents.${componentName}; in buildGoModule (finalAttrs: { - pname = "netbird"; - version = "0.49.0"; + pname = "netbird-${componentName}"; + version = "0.54.0"; src = fetchFromGitHub { owner = "netbirdio"; repo = "netbird"; tag = "v${finalAttrs.version}"; - hash = "sha256-Hv0A9/NTMzRAf9YvYGvRLyy2gdigF9y2NfylE8bLcTw="; + hash = "sha256-qKYJa7q7scEbbxLHaosaurrjXR5ABxCAnuUcy80yKEc="; }; - vendorHash = "sha256-t/X/muMwHVwg8Or+pFTSEQEsnkKLuApoVUmMhyCImWI="; + vendorHash = "sha256-uVVm+iDGP2eZ5GVXWJrWZQ7LpHdZccRIiHPIFs6oAPo="; - nativeBuildInputs = [ installShellFiles ] ++ lib.optional ui pkg-config; + nativeBuildInputs = [ installShellFiles ] ++ lib.optional isUI pkg-config; - buildInputs = lib.optionals (stdenv.hostPlatform.isLinux && ui) [ + buildInputs = lib.optionals (stdenv.hostPlatform.isLinux && isUI) [ gtk3 libayatana-appindicator libX11 @@ -52,7 +85,7 @@ buildGoModule (finalAttrs: { libXxf86vm ]; - subPackages = lib.attrNames modules; + subPackages = [ component.module ]; ldflags = [ "-s" @@ -73,35 +106,36 @@ buildGoModule (finalAttrs: { ''; postInstall = - lib.concatStringsSep "\n" ( - lib.mapAttrsToList ( - module: binary: + let + builtBinaryName = lib.last (lib.splitString "/" component.module); + in + '' + mv $out/bin/${builtBinaryName} $out/bin/${component.binaryName} + '' + + + lib.optionalString + (stdenv.buildPlatform.canExecute stdenv.hostPlatform && (component.hasCompletion or false)) '' - mv $out/bin/${lib.last (lib.splitString "/" module)} $out/bin/${binary} + installShellCompletion --cmd ${component.binaryName} \ + --bash <($out/bin/${component.binaryName} completion bash) \ + --fish <($out/bin/${component.binaryName} completion fish) \ + --zsh <($out/bin/${component.binaryName} completion zsh) '' - + lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform && !ui) '' - installShellCompletion --cmd ${binary} \ - --bash <($out/bin/${binary} completion bash) \ - --fish <($out/bin/${binary} completion fish) \ - --zsh <($out/bin/${binary} completion zsh) - '' - ) modules - ) - + lib.optionalString (stdenv.hostPlatform.isLinux && ui) '' + # assemble & adjust netbird.desktop files for the GUI + + lib.optionalString (stdenv.hostPlatform.isLinux && isUI) '' install -Dm644 "$src/client/ui/assets/netbird-systemtray-connected.png" "$out/share/pixmaps/netbird.png" install -Dm644 "$src/client/ui/build/netbird.desktop" "$out/share/applications/netbird.desktop" substituteInPlace $out/share/applications/netbird.desktop \ - --replace-fail "Exec=/usr/bin/netbird-ui" "Exec=$out/bin/netbird-ui" + --replace-fail "Exec=/usr/bin/netbird-ui" "Exec=$out/bin/${component.binaryName}" ''; nativeInstallCheckInputs = [ versionCheckHook ]; - versionCheckProgram = "${placeholder "out"}/bin/${finalAttrs.meta.mainProgram}"; - versionCheckProgramArg = "version"; - # Disabled for the `netbird-ui` version because it does a network request. - doInstallCheck = !ui; + versionCheckProgram = "${placeholder "out"}/bin/${component.binaryName}"; + versionCheckProgramArg = component.versionCheckProgramArg or "version"; + doInstallCheck = component ? versionCheckProgramArg; passthru = { tests = { @@ -115,11 +149,12 @@ buildGoModule (finalAttrs: { homepage = "https://netbird.io"; changelog = "https://github.com/netbirdio/netbird/releases/tag/v${finalAttrs.version}"; description = "Connect your devices into a single secure private WireGuard®-based mesh network with SSO/MFA and simple access controls"; - license = lib.licenses.bsd3; + license = component.license; maintainers = with lib.maintainers; [ + nazarewk saturn745 loc ]; - mainProgram = if ui then "netbird-ui" else "netbird"; + mainProgram = component.binaryName; }; })