fix(swarm-controller): the socket needs 0666, the directory is the guard

bind leaves a unix socket 0755 and connecting needs write, so the
gateway's nginx -- a different user -- would be locked out.

0666 is what hive-c0re already does for the per-agent sockets, and it
rests on the same argument: the containing directory is the access
control, not the socket mode. This directory holds one socket and is
bind-mounted into exactly one container.

That is also the sharper reason the socket does not live beside the host
admin socket. With a 0666 socket, a directory that carries more than it
should is not untidiness, it is the vulnerability.
This commit is contained in:
atlas 2026-08-05 11:32:53 +02:00 committed by mara
commit f10f8a6bc6

View file

@ -12,6 +12,7 @@
//! Distinct from `hive-c0re`, which is per-hive: c0re owns the agents on
//! one host, this owns what is true across hives.
use std::os::unix::fs::PermissionsExt as _;
use std::path::PathBuf;
use anyhow::{Context, Result};
@ -67,6 +68,18 @@ async fn main() -> Result<()> {
let listener = tokio::net::UnixListener::bind(&path)
.with_context(|| format!("binding {}", path.display()))?;
// `bind` leaves the socket 0755, and connecting needs write — the
// gateway's nginx is a different user, so it would be locked out.
// 0666 matches how hive-c0re publishes the per-agent sockets
// (`socket_server::start`), and rests on the same argument: **the
// containing directory is the access control, not the socket mode.**
// This directory holds one socket and is bind-mounted into exactly
// one container. That is also why it must not be shared with
// hive-c0re's `/run/hyperhive` — with a 0666 socket, a directory
// that carries more than it should is the whole vulnerability.
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o666))
.with_context(|| format!("chmod {}", path.display()))?;
tracing::info!(socket = %path.display(), "swarm-controller listening");
let app = Router::new().route("/health", get(health));