fix: vpn.add-participant writes to root-owned daemon config via sudo

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 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-04-08 16:25:39 +02:00
parent aa2a13d510
commit eebdade0d4
3 changed files with 22 additions and 4 deletions

View File

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