mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
* Gateway: honor OPENCLAW_GATEWAY_URL override for remote/local calls * Agents: fix sandbox sessionKey usage for PI embedded subagent calls * Sandbox: tighten browser container Chromium runtime flags * fix: add sandbox browser defaults for container hardening * docs: expand sandbox browser default flags list * fix: make sandbox browser flags optional and preserve gateway env auth overrides * docs: scope PR 31504 changelog entry * style: format gateway call override handling * fix: dedupe sandbox browser chrome args * fix: preserve remote tls fingerprint for env gateway override * fix: enforce auth for env gateway URL override * chore: document gateway override auth security expectations
260 lines
10 KiB
Markdown
260 lines
10 KiB
Markdown
---
|
||
summary: "How OpenClaw sandboxing works: modes, scopes, workspace access, and images"
|
||
title: Sandboxing
|
||
read_when: "You want a dedicated explanation of sandboxing or need to tune agents.defaults.sandbox."
|
||
status: active
|
||
---
|
||
|
||
# Sandboxing
|
||
|
||
OpenClaw can run **tools inside Docker containers** to reduce blast radius.
|
||
This is **optional** and controlled by configuration (`agents.defaults.sandbox` or
|
||
`agents.list[].sandbox`). If sandboxing is off, tools run on the host.
|
||
The Gateway stays on the host; tool execution runs in an isolated sandbox
|
||
when enabled.
|
||
|
||
This is not a perfect security boundary, but it materially limits filesystem
|
||
and process access when the model does something dumb.
|
||
|
||
## What gets sandboxed
|
||
|
||
- Tool execution (`exec`, `read`, `write`, `edit`, `apply_patch`, `process`, etc.).
|
||
- Optional sandboxed browser (`agents.defaults.sandbox.browser`).
|
||
- By default, the sandbox browser auto-starts (ensures CDP is reachable) when the browser tool needs it.
|
||
Configure via `agents.defaults.sandbox.browser.autoStart` and `agents.defaults.sandbox.browser.autoStartTimeoutMs`.
|
||
- By default, sandbox browser containers use a dedicated Docker network (`openclaw-sandbox-browser`) instead of the global `bridge` network.
|
||
Configure with `agents.defaults.sandbox.browser.network`.
|
||
- Optional `agents.defaults.sandbox.browser.cdpSourceRange` restricts container-edge CDP ingress with a CIDR allowlist (for example `172.21.0.1/32`).
|
||
- noVNC observer access is password-protected by default; OpenClaw emits a short-lived token URL that serves a local bootstrap page and opens noVNC with password in URL fragment (not query/header logs).
|
||
- `agents.defaults.sandbox.browser.allowHostControl` lets sandboxed sessions target the host browser explicitly.
|
||
- Optional allowlists gate `target: "custom"`: `allowedControlUrls`, `allowedControlHosts`, `allowedControlPorts`.
|
||
|
||
Not sandboxed:
|
||
|
||
- The Gateway process itself.
|
||
- Any tool explicitly allowed to run on the host (e.g. `tools.elevated`).
|
||
- **Elevated exec runs on the host and bypasses sandboxing.**
|
||
- If sandboxing is off, `tools.elevated` does not change execution (already on host). See [Elevated Mode](/tools/elevated).
|
||
|
||
## Modes
|
||
|
||
`agents.defaults.sandbox.mode` controls **when** sandboxing is used:
|
||
|
||
- `"off"`: no sandboxing.
|
||
- `"non-main"`: sandbox only **non-main** sessions (default if you want normal chats on host).
|
||
- `"all"`: every session runs in a sandbox.
|
||
Note: `"non-main"` is based on `session.mainKey` (default `"main"`), not agent id.
|
||
Group/channel sessions use their own keys, so they count as non-main and will be sandboxed.
|
||
|
||
## Scope
|
||
|
||
`agents.defaults.sandbox.scope` controls **how many containers** are created:
|
||
|
||
- `"session"` (default): one container per session.
|
||
- `"agent"`: one container per agent.
|
||
- `"shared"`: one container shared by all sandboxed sessions.
|
||
|
||
## Workspace access
|
||
|
||
`agents.defaults.sandbox.workspaceAccess` controls **what the sandbox can see**:
|
||
|
||
- `"none"` (default): tools see a sandbox workspace under `~/.openclaw/sandboxes`.
|
||
- `"ro"`: mounts the agent workspace read-only at `/agent` (disables `write`/`edit`/`apply_patch`).
|
||
- `"rw"`: mounts the agent workspace read/write at `/workspace`.
|
||
|
||
Inbound media is copied into the active sandbox workspace (`media/inbound/*`).
|
||
Skills note: the `read` tool is sandbox-rooted. With `workspaceAccess: "none"`,
|
||
OpenClaw mirrors eligible skills into the sandbox workspace (`.../skills`) so
|
||
they can be read. With `"rw"`, workspace skills are readable from
|
||
`/workspace/skills`.
|
||
|
||
## Custom bind mounts
|
||
|
||
`agents.defaults.sandbox.docker.binds` mounts additional host directories into the container.
|
||
Format: `host:container:mode` (e.g., `"/home/user/source:/source:rw"`).
|
||
|
||
Global and per-agent binds are **merged** (not replaced). Under `scope: "shared"`, per-agent binds are ignored.
|
||
|
||
`agents.defaults.sandbox.browser.binds` mounts additional host directories into the **sandbox browser** container only.
|
||
|
||
- When set (including `[]`), it replaces `agents.defaults.sandbox.docker.binds` for the browser container.
|
||
- When omitted, the browser container falls back to `agents.defaults.sandbox.docker.binds` (backwards compatible).
|
||
|
||
Example (read-only source + an extra data directory):
|
||
|
||
```json5
|
||
{
|
||
agents: {
|
||
defaults: {
|
||
sandbox: {
|
||
docker: {
|
||
binds: ["/home/user/source:/source:ro", "/var/data/myapp:/data:ro"],
|
||
},
|
||
},
|
||
},
|
||
list: [
|
||
{
|
||
id: "build",
|
||
sandbox: {
|
||
docker: {
|
||
binds: ["/mnt/cache:/cache:rw"],
|
||
},
|
||
},
|
||
},
|
||
],
|
||
},
|
||
}
|
||
```
|
||
|
||
Security notes:
|
||
|
||
- Binds bypass the sandbox filesystem: they expose host paths with whatever mode you set (`:ro` or `:rw`).
|
||
- OpenClaw blocks dangerous bind sources (for example: `docker.sock`, `/etc`, `/proc`, `/sys`, `/dev`, and parent mounts that would expose them).
|
||
- Sensitive mounts (secrets, SSH keys, service credentials) should be `:ro` unless absolutely required.
|
||
- Combine with `workspaceAccess: "ro"` if you only need read access to the workspace; bind modes stay independent.
|
||
- See [Sandbox vs Tool Policy vs Elevated](/gateway/sandbox-vs-tool-policy-vs-elevated) for how binds interact with tool policy and elevated exec.
|
||
|
||
## Images + setup
|
||
|
||
Default image: `openclaw-sandbox:bookworm-slim`
|
||
|
||
Build it once:
|
||
|
||
```bash
|
||
scripts/sandbox-setup.sh
|
||
```
|
||
|
||
Note: the default image does **not** include Node. If a skill needs Node (or
|
||
other runtimes), either bake a custom image or install via
|
||
`sandbox.docker.setupCommand` (requires network egress + writable root +
|
||
root user).
|
||
|
||
If you want a more functional sandbox image with common tooling (for example
|
||
`curl`, `jq`, `nodejs`, `python3`, `git`), build:
|
||
|
||
```bash
|
||
scripts/sandbox-common-setup.sh
|
||
```
|
||
|
||
Then set `agents.defaults.sandbox.docker.image` to
|
||
`openclaw-sandbox-common:bookworm-slim`.
|
||
|
||
Sandboxed browser image:
|
||
|
||
```bash
|
||
scripts/sandbox-browser-setup.sh
|
||
```
|
||
|
||
By default, sandbox containers run with **no network**.
|
||
Override with `agents.defaults.sandbox.docker.network`.
|
||
|
||
The bundled sandbox browser image also applies conservative Chromium startup defaults
|
||
for containerized workloads. Current container defaults include:
|
||
|
||
- `--remote-debugging-address=127.0.0.1`
|
||
- `--remote-debugging-port=<derived from OPENCLAW_BROWSER_CDP_PORT>`
|
||
- `--user-data-dir=${HOME}/.chrome`
|
||
- `--no-first-run`
|
||
- `--no-default-browser-check`
|
||
- `--disable-3d-apis`
|
||
- `--disable-gpu`
|
||
- `--disable-dev-shm-usage`
|
||
- `--disable-background-networking`
|
||
- `--disable-extensions`
|
||
- `--disable-features=TranslateUI`
|
||
- `--disable-breakpad`
|
||
- `--disable-crash-reporter`
|
||
- `--disable-software-rasterizer`
|
||
- `--no-zygote`
|
||
- `--metrics-recording-only`
|
||
- `--renderer-process-limit=2`
|
||
- `--no-sandbox` and `--disable-setuid-sandbox` when `noSandbox` is enabled.
|
||
- The three graphics hardening flags (`--disable-3d-apis`,
|
||
`--disable-software-rasterizer`, `--disable-gpu`) are optional and are useful
|
||
when containers lack GPU support. Set `OPENCLAW_BROWSER_DISABLE_GRAPHICS_FLAGS=0`
|
||
if your workload requires WebGL or other 3D/browser features.
|
||
- `--disable-extensions` is enabled by default and can be disabled with
|
||
`OPENCLAW_BROWSER_DISABLE_EXTENSIONS=0` for extension-reliant flows.
|
||
- `--renderer-process-limit=2` is controlled by
|
||
`OPENCLAW_BROWSER_RENDERER_PROCESS_LIMIT=<N>`, where `0` keeps Chromium's default.
|
||
|
||
If you need a different runtime profile, use a custom browser image and provide
|
||
your own entrypoint. For local (non-container) Chromium profiles, use
|
||
`browser.extraArgs` to append additional startup flags.
|
||
|
||
Security defaults:
|
||
|
||
- `network: "host"` is blocked.
|
||
- `network: "container:<id>"` is blocked by default (namespace join bypass risk).
|
||
- Break-glass override: `agents.defaults.sandbox.docker.dangerouslyAllowContainerNamespaceJoin: true`.
|
||
|
||
Docker installs and the containerized gateway live here:
|
||
[Docker](/install/docker)
|
||
|
||
For Docker gateway deployments, `docker-setup.sh` can bootstrap sandbox config.
|
||
Set `OPENCLAW_SANDBOX=1` (or `true`/`yes`/`on`) to enable that path. You can
|
||
override socket location with `OPENCLAW_DOCKER_SOCKET`. Full setup and env
|
||
reference: [Docker](/install/docker#enable-agent-sandbox-for-docker-gateway-opt-in).
|
||
|
||
## setupCommand (one-time container setup)
|
||
|
||
`setupCommand` runs **once** after the sandbox container is created (not on every run).
|
||
It executes inside the container via `sh -lc`.
|
||
|
||
Paths:
|
||
|
||
- Global: `agents.defaults.sandbox.docker.setupCommand`
|
||
- Per-agent: `agents.list[].sandbox.docker.setupCommand`
|
||
|
||
Common pitfalls:
|
||
|
||
- Default `docker.network` is `"none"` (no egress), so package installs will fail.
|
||
- `docker.network: "container:<id>"` requires `dangerouslyAllowContainerNamespaceJoin: true` and is break-glass only.
|
||
- `readOnlyRoot: true` prevents writes; set `readOnlyRoot: false` or bake a custom image.
|
||
- `user` must be root for package installs (omit `user` or set `user: "0:0"`).
|
||
- Sandbox exec does **not** inherit host `process.env`. Use
|
||
`agents.defaults.sandbox.docker.env` (or a custom image) for skill API keys.
|
||
|
||
## Tool policy + escape hatches
|
||
|
||
Tool allow/deny policies still apply before sandbox rules. If a tool is denied
|
||
globally or per-agent, sandboxing doesn’t bring it back.
|
||
|
||
`tools.elevated` is an explicit escape hatch that runs `exec` on the host.
|
||
`/exec` directives only apply for authorized senders and persist per session; to hard-disable
|
||
`exec`, use tool policy deny (see [Sandbox vs Tool Policy vs Elevated](/gateway/sandbox-vs-tool-policy-vs-elevated)).
|
||
|
||
Debugging:
|
||
|
||
- Use `openclaw sandbox explain` to inspect effective sandbox mode, tool policy, and fix-it config keys.
|
||
- See [Sandbox vs Tool Policy vs Elevated](/gateway/sandbox-vs-tool-policy-vs-elevated) for the “why is this blocked?” mental model.
|
||
Keep it locked down.
|
||
|
||
## Multi-agent overrides
|
||
|
||
Each agent can override sandbox + tools:
|
||
`agents.list[].sandbox` and `agents.list[].tools` (plus `agents.list[].tools.sandbox.tools` for sandbox tool policy).
|
||
See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for precedence.
|
||
|
||
## Minimal enable example
|
||
|
||
```json5
|
||
{
|
||
agents: {
|
||
defaults: {
|
||
sandbox: {
|
||
mode: "non-main",
|
||
scope: "session",
|
||
workspaceAccess: "none",
|
||
},
|
||
},
|
||
},
|
||
}
|
||
```
|
||
|
||
## Related docs
|
||
|
||
- [Sandbox Configuration](/gateway/configuration#agentsdefaults-sandbox)
|
||
- [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools)
|
||
- [Security](/gateway/security)
|