hive-priv applies only the path its last RealiseClosure returned for that agent. On drift the swap and create record the pins of the commit the closure was built from.
69 lines
3.6 KiB
Markdown
69 lines
3.6 KiB
Markdown
# hive-priv
|
|
|
|
The minimal **root privileged-helper** for hive-c0re. It runs as root and
|
|
exposes a narrow unix socket at `/run/hive/priv.sock` that accepts `PrivRequest`
|
|
JSON lines and performs only the handful of operations that genuinely require
|
|
root — bind-mount edits, `nsenter` into a container, btrfs subvolume ops. All
|
|
coordination logic (broker, HTTP, scheduling) stays in the _unprivileged_
|
|
`hive-c0re` process, which delegates here.
|
|
|
|
## Why it exists
|
|
|
|
Privsep. `hive-c0re` runs as the unprivileged `hive-core` user so a bug or a
|
|
prompt-injection in the large daemon can't directly wield root. The few root
|
|
operations it needs are funnelled through this small, auditable helper instead.
|
|
See `docs/trust-boundary/boundary.md` and `docs/trust-boundary/security.md` for the privilege boundary.
|
|
|
|
## Security model
|
|
|
|
- **Strict allowlist.** hive-priv checks every request against a container-name
|
|
allowlist before any filesystem or process operation, and accepts only names
|
|
matching the hive convention (`h-*`, the manager container included).
|
|
- **No pass-through.** Every `PrivRequest` variant maps to a single known
|
|
operation; there is no arbitrary-command escape hatch.
|
|
- **Socket-activated, always.** systemd binds `/run/hive/priv.sock`
|
|
(`SocketGroup=hive-core`, `0660`) and passes the listener as fd 3
|
|
(`LISTEN_FDS`); the helper requires this and has no self-bind fallback, so dev
|
|
and prod take the identical path and the group grant always holds.
|
|
|
|
The wire contract (`PrivRequest` / response types) lives in the separate
|
|
`hive-priv-sock` crate so this root binary depends on just the protocol shapes,
|
|
not the whole daemon-shared crate.
|
|
|
|
## Layout
|
|
|
|
`src/main.rs` holds the socket loop and the dispatch table, one arm per
|
|
`PrivRequest` variant. Each op family, with its validation, has its own module:
|
|
`container` (lifecycle), `container_config` (nspawn flags, systemd drop-ins),
|
|
`storage` (btrfs subvolumes, quotas, snapshots, disk measurement), `files`
|
|
(pause marker, socket dir, legacy tmpfiles) and `infra` (gateway nginx, forge
|
|
admin, infra containers). Helpers more than one family calls live in `shared`.
|
|
Each module's `//!` header lists its ops.
|
|
|
|
## Implementation notes
|
|
|
|
### Container toplevels (create/update)
|
|
|
|
`container_toplevel_action` (in `src/container.rs`) applies a closure that is
|
|
already in the store; hive-priv builds nothing. hive-c0re names the closure
|
|
(`toplevel` on `CreateContainer` / `UpdateContainer`), downloaded beforehand
|
|
with `RealiseClosure`. hive-priv remembers, per agent, the path its last
|
|
successful `RealiseClosure` returned (in memory), and applies only that path to
|
|
that agent. It refuses a path never realised for the agent, another agent's
|
|
and the host's own, and logs an error naming the agent and both paths. A
|
|
restart between the download and the apply empties the record, so the apply
|
|
fails and the job retries. hive-priv does not re-check the signature at apply
|
|
time: `RealiseClosure` enforces it, and the binding limits an apply to what that
|
|
call stored.
|
|
|
|
`create` passes the path to `nixos-container create --system-path`, so
|
|
`nixos-container`'s own `buildFlake()` never runs. Without the flag, it builds
|
|
to a hardcoded relative `.tmp` out-link: `$systemPath` stays `undef` and
|
|
`"$systemPath.tmp"` interpolates to the bare string `.tmp` in the caller's cwd.
|
|
With `services.hyperhive.deploy.hive-controller.buildSlots > 1`, two concurrent
|
|
calls could share that one `.tmp`, and one agent's container got another
|
|
agent's closure.
|
|
|
|
`update` points the container's profile at the path with `nix-env --set` and
|
|
reloads the unit if it runs, because `nixos-container update` dies on its
|
|
version probe before it honors `--system-path`.
|