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:
parent
af2e69a4bb
commit
c5f6764470
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue