From eebdade0d44366f8a370046de544aba695d69eb7 Mon Sep 17 00:00:00 2001 From: Dorian Date: Wed, 8 Apr 2026 16:25:39 +0200 Subject: [PATCH] fix: vpn.add-participant writes to root-owned daemon config via sudo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nvpn daemon config at /var/lib/archipelago/nostr-vpn/ is owned by root, but the backend runs as archipelago. Direct write silently failed, so adding a second phone's npub never reached the daemon — service restarted with stale config. Now falls back to sudo cp for root-owned paths, and first-boot sets ownership to archipelago. Co-Authored-By: Claude Opus 4.6 --- core/Cargo.lock | 2 +- core/archipelago/src/api/rpc/vpn.rs | 22 +++++++++++++++++++--- scripts/first-boot-containers.sh | 2 ++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/core/Cargo.lock b/core/Cargo.lock index 46281f5d..d679417f 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -80,7 +80,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "archipelago" -version = "1.3.1" +version = "1.3.4" dependencies = [ "anyhow", "archipelago-container", diff --git a/core/archipelago/src/api/rpc/vpn.rs b/core/archipelago/src/api/rpc/vpn.rs index 1c68446b..d582b888 100644 --- a/core/archipelago/src/api/rpc/vpn.rs +++ b/core/archipelago/src/api/rpc/vpn.rs @@ -310,10 +310,26 @@ impl RpcHandler { } } if let Ok(new_content) = toml::to_string_pretty(&table) { - if let Err(e) = tokio::fs::write(config_path, &new_content).await { - tracing::warn!("Failed to write {}: {}", config_path, e); - } else { + // Try direct write first; fall back to sudo cp for root-owned daemon config + if tokio::fs::write(config_path, &new_content).await.is_ok() { info!("Added participant to {}", config_path); + } else { + // Write to temp file, then sudo cp to target + let tmp = format!("/tmp/.nvpn-config-{}", std::process::id()); + if tokio::fs::write(&tmp, &new_content).await.is_ok() { + let cp = tokio::process::Command::new("sudo") + .args(["cp", &tmp, config_path]) + .output().await; + let _ = tokio::fs::remove_file(&tmp).await; + match cp { + Ok(ref out) if out.status.success() => { + info!("Added participant to {} (via sudo)", config_path); + } + _ => { + tracing::warn!("Failed to write {} (even with sudo)", config_path); + } + } + } } } } diff --git a/scripts/first-boot-containers.sh b/scripts/first-boot-containers.sh index 1d865f4b..9691b67c 100644 --- a/scripts/first-boot-containers.sh +++ b/scripts/first-boot-containers.sh @@ -137,11 +137,13 @@ if command -v nvpn >/dev/null 2>&1; then # Sync config to daemon HOME so the service finds it # (service runs with HOME=/var/lib/archipelago/nostr-vpn) + # Owned by archipelago so the backend can update participants without sudo DAEMON_CONFIG_DIR="/var/lib/archipelago/nostr-vpn/.config/nvpn" mkdir -p "$DAEMON_CONFIG_DIR" if [ -f "$NVPN_CONFIG_DIR/config.toml" ]; then cp "$NVPN_CONFIG_DIR/config.toml" "$DAEMON_CONFIG_DIR/config.toml" fi + chown -R archipelago:archipelago /var/lib/archipelago/nostr-vpn # Ensure env file exists for the service mkdir -p /var/lib/archipelago/nostr-vpn