Skip to content

Replace Docker sandbox with Claude Code built-in sandboxing #980

@james-in-a-box

Description

@james-in-a-box

Summary

Investigate replacing the custom Docker-based egg sandbox with Claude Code's built-in sandboxing (@anthropic-ai/sandbox-runtime), while keeping the gateway sidecar for policy enforcement, credential isolation, and network filtering.

Motivation

The current sandbox uses a full Docker container with custom Squid proxy, dual Docker networks, and binary relocation tricks. Claude Code now ships with OS-native sandboxing (bubblewrap + seccomp on Linux, Seatbelt on macOS) that provides filesystem and network isolation out of the box. Moving to it would simplify deployment and eliminate container build/orchestration overhead.

Key Finding: httpProxyPort / socksProxyPort

The sandbox-runtime supports replacing its built-in proxy with an external one. When httpProxyPort is set, SRT skips starting its own HTTP proxy and bridges all sandboxed traffic to the specified port. This means we can route everything through the gateway sidecar:

Bubblewrap sandbox (no network namespace)
  → socat bridge (Unix socket)
  → Gateway sidecar (HTTP proxy + REST API)
  → Internet (filtered by mode)

Architecture Changes Required

Component Current (Docker) Proposed (Claude Code sandbox)
Network isolation Docker network namespace + Squid Bubblewrap network namespace + socat bridge
Proxy Squid (SNI filtering) Gateway as HTTP CONNECT proxy
Git/GH interception Binary relocation to /opt/.egg-internal/ PATH-based wrappers + NO_PROXY=localhost
Credentials Not in container at all denyRead on credential paths
Public/private mode Docker network assignment Gateway domain allowlist config
Binary availability Only what Dockerfile installs Host binaries available (deny what you can)
Anthropic API proxy ANTHROPIC_BASE_URL → gateway Same, unchanged

Gotchas and Open Questions

1. Gateway must become an HTTP CONNECT proxy

The gateway currently exposes a REST API. It needs to also handle CONNECT tunnel requests to act as a forward proxy. Recommended approach: two ports — REST API on 9848 (for git/gh wrappers via NO_PROXY=localhost), HTTP proxy on 3129 (for general traffic via httpProxyPort).

2. Host binary contamination (gcloud, aws, docker, etc.)

Docker gives default-deny: nothing exists unless installed. Claude Code sandbox gives default-allow with deny rules. Binaries like gcloud are present on the host and functional if their domains are allowed and credentials are readable.

Mitigations:

  • denyRead on ~/.config/gcloud/, ~/.aws/, ~/.ssh/, ~/.kube/, ~/.docker/, ~/.gnupg/
  • Domain blocking at the gateway level
  • Linux denyRead does not support glob patterns — paths must be enumerated literally

3. Programs ignoring HTTP_PROXY silently fail

Bubblewrap removes the network namespace entirely. Programs respecting HTTP_PROXY/HTTPS_PROXY work; others get "network unreachable". This is actually stronger than Docker (hard block vs soft), but may break tools with custom network stacks.

4. Public/private mode switching

Currently per-container via Docker network assignment. New approach: configure gateway domain allowlist at startup. SRT supports dynamic config updates via --control-fd (JSON-lines), enabling runtime mode switching without restart.

5. excludedCommands interaction

docker is explicitly incompatible with the sandbox and must be in excludedCommands. Known bug: when allowUnsandboxedCommands: false, excluded commands still attempt sandboxed execution first and fail (anthropics/claude-code#19135).

6. Linux-specific limitations

  • No glob patterns in filesystem paths (only literal paths)
  • Bind-mount deny rules only affect files existing at sandbox creation time
  • Requires bubblewrap and socat packages on host

7. Credential isolation is weaker

Docker: zero credentials in container. Claude Code sandbox: credentials exist on host, blocked via denyRead. Environment variables are inherited unless scrubbed before launch. Need explicit env scrubbing for GITHUB_TOKEN, cloud credentials, etc.

8. Nested sandbox concerns

If running Claude Code inside Docker (e.g., CI), enableWeakerNestedSandbox: true is required, which substantially weakens namespace isolation. The outer container provides the boundary in that case.

Proposed Settings

{
  "sandbox": {
    "network": {
      "httpProxyPort": 3129,
      "socksProxyPort": 1080,
      "allowedDomains": ["*"]
    },
    "filesystem": {
      "denyRead": ["~/.ssh", "~/.aws", "~/.config/gcloud", "~/.kube", "~/.docker", "~/.gnupg"],
      "allowWrite": ["."],
      "denyWrite": [".env", "secrets.env"]
    }
  }
}

Tasks

  • Add HTTP CONNECT proxy capability to the gateway sidecar
  • Convert git/gh wrappers from binary-relocation to PATH-based approach
  • Implement credential path deny list for host environments
  • Add environment variable scrubbing at launch
  • Implement public/private mode via gateway domain allowlist config
  • Test gcloud, aws, docker behavior under sandbox restrictions
  • Validate git/gh wrapper behavior with NO_PROXY=localhost bypass
  • Evaluate --control-fd for runtime mode switching

— Authored by egg

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions