netbird: 0.49.0 -> 0.54.0 + split up + relicense (#431976)

This commit is contained in:
Paul Haerle 2025-08-11 22:10:25 +02:00 committed by GitHub
commit 086d855355
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 171 additions and 70 deletions

View File

@ -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.

View File

@ -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;

View File

@ -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";

View File

@ -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)
'';
}

View File

@ -0,0 +1,5 @@
{ netbird }:
netbird.override {
componentName = "management";
}

View File

@ -0,0 +1,5 @@
{ netbird }:
netbird.override {
componentName = "relay";
}

View File

@ -0,0 +1,5 @@
{ netbird }:
netbird.override {
componentName = "signal";
}

View File

@ -1,5 +1,5 @@
{ netbird }:
netbird.override {
ui = true;
componentName = "ui";
}

View File

@ -0,0 +1,5 @@
{ netbird }:
netbird.override {
componentName = "upload";
}

View File

@ -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;
};
})