feat(runner): arbitrary host uid support via keep-id uid remap - #228
feat(runner): arbitrary host uid support via keep-id uid remap#228seal-agent wants to merge 2 commits into
Conversation
|
CI status — the red is a pre-existing image-closure drift, not this PR's content.
Fix layer is the pinned hash in Open question (parked for Matt, non-blocking): if the hash fix is not imminent, decouple this PR by dropping the |
The embedded compass-runner refused to start unless its real uid was 1000, blocking hosted/GA deployments where the host uid is arbitrary. This implements the frozen design (`docs/designs/platform/compass-runner-arbitrary-uid/design.md`): launch containers with `--userns=keep-id:uid=<agent-uid>,gid=<agent-gid>` so an arbitrary host uid is remapped to the baked agent uid (1000) that owns `/nix` and `$HOME`, and replace the now-wrong uid refusal with a podman version-floor preflight. ### Changes - **Userns remap** — `ContainerSpec` gains a `UID` field; `Create`'s argv assembly is extracted into a unit-testable `createArgs`, which emits `--userns=keep-id:uid=,gid=` (gid collapses to uid: the image bakes gid==uid==1000). `createAndStart` threads `Workspace.UID` through. - **Startup preflight** — `verifyRunnerUID` (and its test) are deleted; `PodmanCLI.VerifyUsernsRemapSupport` + `parsePodmanVersion` replace it at the same 'ahead of every operator-input check' startup position. Hard floor podman >= 4.3 (where `keep-id:uid=` is available), no `--uidmap` fallback; the error names the required floor and the found version. - **Egress-integrity regression** — a new podman-gated test asserts that an agent exec pinned to the agent uid holds an empty effective capability set (`CapEff 0000000000000000`) inside a NET_ADMIN container, while the container's default-user provisioning exec (the `armEgress` identity) retains the capability. This pins the boundary that was reported as a root-exec escape: every agent-controllable exec already sets `--user`, so a compromised agent cannot flush its own egress ruleset. A future rename that drops an `--user` now reddens rather than silently reintroducing the escape. - **Comment sweep** — corrected the stale 'runs as root' / 'read-only mounts' comments (`podman.go`, `agent.go`, `agent-image/devenv.nix`): the image default user is uid 1000, not root, and the agent gateway socket mount is read-write. ### Tests - `TestCreateArgsRemapsUserns`, `TestParsePodmanVersion` (hermetic). - `TestKeepIDRemapMapsHostUIDToSpecUID` / `BindMountRoundTrip` / `AgentOwnsNix` — red-first against bare keep-id, green after the flag flip. - `TestAgentExecDropsNetAdminInNetAdminContainer` — the egress-integrity boundary, verified on real podman 5.8.4. All podman-gated tests pass on the dev box; build, vet, gofmt, and `compass-go:lint` (0 issues) clean. Spec-impact: none. Refs SEA-1691. Closes SEA-1542. Co-authored-by: Matt Wilkinson <matt@sealedsecurity.com>
Address the three low-severity findings from review of the arbitrary-host-uid PR (no behavior change): - Rescope the egress-integrity test docstring to the property it actually proves — that podman's --user strips the container's ambient CAP_NET_ADMIN — and point to agentenv_test.go / lifecycle_test.go for the production call-site coverage, rather than claiming a rename-drop reddens this test. - Add the major-below-floor row (3.4.4, Ubuntu 22.04 LTS) to TestParsePodmanVersion so the major<4 disjunct of the floor predicate is covered by the design record's named real-world GA-refusal case. - Scope the ExecSpec/StreamingExecSpec User doc comments' uid-1000 fact to the compass-agent image, since the seam itself is image-agnostic. Co-authored-by: Matt Wilkinson <matt@sealedsecurity.com>
e1216c6 to
6da0cd5
Compare
|
Correction to the previous comment — root cause was mis-diagnosed, now rebased. The earlier comment attributed the red to a stale FOD pin in
Action taken: rebased onto current |
The embedded compass-runner refused to start unless its real uid was 1000, blocking hosted/GA deployments where the host uid is arbitrary. This implements the frozen design (
docs/designs/platform/compass-runner-arbitrary-uid/design.md): launch containers with--userns=keep-id:uid=<agent-uid>,gid=<agent-gid>so an arbitrary host uid is remapped to the baked agent uid (1000) that owns/nixand$HOME, and replace the now-wrong uid refusal with a podman version-floor preflight.Changes
ContainerSpecgains aUIDfield;Create's argv assembly is extracted into a unit-testablecreateArgs, which emits--userns=keep-id:uid=,gid=(gid collapses to uid: the image bakes gid==uid==1000).createAndStartthreadsWorkspace.UIDthrough.verifyRunnerUID(and its test) are deleted;PodmanCLI.VerifyUsernsRemapSupport+parsePodmanVersionreplace it at the same 'ahead of every operator-input check' startup position. Hard floor podman >= 4.3 (wherekeep-id:uid=is available), no--uidmapfallback; the error names the required floor and the found version.CapEff 0000000000000000) inside a NET_ADMIN container, while the container's default-user provisioning exec (thearmEgressidentity) retains the capability. This pins the boundary that was reported as a root-exec escape: every agent-controllable exec already sets--user, so a compromised agent cannot flush its own egress ruleset. A future rename that drops an--usernow reddens rather than silently reintroducing the escape.podman.go,agent.go,agent-image/devenv.nix): the image default user is uid 1000, not root, and the agent gateway socket mount is read-write.Tests
TestCreateArgsRemapsUserns,TestParsePodmanVersion(hermetic).TestKeepIDRemapMapsHostUIDToSpecUID/BindMountRoundTrip/AgentOwnsNix— red-first against bare keep-id, green after the flag flip.TestAgentExecDropsNetAdminInNetAdminContainer— the egress-integrity boundary, verified on real podman 5.8.4.All podman-gated tests pass on the dev box; build, vet, gofmt, and
compass-go:lint(0 issues) clean.Spec-impact: none. Refs SEA-1691. Closes SEA-1542.
Co-authored-by: Matt Wilkinson matt@sealedsecurity.com