* fix(security): mount host Docker socket only in aio (DooD) sandbox mode The default Compose stack mounted /var/run/docker.sock read-write into the root gateway container in every sandbox mode, including the default `local` mode that never uses it -- an unnecessary host-escape surface (DooD = root-equivalent host control). deploy.sh already gated the socket *check* on sandbox_mode != local, but the Compose files mounted it unconditionally. Move the socket mount to an opt-in docker/docker-compose.dood.yaml overlay that deploy.sh / docker.sh append only when detect_sandbox_mode() returns `aio`. Default (local) and provisioner/Kubernetes modes no longer expose the host daemon. Tighten the socket existence check from != local to == aio. Document the DooD threat model in SECURITY.md. Reported by @greatmengqi. * refactor(docker): address review on socket-hardening PR - docker.sh: use absolute path for the dood overlay (match deploy.sh, drop cwd dependency) - deploy.sh: drop now-dead DEER_FLOW_DOCKER_SOCKET exports in down/build paths - docker-compose.yaml: fix stale header comment to point at the overlay Addresses codex + reviewer feedback on #3517. --------- Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
4.7 KiB
Security Policy
Supported Versions
As deer-flow doesn't provide an official release yet, please use the latest version for the security updates. Currently, we have two branches to maintain:
- main branch for deer-flow 2.x
- main-1.x branch for deer-flow 1.x
Reporting a Vulnerability
Please go to https://github.com/bytedance/deer-flow/security to report the vulnerability you find.
Sandbox Isolation and the Docker Socket (DooD)
DeerFlow executes agent-generated shell/code through a configurable sandbox
(sandbox.use in config.yaml). The isolation guarantees differ by mode, and
one mode requires mounting the host Docker socket. Understand the trade-offs
before exposing an instance to untrusted input.
| Mode | config.yaml |
Host Docker socket | Isolation |
|---|---|---|---|
local (default) |
deerflow.sandbox.local:LocalSandboxProvider |
Not mounted | Commands run inside the gateway container on its filesystem. Not a strong boundary — allow_host_bash is false by default and should stay off for untrusted workloads. |
aio (pure DooD) |
deerflow.community.aio_sandbox:AioSandboxProvider (no provisioner_url) |
Mounted (opt-in overlay) | Sandbox containers are started via the host Docker daemon. |
provisioner (Kubernetes) |
AioSandboxProvider + provisioner_url |
Not mounted | Sandbox pods are created through the provisioner's K8s API over HTTP. Strongest isolation. |
The Docker socket is host root
Mounting /var/run/docker.sock into a container grants that container
root-equivalent control of the host: anything able to reach the socket can
start a new container that bind-mounts the host filesystem and escape. This
matters for DeerFlow because the gateway executes model-generated commands, so a
prompt injection or any in-container code-execution primitive could pivot to the
host through the socket.
To keep this off the default attack surface:
- The host Docker socket is not mounted by the default Compose stack. It is
added only for
aiomode through the opt-indocker/docker-compose.dood.yamloverlay, whichscripts/deploy.shandscripts/docker.shappend automatically whendetect_sandbox_mode()returnsaio. - Prefer provisioner/Kubernetes mode for multi-tenant or internet-exposed deployments — it isolates sandboxes without handing the gateway the host daemon.
- If you must use
aio/DooD, treat the host as part of the gateway's trust boundary: run it on a dedicated host, and consider a scoped Docker API proxy instead of the raw socket.
Note: the gateway bind-mounts
$HOME/.claudeand$HOME/.codex(read-only) for CLI auto-auth in all modes. These hold long-lived CLI credentials; scope or omit them when the gateway runs untrusted workloads.
CLI Credential Mounts (Claude Code / Codex)
DeerFlow can reuse your Claude Code / Codex CLI subscription login as a model
provider (ClaudeChatModel, the Codex provider) or for ACP agents that run the
CLI in-container. The Compose stack used to bind-mount the entire ~/.claude
and ~/.codex directories (read-only) into the gateway container in every
configuration — exposing not just credentials but full conversation history,
per-project session data, and global CLI config. A gateway compromise (prompt
injection, tool/MCP misuse, RCE) would leak all of it.
These directories are no longer mounted by default. Supply CLI credentials with the least exposure that fits your setup:
| Need | How | Exposure |
|---|---|---|
| Claude model provider | env CLAUDE_CODE_OAUTH_TOKEN / ANTHROPIC_AUTH_TOKEN (via .env), or CLAUDE_CODE_CREDENTIALS_PATH → a single mounted .credentials.json |
none / one file |
| Codex model provider | env CODEX_AUTH_PATH pointing at a single mounted auth.json |
one file |
| ACP agent | the adapter's own auth — many ACP adapters take an env API key (e.g. ANTHROPIC_API_KEY / OPENAI_API_KEY) and need no mount; use the opt-in docker/docker-compose.cli-auth.yaml overlay only if your adapter reads the full CLI config dir |
none / full dir |
The Gateway credential loader checks environment variables before the
default credential files, so the env-token paths need no bind mount at all. ACP
adapters authenticate independently of DeerFlow via their own documented env —
for example the common claude-code-acp adapter starts as
ANTHROPIC_API_KEY=… claude-code-acp and honors CLAUDE_CONFIG_DIR to redirect
its config directory, so it needs no ~/.claude mount at all. Prefer the
adapter's documented env auth, and reach for the
docker-compose.cli-auth.yaml overlay only as a fallback for an adapter that
genuinely reads the full CLI config directory.