spike(macos): rootless containers in bottles — Docker impossible, podman works #449

Closed
didericis-codex wants to merge 5 commits from spike/rootless-docker-macos into main

5 Commits

Author SHA1 Message Date
didericis 55def3732e feat(macos): replace rootless Docker spike with rootless podman
test / stage-firecracker-inputs (pull_request) Successful in 2s
test / integration-docker (pull_request) Successful in 32s
test / unit (pull_request) Successful in 36s
lint / lint (push) Failing after 52s
test / build-infra (pull_request) Successful in 4m5s
tracker-policy-pr / check-pr (pull_request) Successful in 8s
test / integration-firecracker (pull_request) Successful in 2m2s
test / coverage (pull_request) Successful in 2m7s
test / publish-infra (pull_request) Has been skipped
Podman avoids the CAP_SYS_ADMIN requirement that makes rootless Docker
impossible here: with no subordinate UID range configured it falls back
to a single-UID self-mapping, which an unprivileged process may write
itself, so newuidmap is never invoked. The image build therefore strips
/etc/subuid and /etc/subgid entries rather than adding them.

The agent-facing surface is unchanged — docker and docker compose talk
to podman's Docker-compatible API socket.

Two device nodes need relaxing as root inside the bottle (0666 on
/dev/fuse and /dev/net/tun); both already exist and neither needs a
capability the bottle lacks, unlike CAP_SYS_ADMIN.

Verified live on macOS 26 / Apple Container 1.0.0: service starts with
zero added capabilities, compose pulls and serves from the workspace on
a published port, and a nested container cannot reach the network
outside the egress path.

Known limitation, not yet addressed: the agent base image is Debian
bookworm, whose podman 4.3.1 swallows container exit codes through the
compat API (docker run returns 0 regardless). podman 5.4.2 on trixie
fixes it; the base bump is a separate PR. The acceptance test asserts
on an in-band marker rather than an exit code so it cannot false-pass
in the meantime.

Nested containers give no isolation from the agent itself — root inside
one is the agent user outside it. They are a build/test convenience;
the bottle remains the security boundary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GnMp8SUGH57hZX7192Rv2F
2026-07-21 12:59:10 -04:00
didericis 6a22b9f654 docs(research): record rootless-Docker negative result and egress OOM
Rootless Docker cannot run in an Apple Container bottle: the runtime's
capability bounding set omits CAP_SYS_ADMIN, which the kernel requires
to write a multi-range uid_map via newuidmap. Every other prerequisite
(setuid bit, subuid ranges, initial userns, no nosuid, no seccomp) was
already correct, so this is not a packaging gap we can close.

Also records two defects found while validating the podman alternative:

- a large download OOM-kills the shared egress proxy, which buffers
  whole response bodies to scan them; the gateway is a per-host
  singleton with no restart-on-death, so one bottle's pull removes
  egress for every bottle
- registry auth collides with the proxy's unconditional Authorization
  strip, measured: a valid bearer token behaves exactly like sending
  none

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GnMp8SUGH57hZX7192Rv2F
2026-07-21 12:58:59 -04:00
didericis-codex a4d8461081 test(macos): cover rootless image launch wiring
test / stage-firecracker-inputs (pull_request) Successful in 4s
test / integration-docker (pull_request) Successful in 13s
tracker-policy-pr / check-pr (pull_request) Successful in 11s
test / unit (pull_request) Successful in 35s
lint / lint (push) Successful in 2m33s
test / build-infra (pull_request) Successful in 3m24s
test / integration-firecracker (pull_request) Successful in 1m35s
test / coverage (pull_request) Successful in 1m35s
test / publish-infra (pull_request) Has been skipped
2026-07-21 06:20:15 +00:00
didericis-codex 6c7b9c2f31 fix(macos): normalize rootless Docker subordinate IDs
tracker-policy-pr / check-pr (pull_request) Successful in 8s
test / integration-docker (pull_request) Successful in 9s
test / unit (pull_request) Successful in 32s
lint / lint (push) Successful in 46s
test / stage-firecracker-inputs (pull_request) Successful in 2s
test / build-infra (pull_request) Successful in 3m23s
test / integration-firecracker (pull_request) Successful in 1m37s
test / coverage (pull_request) Failing after 1m24s
test / publish-infra (pull_request) Has been skipped
2026-07-21 06:09:41 +00:00
didericis-codex 72e35a1343 feat(macos): spike rootless Docker inside bottles
test / integration-docker (pull_request) Successful in 13s
tracker-policy-pr / check-pr (pull_request) Successful in 11s
test / unit (pull_request) Successful in 37s
test / stage-firecracker-inputs (pull_request) Successful in 2s
lint / lint (push) Successful in 2m44s
test / build-infra (pull_request) Successful in 3m38s
test / integration-firecracker (pull_request) Successful in 2m7s
test / coverage (pull_request) Failing after 1m59s
test / publish-infra (pull_request) Has been skipped
Add an opt-in docker_access path that layers rootless Docker tooling onto the selected agent image, starts the daemon only after registration, and retains the existing outer network and capability boundary. Include fail-closed bootstrap checks plus a live-Mac Compose/security acceptance test.\n\nRefs #392.
2026-07-21 05:53:54 +00:00