Watch
0
0
Fork
You've already forked hyperhive
0

document where the privateUsers flag lives and who it authorises

This commit is contained in:
atlas 2026-11-01 16:21:17 +01:00 • committed by mara
commit 894349459a
2 changed files with 28 additions and 14 deletions

View file

@ -458,12 +458,23 @@ the lines written after the last push.
### User namespace (opt-in, per agent)
An agent named in `services.hyperhive.userns.agents` runs in its own user
namespace. Every other agent keeps the host's uids. The operator sets the list
in the host config; hive-priv reads it from its unit's environment, so an agent
can't switch it on or off.
An agent whose config sets `services.hyperhive.agent.user.privateUsers = true`
runs in its own user namespace. Every other agent keeps the host's uids.
- **On disk the data belongs to one host uid:gid**, `services.hyperhive.userns.dataOwner`
**Approving the config PR that sets it authorises a host-level change:** the
first start afterwards has hive-priv, as root, chown the agent's `state`,
`harness`, `work` and `claude` dirs once to the host's data owner. It deletes nothing.
**How the hive learns the flag.** It never evaluates the agent's nix. The swarm
builder reads `config.services.hyperhive.agent.user.privateUsers` from the
config it evaluates in its sandbox and puts it in the agent's closure record
(`private_users`). hive-c0re reads the record when it writes the container's
flags and sends the value to hive-priv with each start. A record or builder
from before the field reads false; a record hive-c0re can't read, or none,
leaves the agent as it is.
- **On disk the data belongs to one host uid:gid**,
`services.hyperhive.deploy.hive-controller.userns.dataOwner`
(default `60000:60000`). Inside the container that owner shows as the agent
user. hive-priv mounts the agent's `state/`, `harness/`, `work/` and
`claude/` dirs and its socket dir with `owneridmap`, and every other bind too
@ -471,7 +482,7 @@ can't switch it on or off.
- **The container starts with `--private-users=pick --private-users-ownership=map`.**
nspawn idmaps the root at mount time, so a different range for `pick` never
walks or chowns it.
- **The move happens once.** The first `WriteNspawnFlags` that finds a listed
- **The move happens once.** The first `WriteNspawnFlags` that finds the
agent's container stopped chowns those four dirs to `dataOwner` (ownership
only; it removes or copies nothing), writes
`/var/lib/hive-priv/userns/<name>`, and creates the mount points in the root
@ -482,12 +493,15 @@ can't switch it on or off.
or manager that can write the dirs can't redirect it to a host path.
- **The agent's gid must equal its uid.** `owneridmap` maps the gid of a bind
with the uid map, so a group other than the uid's has no mapping and writes
fail. Pin `services.hyperhive.agent.user.uid` and `.gid` to one number in the
agent's config, and start it once, before listing it. The move refuses an
agent whose `state` dir has another gid than uid, naming the agent.
- **Leaving the list is manual.** hive-priv refuses to start an agent whose
marker exists but who is no longer on the list, and one whose `dataOwner`
fail. The agent module asserts that `user.uid` and `user.gid` hold the
same number when `privateUsers` is on. Start the agent once with the pin
before turning it on: the move refuses an agent whose `state` dir has another
gid than uid, naming the agent.
- **Turning it off is manual.** hive-priv refuses to start an agent whose
marker exists while its record says false, and one whose `dataOwner`
changed. Chown the four dirs back, then delete the marker.
- **Keep a `ManageRootAgent` holder off it.** The other agents' dirs it mounts
aren't remapped.
The pause marker needs nothing extra: hive-priv gives it the owner of the
`harness/` dir. hive-c0re still creates agent dirs and new subvolumes as

View file

@ -261,8 +261,8 @@ known operations; there is no arbitrary command pass-through:
| `DestroyContainer` | `nixos-container destroy <name>` |
| `ListContainers` | `nixos-container list` |
| `ReloadGatewayNginx` | `systemctl reload/start/reset-failed nginx` (host unit; the unit name is hard-coded, not a parameter) |
| `WriteNspawnFlags` | write `/etc/nixos-containers/<container>.conf` (bind-mount list, network isolation vars, and `--ephemeral` after the root's salvage). hive-priv first copies a stopped container's root to the agent's `work/`, then renames it aside (it deletes nothing), following no link the container could have planted; for an agent in `services.hyperhive.userns.agents` it also adds `--private-users=pick`, mounts every bind `owneridmap`, and once chowns the agent's `state`, `harness`, `work` and `claude` dirs (never `config`, following no link) to the data owner |
| `EnsureAgentSocketDir` | `mkdirat` `/run/hive-agent/<name>` `0751 root:root` under an `O_NOFOLLOW` parent fd; leaves an existing directory alone, refuses anything else; for an agent in `services.hyperhive.userns.agents` it then chowns the dir to the data owner |
| `WriteNspawnFlags` | write `/etc/nixos-containers/<container>.conf` (bind-mount list, network isolation vars, and `--ephemeral` after the root's salvage). hive-priv first copies a stopped container's root to the agent's `work/`, then renames it aside (it deletes nothing), following no link the container could have planted; for an agent whose closure record sets `private_users` it also adds `--private-users=pick`, mounts every bind `owneridmap`, and once chowns the agent's `state`, `harness`, `work` and `claude` dirs (never `config`, following no link) to the data owner |
| `EnsureAgentSocketDir` | `mkdirat` `/run/hive-agent/<name>` `0751 root:root` under an `O_NOFOLLOW` parent fd; leaves an existing directory alone, refuses anything else; for an agent whose closure record sets `private_users` it then chowns the dir to the data owner |
| `WriteResourceLimits` | write `CPUQuota=`/`MemoryMax=`/`CPUWeight=`/`IOWeight=` systemd drop-in for agent container, with an `ExecStartPre=` that runs `hive-priv check-rootfs <container>` (no-follow check of the root paths the start script touches; see persistence.md), and a GC root on the build it names |
| `RemoveServiceDropin` | remove `container@<name>.service.d/` drop-in on destroy, and the GC root of the hive-priv build it named |
| `DaemonReload` | `systemctl daemon-reload` |