fix: route gateway nginx control through hive-priv

systemctl --machine=hive-gateway requires root (machine-bus transport
enters the container namespace). hive-c0re is unprivileged, so every
call to nginx_active_state() and gateway_systemctl() silently failed
with exit 1, causing a continuous 30s retry loop without ever
syncing nginx.

Fix:
- Move state-aware nginx logic into hive-priv ReloadGatewayNginx:
  check ActiveState, then reload/reset-start/start accordingly.
  hive-priv already runs as root and has machine-bus rights.
- Remove nginx_active_state() and gateway_systemctl() from
  gateway_nginx.rs (they were always running unprivileged, always
  failing silently).
- Make write(), reload_if_pending(), reload_gateway_nginx() async so
  they can call the async priv_client without a blocking bridge.
- Update callers in agent_sockets::spawn_poll and meta::sync_agents
  to await the now-async functions.

The priv_client::reload_gateway_nginx() call and PrivRequest::ReloadGatewayNginx
wire type already existed — the gateway_nginx module was just not using them.
This commit is contained in:
atlas 2026-06-04 09:48:56 +02:00
commit 1d062d1e3e
4 changed files with 103 additions and 139 deletions

View file

@ -289,24 +289,72 @@ async fn exec(req: PrivRequest, writer: &mut OwnedWriteHalf) -> Result<(String,
}
PrivRequest::ReloadGatewayNginx => {
let out = Command::new("systemd-run")
// Query the nginx unit's ActiveState inside the gateway container.
// Requires root: --machine= transport enters the container namespace
// via the machine bus, which is forbidden for unprivileged users.
let state_out = Command::new("systemctl")
.args([
"--machine=hive-gateway",
"--quiet",
"--",
"show",
"--property=ActiveState",
"--value",
"nginx",
"-s",
"reload",
])
.output()
.await
.context("invoke systemd-run for gateway nginx reload")?;
if !out.status.success() {
bail!(
"gateway nginx reload failed ({}): {}",
out.status,
String::from_utf8_lossy(&out.stderr).trim()
);
.context("query nginx ActiveState in hive-gateway")?;
let state = String::from_utf8_lossy(&state_out.stdout).trim().to_owned();
// State-aware action: reload when running; reset+start after
// start-limit failure; plain start when inactive or unknown.
match state.as_str() {
"active" => {
let out = Command::new("systemctl")
.args(["--machine=hive-gateway", "reload", "nginx"])
.output()
.await
.context("reload nginx in hive-gateway")?;
if !out.status.success() {
bail!(
"gateway nginx reload failed ({}): {}",
out.status,
String::from_utf8_lossy(&out.stderr).trim()
);
}
}
"failed" => {
// Clear start-limit hit so the next start can proceed.
let _ = Command::new("systemctl")
.args(["--machine=hive-gateway", "reset-failed", "nginx"])
.status()
.await;
let out = Command::new("systemctl")
.args(["--machine=hive-gateway", "start", "nginx"])
.output()
.await
.context("start nginx after reset-failed in hive-gateway")?;
if !out.status.success() {
bail!(
"gateway nginx start (after reset-failed) failed ({}): {}",
out.status,
String::from_utf8_lossy(&out.stderr).trim()
);
}
}
_ => {
// inactive, activating, deactivating, unknown — just start.
let out = Command::new("systemctl")
.args(["--machine=hive-gateway", "start", "nginx"])
.output()
.await
.context("start nginx in hive-gateway")?;
if !out.status.success() {
bail!(
"gateway nginx start failed (state={state}) ({}): {}",
out.status,
String::from_utf8_lossy(&out.stderr).trim()
);
}
}
}
Ok((String::new(), String::new()))
}