Trying out bubblewrap used in Claude Code's Sandbox Runtime and exploring its network restriction mechanism

llmlinux

Claude Code has a Sandboxing feature that isolates filesystem and network to run agents safely while giving them strong permissions. On Linux, this is implemented using bubblewrap. bubblewrap isolates each namespace by calling the clone(2) system call with flags such as CLONE_NEWNET and CLONE_NEWPID.

$ sudo apt install bubblewrap
$ bwrap \
  --ro-bind /usr /usr \
  --ro-bind /lib /lib \
  --ro-bind /lib64 /lib64 \
  --unshare-net \
  ip addr

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever

 $ bwrap \
  --ro-bind /usr /usr \
  --ro-bind /lib /lib \
  --ro-bind /lib64 /lib64 \
  --proc /proc \
  --unshare-pid \
  ps aux

USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
1000           1  0.0  0.0   3584  1420 ?        S+   07:14   0:00 bwrap --ro-bind /usr /usr --ro-bind /lib /lib --ro-bi
1000           2  0.0  0.0   8284  4096 ?        R+   07:14   0:00 ps aux

Unlike Docker, there’s no need to run and communicate with a daemon, allowing execution processes with minimal overhead.

$ time for i in {1..100}; do
  echo "test" > /dev/null
done

real    0m0.022s
user    0m0.019s
sys     0m0.003s

$ time for i in {1..100}; do
  bwrap --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind /lib64 /lib64 --unshare-all echo "test" > /dev/null
done

real    0m0.374s
user    0m0.125s
sys     0m0.095s

$ time for i in {1..100}; do
  docker run --rm alpine echo "test" > /dev/null
done

real    0m1.126s
user    0m0.254s
sys     0m0.158s

Sandbox Runtime (srt) uses bwrap --unshare-net to block external communication, while using the socat command to forward the sandbox’s localhost:{port} to a socket, and setting HTTP(S)_PROXY to localhost:{port}, allowing external access through the host’s proxy server. This server performs filtering based on allowed/denied domains.

$ npm install -g @anthropic-ai/sandbox-runtime
$ sudo apt install ripgrep socat
$ srt --version
1.0.0

$ cat ~/.srt-settings.json
{
  "filesystem": {
    "denyRead": [],
    "allowWrite": ["."],
    "denyWrite": []
  },
  "network": {
    "allowedDomains": ["example.com"],
    "deniedDomains": []
  }
}

$ SRT_DEBUG=1 srt curl "sambaiz.net"
...
[SandboxDebug] HTTP proxy listening on localhost:44889
[SandboxDebug] SOCKS proxy listening on 127.0.0.1:44311
[SandboxDebug] Starting HTTP bridge: socat UNIX-LISTEN:/tmp/claude-http-59898d4a7ef502ee.sock,fork,reuseaddr TCP:localhost:44889,keepalive,keepidle=10,keepintvl=5,keepcnt=3
[SandboxDebug] Starting SOCKS bridge: socat UNIX-LISTEN:/tmp/claude-socks-59898d4a7ef502ee.sock,fork,reuseaddr TCP:localhost:44311,keepalive,keepidle=10,keepintvl=5,keepcnt=3
...
[SandboxDebug] No matching config rule, denying: sambaiz.net:80
[SandboxDebug] HTTP request blocked to sambaiz.net:80
Connection blocked by network allowlist[SandboxDebug] Sent SIGTERM to HTTP bridge process
[SandboxDebug] Sent SIGTERM to SOCKS bridge process

Besides, Claude Code’s /sandbox currently does not support Windows including WSL2.

> /sandbox
  ⎿  Error: Sandboxing is currently only supported on macOS and Linux

However, it can be run using the srt command. You need to allow the required domains to run Claude Code.

$ cat ~/.srt-settings.json
{
  "filesystem": {
    "denyRead": [
      "~/.ssh",
      "~/.aws"
    ],
    "allowWrite": [
      ".",
      "/tmp",
      "~/.claude.json",
      "~/.claude"
    ],
    "denyWrite": [
      ".env"
    ]
  },
  "network": {
    "allowedDomains": [
      "api.anthropic.com",
      "claude.ai",
      "statsig.anthropic.com",
      "sentry.io"
    ],
    "deniedDomains": []
  },
  "ignoreViolations": {
    "*": [
      "/usr/bin",
      "/System"
    ]
  }
}

$ srt "claude --dangerously-skip-permissions --print \"run a command: curl sambaiz.net\""
The curl command attempted to connect to sambaiz.net but the connection was blocked by a network allowlist. This means that network access to this domain is restricted in the current environment.

The response shows that only 39 bytes were received before the connection was blocked, suggesting some initial data was retrieved but the full content couldn't be fetched due to network restrictions.