Skip to content

Commit 1aa3840

Browse files
mwdominoClaude Opus 4.8 (1M context)raine
authored
Kata containers backend (#198)
* feat(sandbox) - add oci_runtime passthrough for container backend Let the container sandbox backend run under an alternative OCI runtime via `docker`/`podman run --runtime <x>`, selected with a new `sandbox.container.oci_runtime` option. Unset keeps the runtime default (runc), so existing behavior is unchanged. Pointing it at a VM-based runtime such as Kata Containers gives each sandbox a hardware-VM boundary while reusing the entire tested container path (image, mounts, user mapping, RPC). The flag is threaded into `build_docker_run_args` immediately after the `run` subcommand, so both agent panes and `workmux sandbox shell` pick it up. Like `devices`/`group_add`, `oci_runtime` is global-only: it changes the isolation boundary, so a project `.workmux.yaml` cannot set (or downgrade) it and a warning is emitted if it tries. No new SandboxBackend variant and no supervisor/multiplexer changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(sandbox) - add container.cap_add and security_opt passthroughs Global-only, default empty (no behavior change when unset). Emits docker/podman run --cap-add / --security-opt. Enables docker-in-docker under the kata oci_runtime without --privileged, which fails on kata (injects host device nodes that collide with the guest's: 'Creating container device /dev/full EEXIST'). Granting caps + security_opt instead boots cleanly and keeps the daemon confined to the VM. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sandbox) - skip oci_runtime and security opts on apple container Address PR #198 review feedback. build_docker_run_args_inner emitted `--runtime`, `--cap-add`, and `--security-opt` before the outer runtime was known, so a global `oci_runtime: kata` (or cap/security opts) produced `container run --runtime kata ...` on macOS, where Apple Container is auto-preferred. Apple Container misreads these Docker/Podman flags and fails to start the sandbox, contradicting the docs that promised the setting is ignored there. Compute the runtime first and skip these Docker/Podman-only flags on Apple Container, matching the documented "ignored" behavior and letting a single global config target Kata on a Docker host while still running on macOS. Docs: correct the unset default (engine default -- runc on Docker, crun on Podman -- not a hardcoded runc), make the Kata example Docker-first, and note Kata does not officially support Podman. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EvyFzQAKuwhy1DUPLdTBd2 * fix container runtime compatibility guidance Remote Podman clients do not expose the local engine's --runtime flag, so configured OCI runtime overrides failed with an opaque unknown-flag error. Reject known remote Podman environments with an actionable message while preserving support for local Podman on Linux. Describe unset OCI runtime behavior as deferring to the engine's configured default in both generated and source documentation. Document cap_add and security_opt syntax, runtime support, global-only scope, and the isolation impact of permissive values used without a VM-based runtime. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Raine Virta <raine.virta@gmail.com>
1 parent e99b331 commit 1aa3840

3 files changed

Lines changed: 401 additions & 27 deletions

File tree

docs/guide/sandbox/container.md

Lines changed: 91 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,26 @@ workmux sandbox pull
4141

4242
## Configuration
4343

44-
| Option | Default | Description |
45-
| ------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46-
| `enabled` | `false` | Enable container sandboxing |
47-
| `container.runtime` | auto-detect | Container runtime: `docker`, `podman`, or `apple-container`. Auto-detected from PATH when not set. On macOS, prefers Apple Container (`container`) over Docker/Podman. |
48-
| `container.memory` | `16G` (Apple Container) / none (others) | Memory limit for the container. Apple Container VMs default to 1 GB which is too low, so workmux sets `16G` by default. This is a ceiling, not an upfront allocation. Works with any runtime when explicitly set. |
49-
| `container.cpus` | none | CPU count for the container. Only passed when explicitly set. Apple Container defaults to 4 CPUs which is sufficient for most workloads. |
50-
| `container.devices` | `[]` | Host device nodes exposed to the sandbox (e.g. `/dev/kvm`, `/dev/ttyUSB0`). Passed to the runtime as `--device`. Docker and Podman only; Apple Container rejects. **Global config only.** |
51-
| `container.group_add` | `[]` | Supplementary groups added to the sandboxed process (e.g. `dialout`, `video`, or numeric GIDs). Passed to the runtime as `--group-add`. Docker and Podman only; Apple Container rejects. **Global config only.** |
52-
| `target` | `agent` | Which panes to sandbox: `agent` or `all` |
53-
| `image` | `ghcr.io/raine/workmux-sandbox:{agent}` | Container image name (auto-resolved from configured agent). |
54-
| `rpc_host` | auto | Override hostname for guest-to-host RPC. Defaults to `host.docker.internal` (Docker), `host.containers.internal` (Podman), or `192.168.64.1` (Apple Container). **Global config only.** |
55-
| `env_passthrough` | `[]` | Environment variables to pass through. **Global config only.** |
56-
| `env` | `{}` | Environment variables to set with explicit values (unlike `env_passthrough` which reads from host). **Global config only.** |
57-
| `extra_mounts` | `[]` | Additional host paths to mount (see [shared features](./features#extra-mounts)). **Global config only.** |
58-
| `agent_config_dir` | per-agent default | Custom host directory for agent config. Supports `{agent}` placeholder. Overrides default mounts (e.g. `~/.claude/`). Auto-created if missing. **Global config only.** |
59-
| `network.policy` | `allow` | Network restriction policy: `allow` (no restrictions) or `deny` (block all except allowed domains). See [network restrictions](#network-restrictions). **Global config only.** |
60-
| `network.allowed_domains` | `[]` | Allowed outbound HTTPS domains when policy is `deny`. Supports exact matches, `*.` wildcard prefixes, and exact-host private destination opt-in. **Global config only.** |
44+
| Option | Default | Description |
45+
| ------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46+
| `enabled` | `false` | Enable container sandboxing |
47+
| `container.runtime` | auto-detect | Container runtime: `docker`, `podman`, or `apple-container`. Auto-detected from PATH when not set. On macOS, prefers Apple Container (`container`) over Docker/Podman. |
48+
| `container.memory` | `16G` (Apple Container) / none (others) | Memory limit for the container. Apple Container VMs default to 1 GB which is too low, so workmux sets `16G` by default. This is a ceiling, not an upfront allocation. Works with any runtime when explicitly set. |
49+
| `container.cpus` | none | CPU count for the container. Only passed when explicitly set. Apple Container defaults to 4 CPUs which is sufficient for most workloads. |
50+
| `container.devices` | `[]` | Host device nodes exposed to the sandbox (e.g. `/dev/kvm`, `/dev/ttyUSB0`). Passed to the runtime as `--device`. Docker and Podman only; Apple Container rejects. **Global config only.** |
51+
| `container.group_add` | `[]` | Supplementary groups added to the sandboxed process (e.g. `dialout`, `video`, or numeric GIDs). Passed to the runtime as `--group-add`. Docker and Podman only; Apple Container rejects. **Global config only.** |
52+
| `container.cap_add` | `[]` | Extra Linux capabilities granted to the container, passed verbatim as `--cap-add` (e.g. `ALL` or `SYS_ADMIN`). Docker and Podman only; Apple Container ignores it. Permissive values weaken host-kernel container isolation. **Global config only.** |
53+
| `container.security_opt` | `[]` | Security options passed verbatim as `--security-opt` (e.g. `seccomp=unconfined` or `no-new-privileges`). Docker and Podman only; Apple Container ignores it. Options can strengthen or weaken confinement. **Global config only.** |
54+
| `container.oci_runtime` | none (engine default) | OCI runtime to run the container under, passed as Docker or local Podman `--runtime`. Unset omits `--runtime`, deferring to the engine's configured default. Set to e.g. `kata` for stronger VM-based isolation. Remote Podman rejects this flag; Apple Container ignores it. **Global config only.** |
55+
| `target` | `agent` | Which panes to sandbox: `agent` or `all` |
56+
| `image` | `ghcr.io/raine/workmux-sandbox:{agent}` | Container image name (auto-resolved from configured agent). |
57+
| `rpc_host` | auto | Override hostname for guest-to-host RPC. Defaults to `host.docker.internal` (Docker), `host.containers.internal` (Podman), or `192.168.64.1` (Apple Container). **Global config only.** |
58+
| `env_passthrough` | `[]` | Environment variables to pass through. **Global config only.** |
59+
| `env` | `{}` | Environment variables to set with explicit values (unlike `env_passthrough` which reads from host). **Global config only.** |
60+
| `extra_mounts` | `[]` | Additional host paths to mount (see [shared features](./features#extra-mounts)). **Global config only.** |
61+
| `agent_config_dir` | per-agent default | Custom host directory for agent config. Supports `{agent}` placeholder. Overrides default mounts (e.g. `~/.claude/`). Auto-created if missing. **Global config only.** |
62+
| `network.policy` | `allow` | Network restriction policy: `allow` (no restrictions) or `deny` (block all except allowed domains). See [network restrictions](#network-restrictions). **Global config only.** |
63+
| `network.allowed_domains` | `[]` | Allowed outbound HTTPS domains when policy is `deny`. Supports exact matches, `*.` wildcard prefixes, and exact-host private destination opt-in. **Global config only.** |
6164

6265
### Example configurations
6366

@@ -148,6 +151,77 @@ Listed paths are relative to the worktree root. Each one is shadowed by a read-o
148151

149152
**Note:** `excluded_files` relies on file-level bind mounts, which Apple Container does not support (it only accepts directory mounts). When the runtime is Apple Container and `excluded_files` is set, workmux fails fast rather than silently leaving secrets readable. Use Docker or Podman if you need this feature.
150153

154+
**Run under a VM-based OCI runtime (stronger isolation):**
155+
156+
```yaml
157+
sandbox:
158+
enabled: true
159+
container:
160+
runtime: docker
161+
oci_runtime: kata
162+
```
163+
164+
`oci_runtime` is passed straight through to Docker or local Podman as `--runtime`,
165+
so the container runs under whatever OCI runtime you name instead of the engine's
166+
configured default. Pointing it at a VM-based runtime such as
167+
[Kata Containers](https://katacontainers.io/) gives each sandbox a hardware-VM
168+
boundary while reusing the exact same container image, mounts, user mapping, and
169+
RPC path as the normal container backend. Leaving it unset keeps the engine's
170+
default behavior.
171+
172+
The named runtime must already be installed on the host and registered with your
173+
container engine (for Docker, listed under `docker info`'s runtimes). Setting it
174+
up is out of scope here. See the runtime's own documentation.
175+
176+
Notes:
177+
178+
- **Global config only.** Like `devices`/`group_add`, `oci_runtime` is ignored
179+
when set in a project's `.workmux.yaml`. It changes the isolation boundary, so
180+
a repository must not be able to select (or downgrade) it via its own config.
181+
- **Kata needs Docker.** Kata Containers does not officially support Podman as a
182+
container manager ([Kata limitations](https://github.com/kata-containers/kata-containers/blob/main/docs/Limitations.md)),
183+
so use `runtime: docker` for the `kata` example above.
184+
- **Podman support is local-only.** Other OCI runtimes may work with local Podman
185+
on Linux. Remote Podman clients, including all macOS and Windows builds, do
186+
not expose `podman run --runtime`. Workmux rejects `oci_runtime` on non-Linux
187+
hosts and when `CONTAINER_HOST` or `CONTAINER_CONNECTION` selects a remote
188+
service. Other remote Podman configurations are unsupported and may return
189+
`unknown flag: --runtime` directly from Podman.
190+
- **Apple Container ignores it.** Apple Container manages its own VM and has no
191+
`--runtime` concept, so a configured `oci_runtime` is silently omitted there
192+
(as are `cap_add`/`security_opt`). This lets one global config target Kata on a
193+
Docker host and still run on macOS without erroring.
194+
- **Network-deny mode** ([below](#network-restrictions)) configures `iptables`
195+
inside the container, which needs a real in-guest kernel. It works under a
196+
VM-based runtime like Kata, but may not under a userspace-kernel runtime such
197+
as gVisor.
198+
199+
**Docker-in-Docker under Kata:**
200+
201+
```yaml
202+
sandbox:
203+
enabled: true
204+
container:
205+
runtime: docker
206+
oci_runtime: kata
207+
cap_add: [ALL]
208+
security_opt:
209+
- seccomp=unconfined
210+
- apparmor=unconfined
211+
- systempaths=unconfined
212+
```
213+
214+
`cap_add` and `security_opt` are global-only passthroughs for Docker and Podman.
215+
They support an inner Docker daemon under Kata without `--privileged`, which
216+
injects host device nodes that conflict with devices in the guest. The values
217+
above grant every Linux capability and disable several container-level security
218+
controls, while Kata's guest VM remains the host isolation boundary.
219+
220+
These fields are independent of `oci_runtime`. Without a VM-based OCI runtime,
221+
permissive values apply to a host-kernel container and weaken the sandbox's
222+
isolation from the host. `security_opt` values such as `no-new-privileges` can
223+
instead strengthen confinement. Apple Container silently omits both fields.
224+
151225
**Sandbox all panes (not just agent):**
152226

153227
```yaml

0 commit comments

Comments
 (0)