From 9aa8b9334507ad90e5ac78a78e2087766d3fa1d9 Mon Sep 17 00:00:00 2001 From: Raul Bardaji Date: Tue, 20 Oct 2026 18:01:02 +0200 Subject: [PATCH] fix: record an existing NetBird connection without sudo (#367) An unattended run on a host already connected to NDP's NetBird network reported NetBird not joined: the installer checked for passwordless sudo before looking at the connection, which needs no sudo. It now reads the connection first and only requires sudo to install the client or join. Release 0.34.58. --- CHANGELOG.md | 5 +++++ api/config/swagger_settings.py | 2 +- install/install.sh | 17 ++++++++++++++--- install/tests/test_netbird_affinities.py | 18 ++++++++++++++++++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03c3f6c..b67a196 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.34.58] - 2026-10-20 + +### Fixed +- **An unattended run reported NetBird not joined on a machine already connected to it.** With 0.34.57 a new installation joins NetBird by default; run with `--yes` or without a terminal on a host already in NDP's network (another Endpoint on it had joined), the installer said it needed `sudo` and could not ask for the password, and wrote `NETBIRD_ENABLED=False`. It checked for passwordless `sudo` before looking at the existing connection, which needs no `sudo`. It now reads the connection first (`netbird status` as the user, through `sudo` only if that fails) and only requires `sudo` to install or join. New test in `install/tests/test_netbird_affinities.py`. Verified on a host already connected to NetBird: re-running the installer unattended on a second Endpoint recorded the connection (`NETBIRD_ENABLED=True`, the NetBird address, reported in its metrics), with the rest of its `.env` unchanged (#367). + ## [0.34.57] - 2026-10-20 ### Changed diff --git a/api/config/swagger_settings.py b/api/config/swagger_settings.py index 2c94c0d..6e59a6c 100644 --- a/api/config/swagger_settings.py +++ b/api/config/swagger_settings.py @@ -12,7 +12,7 @@ class Settings(BaseSettings): swagger_title: str = "API Documentation" swagger_description: str = "This is the API documentation." - swagger_version: str = "0.34.57" + swagger_version: str = "0.34.58" root_path: str = "" # API root path prefix (e.g., "/test" or "") is_public: bool = True metrics_endpoint: str = "https://federation.ndp.utah.edu/metrics/" diff --git a/install/install.sh b/install/install.sh index 926584b..5224f29 100755 --- a/install/install.sh +++ b/install/install.sh @@ -1299,8 +1299,11 @@ if [[ "$want_affinities" == "yes" ]]; then fi netbird_status() { - # The client's own view: management connection and address, as JSON. - "${netbird_sudo[@]}" netbird status --json 2>/dev/null || true + # The client's own view: management connection and address, as JSON. Read + # as the user first, which needs no sudo; through sudo only if that fails. + netbird status --json 2>/dev/null \ + || { [[ ${#netbird_sudo[@]} -gt 0 ]] && "${netbird_sudo[@]}" netbird status --json 2>/dev/null; } \ + || true } netbird_field() { @@ -1345,10 +1348,18 @@ join_netbird() { (Federation 1.12.9 or later, with --config-id) or --netbird-management-url." || return 1 fi netbird_sudo=() + # A machine already connected (another Endpoint on this host joined it) needs + # no sudo at all: seeing the connection does not (issue #367). + already_connected="no" + if command -v netbird >/dev/null 2>&1 \ + && [[ "$(netbird_status | netbird_field connected)" == "yes" ]]; then + already_connected="yes" + fi if [[ "$(id -u)" != "0" ]]; then netbird_sudo=(sudo) # Asked for its password only where someone can answer. - if [[ "$want_netbird" == "default" ]] && ! interactive && ! sudo -n true 2>/dev/null; then + if [[ "$want_netbird" == "default" && "$already_connected" == "no" ]] \ + && ! interactive && ! sudo -n true 2>/dev/null; then netbird_stop "it needs sudo, and sudo would ask for a password nobody can type here." || return 1 fi fi diff --git a/install/tests/test_netbird_affinities.py b/install/tests/test_netbird_affinities.py index bae0c17..e0a7ae4 100644 --- a/install/tests/test_netbird_affinities.py +++ b/install/tests/test_netbird_affinities.py @@ -460,3 +460,21 @@ def test_by_default_without_passwordless_sudo_it_carries_on(host, federation): assert "needs sudo" in result.stderr assert "netbird up" not in _netbird_calls(host) assert _env(host)["NETBIRD_ENABLED"] == "False" + + +def test_an_unattended_run_on_a_connected_machine_records_netbird(host, federation): + """Issue #367: seeing an existing connection needs no sudo.""" + federation["config"] = _configuration(netbird_management_url=MANAGEMENT_URL) + # Already connected, as the second Endpoint on a host that joined before. + (host["tmp"] / "netbird.joined").touch() + # sudo that would need a password: -n refuses, anything else fails too. + (host["stubs"] / "sudo").write_text("#!/bin/sh\nexit 1\n", encoding="utf-8") + + result = _install(host, federation) + + assert result.returncode == 0, result.stdout + result.stderr + assert "needs sudo" not in result.stderr + assert "netbird up" not in _netbird_calls(host) + assert _env(host)["NETBIRD_ENABLED"] == "True" + assert _env(host)["NETBIRD_IP"] == "100.64.0.7" + assert _state(host)["NETBIRD"] == "yes"