71 lines
4.4 KiB
Markdown
71 lines
4.4 KiB
Markdown
# agent-sandbox
|
|
|
|
Sandbox agentic coding assistants with [bubblewrap](https://github.com/containers/bubblewrap). Limits what an AI agent can see and modify on the host, reducing the blast radius of prompt injection and accidental damage.
|
|
|
|
## Modes
|
|
|
|
### Whitelist
|
|
|
|
Tight sandbox for normal agent coding tasks. Only explicitly listed paths are visible — system binaries, libraries, a subset of `/etc`, `/sys` (all read-only), synthetic `/dev`, private `/proc`, `/tmp`, `/run`, and the working directory (read-write). Everything else is invisible.
|
|
|
|
### Blacklist
|
|
|
|
Looser sandbox for system-level debugging with agent assistance. The host filesystem is mounted read-only, with targeted overlays hiding sensitive paths (credentials, history, secrets, sockets, input devices). `/run` and `${XDG_RUNTIME_DIR}` are replaced with tmpfs mounts that only expose the paths needed for system tooling (`systemctl`, `resolvectl`, `journalctl`, etc.).
|
|
|
|
The threat model is prompt injection and accidental damage, not a determined attacker with user-level access.
|
|
|
|
**Not protected in blacklist mode:** arbitrary readable files outside the sensitive paths list, and D-Bus method calls (access control is daemon-side).
|
|
|
|
## Environment filtering
|
|
|
|
Both modes clamp the environment the child sees so prompt-injected agents can't `printenv` their way to secrets.
|
|
|
|
- **Whitelist** clears the parent env and re-adds a small allowlist: identity/shell vars (`HOME`, `PATH`, …), terminal/locale, proxy, non-GUI XDG base dirs, and agent vendor prefixes (`ANTHROPIC_*`, `CLAUDE_*`, `OPENAI_*`, `CODEX_*`, `GEMINI_*`, `OTEL_*`).
|
|
- **Blacklist** keeps the parent env but unsets credentials and dangling pointers: cloud creds (`AWS_*`, `GOOGLE_APPLICATION_CREDENTIALS`, …), backup tool passphrases, sockets stripped by path overlays (`SSH_AUTH_SOCK`, `DISPLAY`, `GNUPGHOME`, …), and anything matching `*_TOKEN`, `*_SECRET`, `*_PASSWORD`, `*_PASSPHRASE`, `*_API_KEY`, `*_PRIVATE_KEY`, `*_CLIENT_SECRET`. Vendor-prefix vars (`ANTHROPIC_API_KEY` etc.) are carved out so they survive.
|
|
|
|
Disable the built-in policy entirely with `--no-env-filter` (or `env-filter = false` in the config file) to pass the parent env through unchanged. User `--setenv`/`--unsetenv` escape hatches still apply.
|
|
|
|
## Seccomp
|
|
|
|
Both modes apply a seccomp-BPF syscall allowlist derived from Podman's default profile. Dangerous syscalls (`mount`, `unshare`, `ptrace`, `bpf`, `perf_event_open`, `io_uring_*`, `keyctl`, `kexec_*`, …) return `ENOSYS`. Disable with `--no-seccomp` or `seccomp = false` in the config file.
|
|
|
|
## Configuration file
|
|
|
|
Settings can be stored in a TOML config file at `$XDG_CONFIG_HOME/agent-sandbox/config.toml` (or pass `--config <path>`). Use `--no-config` to skip loading it. The config file accepts the same options as the corresponding CLI flags.
|
|
|
|
Top-level keys set defaults; `[profile.<name>]` sections define named presets selectable with `--profile <name>`. CLI flags always take highest precedence, followed by the active profile, then top-level defaults.
|
|
|
|
```toml
|
|
# Global defaults
|
|
whitelist = true
|
|
unshare-net = true
|
|
ro = ["~/.aws"]
|
|
|
|
# Named profile
|
|
[profile.docker]
|
|
blacklist = true
|
|
rw = ["/var/run/docker.sock"]
|
|
command = ["claude", "--dangerously-skip-permissions"]
|
|
```
|
|
|
|
## Escape hatches
|
|
|
|
When the agent needs access to something the sandbox blocks, use `--rw` or `--ro` for paths and `--setenv`/`--unsetenv` for env vars. User overrides always win over the built-in policies.
|
|
|
|
```bash
|
|
agent-sandbox --rw /var/run/docker.sock -- claude --dangerously-skip-permissions
|
|
agent-sandbox --ro ~/.aws -- claude --dangerously-skip-permissions
|
|
agent-sandbox --setenv DATABASE_URL=postgres://localhost/dev -- claude
|
|
agent-sandbox --unsetenv HTTP_PROXY -- claude
|
|
```
|
|
|
|
## Ubuntu 23.10+: AppArmor unprivileged userns restrictions
|
|
|
|
Ubuntu 23.10 and later ship with `kernel.apparmor_restrict_unprivileged_userns=1`, which blocks `bwrap` from creating user namespaces and causes failures like `bwrap: setting up uid map: Permission denied`. Ubuntu does not enable a profile for `bwrap` by default, but the `apparmor-profiles` package ships an opt-in `bwrap-userns-restrict` profile that grants the necessary access while still preventing `bwrap` from being used to bypass the userns restriction generally:
|
|
|
|
```bash
|
|
sudo apt install apparmor-profiles
|
|
sudo ln -s /usr/share/apparmor/extra-profiles/bwrap-userns-restrict /etc/apparmor.d/
|
|
sudo apparmor_parser -r /etc/apparmor.d/bwrap-userns-restrict
|
|
```
|