0014: What the bubblewrap sandbox needs from a container
Status: accepted
Context
Section titled “Context”With sandbox: bubblewrap (0013), Claude Code 2.1.308 runs each of the agent’s commands in bubblewrap. It always uses user and PID namespaces, a fresh /proc and --disable-userns, and ignores enableWeakerNestedSandbox in this mode. It checks only that bwrap exists; when bubblewrap is blocked, every command fails.
A hardened container blocks bubblewrap in four ways:
- the default seccomp profiles of Docker and containerd refuse namespace creation, mounts and
pivot_root; - their AppArmor profiles deny every mount;
- Ubuntu 24.04 (
kernel.apparmor_restrict_unprivileged_userns=1) denies user namespaces to processes without a profile that grants them; - the container’s masked
/procstops a fresh/procmount.
The sandbox also mounts the whole filesystem read-only inside, so a mounted secret file would still be readable.
Decision
Section titled “Decision”- The agent image always ships bubblewrap and socat, pinned, with bubblewrap not setuid. They do nothing unless the option is on.
- The runtime checks at start. With the option on, the adapter runs bubblewrap with Claude Code’s flags before it creates the runtime, and stops the agent with one error that names the missing piece and its fix.
ost doctorshows the same check. - The runtime hides Claude Code’s credential files inside the sandbox (
sandbox.credentials.files). Other secret files stay readable, becauseost git-credentialruns inside the sandbox and needs the GitHub App keys. - The examples stay plain. Optional variants change only the agents:
examples/k8s-sandbox(a kustomize overlay) andexamples/compose/compose.sandbox.yaml(a Compose override);- a seccomp profile: the runtime default plus
cloneandunsharefor user, mount, PID and network namespaces,mount,umount2andpivot_root; - an AppArmor profile: containerd’s default plus
userns, the mounts and pivots bubblewrap makes, and the write to/proc/sys/userbehind--disable-userns; each added rule was checked to be needed; - an unmasked
/proc, and on Kubernetes the pod’s own user namespace, which Kubernetes requires for it. The overlay’s namespace enforcesbaseline, becauserestrictedforbids an unmasked/proc.
Consequences
Section titled “Consequences”- Operators who turn the sandbox on install two profiles on every node that runs agents. The cluster needs Kubernetes 1.33, containerd 2.0, runc 1.2, Linux 6.3 and volumes with idmapped mounts. The deployment guides list the checks.
- Agents with the sandbox can create user namespaces in their pod. That is more kernel surface; the pod’s own user namespace keeps the node’s users out of reach, and bubblewrap stops commands from nesting further.
- With the sandbox on, tools that need their own user namespace inside a command (rootless Podman, Chromium’s sandbox) do not run.