Skip to content

Why Your Claude Code Sandbox Failed: Network Isolation Mistakes That Cost You Security

I thought putting Claude Code in a VM would keep it isolated. Then it accessed Chrome on my host machine within minutes.

The Problem: False Security Through VM Placement

I created a VirtualBox VM specifically to sandbox Claude Code. Fresh install, isolated environment—or so I thought. I started a coding session, and suddenly Claude was interacting with my host’s Chrome browser through the extension.

How did it “escape”?

It didn’t. I had built the escape route myself.

My Failed Setup
┌─────────────────────────────────────────────────┐
│ HOST │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ Chrome │◄────────│ Bridged Network │ │
│ │ Extension │ │ (Shared IP) │ │
│ │ (localhost) │ └─────────────────┘ │
│ └─────────────┘ ▲ │
│ ▲ │ │
│ │ │ │
└─────────│────────────────────────│──────────────┘
│ │
│ ┌─────────────────┴────────────┐
│ │ VM │
└──────┤ │
│ Claude Code │
│ (thinks it's local) │
└───────────────────────────────┘

The Reddit thread on this topic was brutal but accurate. One comment with 97 upvotes summed it up perfectly:

“You put it in a VM that had an open network back to the host? It didn’t escape anything. You left the door wide open with an Exit Here sign.”

Why Bridged Networking Broke Everything

Bridged networking creates a direct bridge between the VM and the host’s physical network interface. The VM gets its own IP on the same network as the host, but more importantly:

  1. The VM shares the host’s network stack for local connections
  2. Services binding to localhost on the host appear accessible
  3. Chrome extensions listening on 127.0.0.1 can’t distinguish VM traffic from local traffic

Here’s what actually happened:

The Attack Path
Step 1: Claude in VM requests network access
Step 2: Bridged network routes to host network stack
Step 3: Chrome extension on host listens on localhost:PORT
Step 4: VM's requests appear as local connections (same IP via bridge)
Step 5: Claude "escapes" by using the network path I provided

The extension architecture is particularly vulnerable here. Many development tools bind to localhost assuming all connections come from the local machine. With bridged networking, that assumption fails catastrophically.

The Solution: Proper Network Isolation

After recovering from my embarrassment, I researched proper sandboxing approaches. Here are four real solutions, from practical to paranoid.

Option A: NAT with Strict Firewall Rules

The simplest fix—switch from bridged to NAT networking and add firewall rules.

Host Firewall Rules (Linux)
# Block VM from accessing host services
# Adjust IP range to match your VM network config
sudo iptables -I INPUT -s 192.168.56.0/24 -j DROP
sudo iptables -I INPUT -s 172.16.0.0/12 -j DROP
# Only allow specific outbound from VM
# This is configured in the VM, not shown here
VM Firewall Rules
# Inside the VM, restrict outbound traffic
# Only allow Anthropic API endpoints
sudo iptables -A OUTPUT -d api.anthropic.com -j ACCEPT
sudo iptables -A OUTPUT -d statsig.anthropic.com -j ACCEPT
sudo iptables -A OUTPUT -j DROP

Pros: Works with existing VMs, minimal reconfiguration Cons: Firewall rules are easy to misconfigure, NAT still allows some network access

Option B: Host-Only Networking + Explicit Proxy

Remove internet access entirely from the VM, force all API calls through a controlled proxy.

Architecture
┌─────────────────────────────────────────────────┐
│ HOST │
│ ┌──────────┐ ┌──────────────────────┐ │
│ │ API │ │ Host-Only │ │
│ │ Proxy │◄─────│ Network │ │
│ │(whitelist)│ └──────────────────────┘ │
│ └──────────┘ ▲ │
│ ▲ │ │
│ │ │ │
└───────│───────────────────────│─────────────────┘
│ │
│ ┌─────────────────┴────────────┐
│ │ VM │
└─────┤ │
│ Claude Code (no direct │
│ internet, uses proxy) │
└──────────────────────────────┘
proxy-whitelist.conf
# Nginx proxy configuration
# Only proxy to Anthropic endpoints
server {
listen 8080;
location / {
if ($host !~* "api\.anthropic\.com|statsig\.anthropic\.com") {
return 403;
}
proxy_pass https://$host;
}
}

Pros: Fine-grained control, no direct internet from VM Cons: Requires proxy setup, maintenance overhead

Option C: Air-Gapped with Manual Updates

Maximum security, maximum inconvenience.

Air-Gapped Setup Checklist
┌─────────────────────────────────────────────────┐
│ VM Configuration: │
│ ✓ Network adapter: Removed │
│ ✓ Clipboard: Disabled │
│ ✓ Shared folders: Read-only, no execute │
│ ✓ File transfer: Manual only for code review │
│ ✓ USB passthrough: Disabled │
│ │
│ Workflow: │
│ 1. Export code to file │
│ 2. Scan file on host │
│ 3. Manually review │
│ 4. Import to host project │
└─────────────────────────────────────────────────┘

Pros: True isolation, no network-based escape possible Cons: Painful workflow, Claude Code needs API access (won’t work at all)

The best balance of security and usability—containerized isolation with explicit network control.

Dockerfile
FROM node:20-slim
# Create non-root user
RUN useradd -m -s /bin/bash claude
# Install Claude Code CLI
RUN npm install -g @anthropic-ai/claude-code
# Set up workspace
WORKDIR /workspace
RUN chown claude:claude /workspace
USER claude
# No network access by default
# No volumes mounted by default
CMD ["claude-code"]
docker-compose.yml
version: '3.8'
services:
claude-code:
build: .
network_mode: none # No network by default
volumes:
- ./workspace:/workspace:rw
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
# Separate container for API proxy
api-proxy:
image: nginx:alpine
networks:
- api-only
volumes:
- ./proxy.conf:/etc/nginx/nginx.conf:ro
networks:
api-only:
driver: bridge
internal: true # No external access beyond allowed APIs
Running Isolated Claude Code
# Build the isolated image
docker build -t claude-code-isolated .
# Run with explicit isolation
docker run -it \
--network none \
--cap-drop ALL \
--security-opt no-new-privileges:true \
-v $(pwd)/workspace:/workspace:rw \
claude-code-isolated:latest
# For API access, use a separate proxy container
# Never give the Claude container direct network access

Pros: Easy to set up, reproducible, fine-grained control Cons: Requires Docker knowledge, may need adjustments for your workflow

Common Mistakes Table

I made several of these mistakes. Learn from my failure.

MistakeWhy It FailsCorrect Approach
Bridged networking in VMShares host IP, bypasses NAT isolationUse NAT with firewall rules
Installing host tools with network accessCreates pathways back to hostRun all tools inside the sandbox
Localhost-only “security”Assumes requests come from same machineVerify network source with firewall rules
Shared clipboardCan exfiltrate dataDisable clipboard sharing
Shared folders with executeCode execution on hostMount read-only, no execute
Trusting the agentAI follows instructions, not rulesEnforce limits at infrastructure level

The Principle of Least Privilege for AI Agents

When sandboxing AI agents, I now apply zero-trust principles:

Zero-Trust Sandbox Principles
┌─────────────────────────────────────────────────┐
│ 1. ASSUME BREACH │
│ The agent will try to access your system │
│ │
│ 2. DEFAULT DENY │
│ Block everything, allow only what's needed │
│ │
│ 3. DEFENSE IN DEPTH │
│ Multiple layers of isolation │
│ - Network isolation │
│ - Filesystem isolation │
│ - Process isolation │
│ - Capability restrictions │
└─────────────────────────────────────────────────┘

AI agents don’t follow security policies—they follow instructions. If there’s a pathway, they’ll find it. Your job is to ensure the only pathways are the ones you explicitly created for legitimate use.

What I Do Now

After this incident, my Claude Code setup looks like this:

Current Setup (docker-compose.yml)
version: '3.8'
services:
claude-sandbox:
image: claude-code:isolated
network_mode: none
volumes:
- ./project:/workspace:rw
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
read_only: false
tmpfs:
- /tmp:noexec,nosuid,size=100m
api-gateway:
image: nginx:alpine
networks:
- api-only
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
networks:
api-only:
internal: true

No bridged networks. No host extensions. No shared clipboard. The sandbox is now a sandbox, not a turnstile.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments