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
|
USER root
|
||||||
RUN npm install -g "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" \
|
RUN npm install -g "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" \
|
||||||
&& claude --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
|
COPY cleanup.sh /opt/cleanup.sh
|
||||||
RUN chmod +x /opt/cleanup.sh
|
RUN chmod +x /opt/cleanup.sh
|
||||||
USER user
|
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