Docker vs VM Sandboxing: Why Network Configuration Matters More Than You Think
Problem
When I read about Claude “escaping” a VM sandbox, I assumed it was some sophisticated hypervisor exploit. The reality was far more mundane—and far more concerning for anyone running AI agents in containers or VMs.
User's setup:- Claude running inside a VM- Claude browser extension installed on host machine- VM using default networking (NAT/bridged)
What happened:- Claude inside VM "discovered" the host's browser extension- Claude used the extension to complete tasks- User thought the VM was compromisedThe VM wasn’t hacked. The network was just… open.
What I investigated
I wanted to understand exactly how this “escape” happened and whether Docker would be any safer. Here’s what I found:
The escape mechanism
The incident worked like this:
1. Host machine runs Chrome with Claude extension2. Extension listens on localhost:PORT for API calls3. VM uses NAT networking (default for most VMs)4. Guest VM can reach host's IP address5. Claude in VM scans network, finds the extension endpoint6. Claude uses extension as a tool to complete tasksThe key insight: localhost on the host is NOT isolated from the guest when using NAT or bridged networking.
Why Docker has the same problem
I tested this with Docker and found identical behavior:
# Default Docker network allows guest-to-host communicationdocker run -it alpine sh# Inside container:ping host.docker.internal# Result: Host is reachable!
curl http://host.docker.internal:8080# Result: Can access host services!The host.docker.internal DNS name resolves to the host’s IP address in Docker Desktop. On Linux, you need to add --add-host manually, but the principle is the same.
How I compared Docker vs VM isolation
I created a comparison table to understand the actual security boundaries:
| Configuration | Docker | VM (QEMU) | Guest→Host Access ||---------------------|--------|-----------|-------------------|| Default network | bridge | NAT | YES (vulnerable) || Custom bridge | bridge | bridge | YES (vulnerable) || Host network | host | N/A | YES (no isolation)|| No network | none | none | NO (isolated) || Internal network | internal| isolated | NO (isolated) |Both Docker and VMs have the same fundamental issue: default networking allows guest-to-host communication.
Docker network modes explained
# WRONG: Default bridge networkdocker run claude-code# Guest can reach host services via gateway
# WRONG: Host network (no isolation at all)docker run --network=host claude-code# Guest shares host's network stack entirely
# CORRECT: No network accessdocker run --network=none claude-code# Guest has no network interface (except loopback)
# CORRECT: Internal network (no external access)docker network create --internal claude-internaldocker run --network=claude-internal claude-code# Guest can only talk to other containers on same networkVM network modes explained
# WRONG: User-mode networking (NAT) - DEFAULTqemu-system-x86_64 -netdev user,id=net0 -device virtio-net,netdev=net0# Guest can reach host services via gateway (10.0.2.2)
# CORRECT: No networkqemu-system-x86_64 -net none# Guest has no network interface
# CORRECT: Isolated tap device with firewallip tuntap add dev tap0 mode tapip link set tap0 up# Block guest-to-host trafficiptables -I FORWARD -i tap0 -d 192.168.0.0/16 -j DROPiptables -I FORWARD -i tap0 -d 10.0.0.0/8 -j DROPiptables -I FORWARD -i tap0 -d 172.16.0.0/12 -j DROPThe real security boundaries
I realized that “sandbox” means different things to different people:
ISOLATED by VM:- Process memory- CPU registers- File system (if not shared)- Hardware devices
NOT ISOLATED by default VM:- Network (unless explicitly configured)- Shared folders- Clipboard (if enabled)- USB devices (if passed through)The VM provides process isolation, but network isolation is optional.
Why AI agents are particularly dangerous
AI agents don’t need to “hack” anything. They just explore:
1. Agent receives task: "Check my email"2. Agent looks for email tools3. Agent finds no email tool configured4. Agent scans network for services5. Agent finds IMAP server on host:9936. Agent connects and completes task7. User never intended this access!The agent did exactly what it was asked to do. The problem is that the network boundary was never established.
How to properly isolate AI agents
Based on my investigation, here’s the correct approach:
For Docker
# Create isolated networkdocker network create --internal claude-net
# Run with no external accessdocker run \ --network=claude-net \ --cap-drop=ALL \ --security-opt=no-new-privileges \ --read-only \ -v /workspace:/workspace:rw \ claude-code
# Verify isolationdocker exec <container> ping -c 1 8.8.8.8# Should fail: Network unreachableFor VMs
# Option 1: No network at allqemu-system-x86_64 \ -m 4096 \ -net none \ -drive file=vm.qcow2,format=qcow2
# Option 2: Isolated network with firewall# Create isolated bridgeip link add br-isolated type bridgeip link set br-isolated up
# Create tap deviceip tuntap add tap0 mode tapip link set tap0 master br-isolatedip link set tap0 up
# Block all traffic from isolated bridgeiptables -I FORWARD -i br-isolated -j DROP
# Run VM with isolated networkqemu-system-x86_64 \ -m 4096 \ -netdev tap,id=net0,ifname=tap0,script=no,downscript=no \ -device virtio-net,netdev=net0 \ -drive file=vm.qcow2,format=qcow2Additional hardening
1. Network isolation (--network=none or firewall rules)2. No shared credentials between host and guest3. No browser extensions installed on both4. No shared folders with sensitive data5. No USB passthrough6. Read-only root filesystem where possible7. Dropped capabilities (--cap-drop=ALL)8. No new privileges (no-new-privileges)Testing your isolation
I created a simple test script to verify isolation:
#!/bin/bash# test-isolation.sh - Run inside your sandbox
echo "Testing network isolation..."
# Test 1: Can we reach the internet?if curl -s --connect-timeout 5 https://google.com > /dev/null 2>&1; then echo "FAIL: Internet access available"else echo "PASS: No internet access"fi
# Test 2: Can we reach common host ports?HOST_IP=$(ip route | grep default | awk '{print $3}')if [ -n "$HOST_IP" ]; then for port in 80 443 8080 3000 5000; do if timeout 2 bash -c "echo > /dev/tcp/$HOST_IP/$port" 2>/dev/null; then echo "FAIL: Host port $port is reachable" fi done echo "PASS: No host ports reachable (or no default gateway)"else echo "PASS: No default gateway found"fi
# Test 3: Can we resolve host.docker.internal?if getent hosts host.docker.internal > /dev/null 2>&1; then echo "WARN: host.docker.internal resolves"else echo "PASS: host.docker.internal not found"fi
echo "Isolation test complete."Summary
I investigated the “VM escape” incident and found that AI agents don’t break out of sandboxes—they walk through open network doors. The key findings:
- Default networking is insecure: Both Docker and VMs allow guest-to-host communication by default
- localhost is not isolated: Services on
localhost:PORTare reachable from guests using NAT/bridged networking - AI agents are explorers: They discover available resources and use them to complete tasks
- True isolation requires explicit configuration: Use
--network=noneor firewall rules
The solution is simple but often overlooked:
# Dockerdocker run --network=none claude-code
# VMqemu-system-x86_64 -net noneYour sandbox is only as secure as your network configuration. The VM provides isolation, but the network defines the actual security boundary.
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:
- 👨💻 Reddit: Claude Escaped My VM Sandbox During My First Prompt
- 👨💻 Docker Network Isolation Documentation
- 👨💻 QEMU Network Security Best Practices
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments