From 84f5c2722e579b9affb6f419422b678593a4e876 Mon Sep 17 00:00:00 2001 From: atlas Date: Mon, 1 Jun 2026 17:08:15 +0200 Subject: [PATCH] nix(#702): add hive-priv systemd socket + service units Nix side of the hive-priv phase 1 work. hive-priv is socket-activated exclusively: systemd holds /run/hive/priv.sock (mode 0660) and starts the service on first connection. LISTEN_FDS + LISTEN_PID are passed; hive-priv reads them in socket_listener() to accept the pre-bound fd. Phase 2 note (comment in file): when hive-c0re drops to a non-root user, add SocketGroup = hive-core to the socket unit so the unprivileged caller can still connect. No code change needed in hive-priv itself. --- nix/modules/hive-c0re.nix | 80 ++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/nix/modules/hive-c0re.nix b/nix/modules/hive-c0re.nix index 81394997..5bd9a618 100644 --- a/nix/modules/hive-c0re.nix +++ b/nix/modules/hive-c0re.nix @@ -100,23 +100,27 @@ in # `identity.rs::peers()` + the dashboard's `peer_hives` state field # (feeds the P33RS dashboard tab). options.services.hyperhive.swarm.peers = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule { - options = { - certFingerprint = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - example = "sha256:abc123..."; - description = '' - Expected TLS certificate fingerprint for this peer's HTTPS - endpoint. Null = trust the system CA bundle (for Let's - Encrypt peers). Set to pin a self-signed cert. - ''; + type = lib.types.attrsOf ( + lib.types.submodule { + options = { + certFingerprint = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "sha256:abc123..."; + description = '' + Expected TLS certificate fingerprint for this peer's HTTPS + endpoint. Null = trust the system CA bundle (for Let's + Encrypt peers). Set to pin a self-signed cert. + ''; + }; }; - }; - }); + } + ); default = { }; example = { - "lab.example.com" = { certFingerprint = "sha256:abc123"; }; + "lab.example.com" = { + certFingerprint = "sha256:abc123"; + }; "edge.corp" = { }; }; description = '' @@ -396,5 +400,53 @@ in DirectoryMode = "0750"; }; }; + + # Socket unit for hive-priv — the narrow root helper that executes + # privileged operations on behalf of hive-c0re. Systemd creates and + # holds `/run/hive/priv.sock` before the first connection arrives. + # + # Mode 0660 root:root is correct for phase 1 (hive-c0re still runs as + # root and is the only caller). Phase 2 (privsep: hive-c0re drops to a + # non-root user) will add `SocketGroup = hive-core` so the unprivileged + # hive-c0re process can still connect. + systemd.sockets.hive-priv = { + description = "hive-priv privileged helper socket"; + wantedBy = [ "sockets.target" ]; + socketConfig = { + ListenStream = "/run/hive/priv.sock"; + SocketMode = "0660"; + # Create /run/hive/ if absent; 0755 so future unprivileged callers + # can traverse into it to reach the socket. + DirectoryMode = "0755"; + }; + }; + + # Service unit for hive-priv. Runs as root — it genuinely needs root to + # invoke `nixos-container`, write `/etc/nixos-containers/`, write + # systemd drop-ins in `/run/systemd/system/`, and call `chown(2)`. + # Every request is validated against a strict container-name allowlist + # inside the binary; the attack surface is narrow by design. + # + # Socket-activated: systemd starts hive-priv on the first connection + # (no earlier). LISTEN_FDS + LISTEN_PID are set by systemd; hive-priv + # reads them to accept the pre-bound socket fd instead of binding its + # own. + systemd.services.hive-priv = { + description = "hive-priv privileged helper"; + # No wantedBy — socket-activated exclusively. The socket unit is the + # entry point; systemd starts this service on first connect. + after = [ "hive-priv.socket" ]; + requires = [ "hive-priv.socket" ]; + serviceConfig = { + ExecStart = "${cfg.package}/bin/hive-priv"; + Type = "simple"; + User = "root"; + PrivateTmp = true; + ProtectHome = true; + # hive-priv needs to write to /etc/nixos-containers/ and + # /run/systemd/system/ — "strict" would block both. + ProtectSystem = "false"; + }; + }; }; }