docs(security): document hive-priv privilege separation model

This commit is contained in:
iris 2026-06-05 16:28:55 +02:00 committed by mara
commit 28df69286d
2 changed files with 55 additions and 2 deletions

View file

@ -71,3 +71,53 @@ The proper fix is to enable user namespaces inside nspawn containers
(`--private-users=inherit` in `EXTRA_NSPAWN_FLAGS`) so nix can set up its real
sandbox and `sandbox-fallback` becomes a true last resort. This requires verifying
bind-mount compatibility with user namespace UID mapping and is tracked as a TODO.
## hive-c0re privilege separation
### Background
`hive-c0re` runs as the unprivileged system user `hive-core`
(`/var/lib/hyperhive` owned by `hive-core:hive-core`). It cannot
directly invoke `nixos-container`, `journalctl -M`, or `systemctl
-M hive-gateway` — those require root. `hive-priv` fills this gap.
### hive-priv
`hive-priv` is a minimal privileged helper that runs as root, socket-activated
at `/run/hive/priv.sock` (mode `0660`, group `hive-core` — only the
`hive-core` user can connect). `hive-c0re` calls it via `priv_client`
for every operation that genuinely requires root.
**Narrow interface** — `PrivRequest` variants map 1:1 to specific
known operations; there is no arbitrary command pass-through:
| Operation | What it runs |
|-----------|-------------|
| `StartContainer` / `StopContainer` / `KillContainer` | `nixos-container start/stop/kill <name>` |
| `CreateContainer` / `UpdateContainer` | `nixos-container create/update <name> --flake <ref>` |
| `DestroyContainer` | `nixos-container destroy <name>` |
| `ListContainers` | `nixos-container list` |
| `ReadContainerJournal` | `journalctl -M <container> -n <n> [filters...]` |
| `ReloadGateway` | `systemctl -M hive-gateway reload/start/reset-failed nginx` |
| `AddBindMount` / `RemoveBindMount` | `systemctl set-property` or `machinectl bind` |
| `WriteAgentForgeToken` / `WriteAgentMatrixToken` | write `0600` credential file into agent state |
| `SetNetworkIsolation` | manage per-container network isolation flags |
**Container allowlist** — every request is validated against an
allowlist before any operation: only names matching `h-<agent>` (the
standard agent prefix), the manager container, or the known sibling
service containers (`hive-gateway`, `hive-forge`, `hive-matrix`,
`hive-ci`) are accepted. Arbitrary container names are rejected.
**Socket-activated** — systemd starts `hive-priv` on the first
incoming connection (`LISTEN_FDS=1`); it is not running between calls.
The `ProtectSystem=strict` + `ReadWritePaths` sandbox limits filesystem
writes to only the paths `hive-priv` legitimately needs.
### Privilege boundary summary
| Component | Runs as | Privilege needed for |
|-----------|---------|----------------------|
| `hive-c0re` | `hive-core` | broker, HTTP dashboard, scheduling, approvals |
| `hive-priv` | `root` | container lifecycle, journal reads, bind mounts, cred writes |
| `hive-ag3nt` (per-container) | per-agent user | turn execution, MCP serving |