feat(sandbox): enable Claude Code built-in sandbox for Bash + WebFetch egress

Closes #10. Until now the sandbox container had unrestricted outbound NAT
on sandbox-net — a compromised agent could curl arbitrary hosts, pip/npm
install malicious packages, or exfiltrate files via WebFetch. The
anthropic-proxy path-allowlist (#6) guarded only the credential-injection
proxy, not general egress.

Two layers added, both shipped as managed (enterprise) settings so user
and project settings cannot downgrade them:

- sandbox.*  → bubblewrap + local proxy gating Bash subprocesses (and all
  their children: pip, npm, git, curl). enableWeakerNestedSandbox=true
  because we run inside Docker without privileged user namespaces;
  accepted because the Docker container boundary is unchanged and this
  adds new egress filtering that previously did not exist.
- permissions.* → deny-by-default WebFetch with explicit domain allows
  (WebFetch bypasses the OS sandbox and needs its own allowlist).

failIfUnavailable=true + allowUnsandboxedCommands=false close the two
silent-fallback paths: if bubblewrap/socat install breaks, the container
fails loud instead of serving unsandboxed bash; and the
dangerouslyDisableSandbox escape hatch is disabled so sandbox violations
can't be bypassed via a permission prompt in our unattended deployment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Thomas Friedel 2026-04-18 23:31:07 +02:00
parent af2e69a4bb
commit c5f6764470
2 changed files with 51 additions and 0 deletions

View File

@ -13,6 +13,22 @@ ARG CLAUDE_CODE_VERSION=2.1.114
USER root
RUN npm install -g "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" \
&& claude --version
# bubblewrap + socat are the Linux backends for Claude Code's built-in
# `/sandbox` feature. Without them, Claude falls back to unsandboxed bash —
# our managed-settings.json sets failIfUnavailable=true so that silent
# degradation becomes a hard error instead.
RUN apt-get update \
&& apt-get install -y --no-install-recommends bubblewrap socat \
&& rm -rf /var/lib/apt/lists/*
# Managed (enterprise) settings live at the system-wide path and take
# precedence over per-user and per-project settings. This is how we pin
# Bash egress + WebFetch allowlists: users can extend via their own
# settings, but cannot downgrade these rules.
COPY managed-settings.json /etc/claude-code/managed-settings.json
RUN chmod 0644 /etc/claude-code/managed-settings.json
COPY cleanup.sh /opt/cleanup.sh
RUN chmod +x /opt/cleanup.sh
USER user

View File

@ -0,0 +1,35 @@
{
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false,
"allowManagedDomainsOnly": true,
"enableWeakerNestedSandbox": true,
"network": {
"allowedDomains": [
"pypi.org",
"files.pythonhosted.org",
"registry.npmjs.org",
"github.com",
"api.github.com",
"raw.githubusercontent.com",
"objects.githubusercontent.com",
"codeload.github.com",
"anthropic-proxy"
]
}
},
"permissions": {
"deny": [
"WebFetch"
],
"allow": [
"WebFetch(domain:github.com)",
"WebFetch(domain:raw.githubusercontent.com)",
"WebFetch(domain:docs.anthropic.com)",
"WebFetch(domain:code.claude.com)",
"WebFetch(domain:pypi.org)",
"WebFetch(domain:registry.npmjs.org)"
]
}
}