feat(#702): drop hive-c0re from root to hive-core user (privsep phase 2)

This commit is contained in:
damocles 2026-06-02 22:14:07 +02:00 committed by mara
commit 0bec3af933
2 changed files with 128 additions and 261 deletions

View file

@ -315,6 +315,17 @@ in
managerToplevel
];
# Unprivileged coordinator user. hive-c0re runs as this user
# (privsep phase 2); privileged operations are delegated to
# hive-priv which runs as root, socket-activated at
# /run/hive/priv.sock.
users.users.hive-core = {
isSystemUser = true;
group = "hive-core";
description = "hive-c0re coordinator daemon user";
};
users.groups.hive-core = { };
# Open the per-agent web-port range when the gateway is *off* —
# otherwise the gateway nginx is the sole external entry point.
# See `docs/gateway.md::Firewall posture (host-level)`.
@ -412,12 +423,22 @@ in
};
serviceConfig = {
ExecStart = "${cfg.package}/bin/hive-c0re --socket /run/hyperhive/host.sock serve --hyperhive-flake ${cfg.hyperhiveFlake} --nixpkgs-flake ${cfg.nixpkgsFlake} --nixpkgs-unstable-flake ${cfg.nixpkgsUnstableFlake} --dashboard-port ${toString cfg.dashboardPort} --operator-pronouns ${lib.escapeShellArg cfg.operatorPronouns} --context-window-tokens ${lib.escapeShellArg (builtins.toJSON cfg.contextWindowTokens)}";
# One-time migration: chown existing state tree to the service
# user after upgrading from a root-run install. The `+` prefix
# runs this step as root even though User = hive-core. systemd's
# StateDirectory chowns the top-level dir at every start, but
# pre-existing files inside may still be root-owned.
ExecStartPre = "+${pkgs.coreutils}/bin/sh -c 'chown -R hive-core:hive-core /var/lib/hyperhive || true'";
Restart = "on-failure";
RestartSec = 2;
User = "hive-core";
Group = "hive-core";
SupplementaryGroups = [ "systemd-journal" ];
RuntimeDirectory = "hyperhive";
RuntimeDirectoryMode = "0750";
RuntimeDirectoryPreserve = "yes";
StateDirectory = "hyperhive";
StateDirectoryMode = "0750";
};
};
@ -451,18 +472,18 @@ in
# 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.
# Mode 0660 hive-core:hive-core: only the hive-c0re service user can
# connect. hive-priv (server) runs as root and validates every request
# against a strict allowlist before executing any privileged op.
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.
SocketGroup = "hive-core";
# Create /run/hive/ if absent; 0755 so the hive-core user can
# traverse into it to reach the socket.
DirectoryMode = "0755";
};
};