A containerized sandbox for running Claude Code with --dangerously-skip-permissions safely using Podman.
- Isolated environment: Run Claude Code in a Debian Trixie container
- Network isolation modes:
- Internet access with local network isolation (default)
- Complete network isolation (--no-internet)
- TRUE network filtering via network namespace + iptables (--allow-hosts)
- Smart mounting:
- Sandboxed home: ~/.claudebox-home mounted as ~ in the container
- Current directory mounted read-write at the same path
- ~/git/ mounted read-write (when it exists)
- Permission safety: Runs with
--dangerously-skip-permissionsinside container - State persistence: Claude configuration and state preserved between runs
- Podman installed (tested with 5.4.2)
- Debian-based system (or similar)
- User namespace mappings configured for rootless containers
- For
--allow-hostsmode: iptables, iproute2, dnsutils (usually pre-installed)
On Debian/Ubuntu:
sudo apt-get update
sudo apt-get install -y podman iptables iproute2 dnsutilsVerify installation:
podman --version
iptables --version
ip netns list # Should work without errors-
Build the container:
./build-claude-container.sh
-
Run Claude in the container:
./claudebox
-
Chat with Claude - you're now running in an isolated environment!
# Run with default settings (internet access, no local network)
./claudebox
# Pass arguments to Claude
./claudebox "help me debug this code"./claudebox- Full internet access via user-mode networking (slirp4netns)
- No access to local network (192.168.x.x, 10.x.x.x, etc.)
- Good for general development work
- No setup required
./claudebox --no-internet- Complete network isolation
- Use when working with sensitive code
- Claude can only access mounted files
- No setup required
This mode uses a network namespace with iptables rules to provide TRUE network filtering at the IP level.
Setup (one-time):
# 1. Create allowed-hosts configuration
cp allowed-hosts.conf.example allowed-hosts.conf
# Edit to add your hosts and ports (e.g., github.com:443)
# 2. Option A: Manual setup
sudo ./setup-claude-netns.sh allowed-hosts.conf
# 2. Option B: Systemd service (recommended for persistence)
sudo ./install-systemd-service.sh
sudo systemctl start claude-netns.service
sudo systemctl enable claude-netns.service # Optional: start on bootUsage:
./claudebox --allow-hosts allowed-hosts.confHow it works:
- Creates a network namespace with restrictive iptables rules
- Resolves hostnames to IPs and whitelists only those IPs/ports
- Filters ALL protocols (HTTP, SSH, raw TCP, etc.)
- DNS (port 53) is allowed by default for name resolution
- Container runs in this isolated network namespace
Configuration format (allowed-hosts.conf):
# Format: hostname:port
github.com:443
github.com:22
api.anthropic.com:443
# Or just hostname (defaults to ports 80,443)
example.com
Check status:
./status-claude-netns.shStop/cleanup:
# Manual
sudo ./cleanup-claude-netns.sh
# Systemd
sudo systemctl stop claude-netns.service--no-internet, -n Disable all network access
--allow-hosts FILE, -a FILE Load allowed hosts from configuration file
--shell, -s Start bash shell instead of Claude (for testing)
--help, -h Show usage information
All other arguments are passed directly to Claude.
To test the container environment without running Claude:
./claudebox --shell
# Inside the container shell, you can:
# - Test network connectivity: curl https://github.com
# - Check mounts: ls -la ~/git/
# - Verify environment: echo $HOME, whoami
# - Test file permissions: touch test.txtThe container is based on Debian Trixie (slim) and includes:
- Claude Code CLI installed at
/usr/local/bin/claude - Cross-compiler toolchains for embedded development (ARM, RISC-V, etc.)
- Build tools and dependencies for barebox development
- QEMU for testing embedded systems
- Minimal base dependencies (curl, ca-certificates)
The container uses a sophisticated mount strategy:
-
Home directory (
~/.claudebox-home): Mounted read-write as~in the container- Sandboxed
$HOME— must exist first (mkdir -p ~/.claudebox-home) - Anything Claude writes to its home (shell history, caches, tool state) stays here, isolated from your real home
- Sandboxed
-
Claude config (
~/.claude/and~/.claude.json): The host's real files are mounted read-write on top of the sandboxed home- Shared with your host Claude install (credentials, settings, history persist between runs)
- Not sandboxed — this is a deliberate exception to the home isolation above
-
Current directory (
$(pwd)): Mounted read-write at the same absolute path- Preserves script compatibility (absolute paths work)
- Changes are immediately visible on host
- Skipped when already inside
~/.claudebox-home(reachable via the home mount)
-
Git directory (
~/git/): Mounted read-write at the same path (when it exists)- Changes made in the container affect the host filesystem
- Skipped when inside
~/.claudebox-home
-
Extra mounts (
~/.config/claudebox/extra-mounts.conf): Optional user-defined mounts- Plain-list config, one
host_path[:container_path][:mode]per line - Personal/machine-specific mounts live here instead of being hardcoded in the script
- See Additional Mounts
- Plain-list config, one
Uses --userns=keep-id to preserve your UID/GID:
- Files created in container owned by you on host
- Permissions match as if running directly on host
- No root privileges needed
Three modes available:
-
slirp4netns (default): User-mode networking that automatically isolates from local network
- Good for general use
- No setup required
-
--network=none: Complete isolation at the network stack level
- No network access at all
- No setup required
-
Network namespace + iptables (--allow-hosts): True network filtering
- Creates isolated network namespace with custom iptables rules
- Filters at IP level (works for all protocols: HTTP, SSH, TCP, UDP)
- Requires sudo for setup
- Hostnames resolved to IPs during setup
- Only whitelisted IPs/ports are accessible
✅ Accidental file modifications outside current directory ✅ Access to local network services (192.168.x.x, etc.) in all modes ✅ Permission prompts (runs with --dangerously-skip-permissions safely) ✅ System-wide changes (containerized environment) ✅ Network access to non-whitelisted hosts (in --allow-hosts mode with iptables)
Default mode (slirp4netns):
- Isolates from local network
- Full internet access
- Good for general development
No-internet mode:
- Complete isolation
- Maximum security
- No network dependencies
Filtered mode (--allow-hosts with network namespace):
- TRUE IP-level filtering via iptables
- Only whitelisted IPs/ports accessible
- DNS resolution works (port 53 always allowed)
- Filters ALL protocols (HTTP, HTTPS, SSH, etc.)
- Requires root for setup (network namespace creation)
❌ Mounted directories are readable and writable: $(pwd) and ~/git/ are mounted read-write, so Claude can both read and modify them on the host. Don't run from, or keep sensitive data under, directories you don't want Claude to touch.
❌ Not a VM: This is container isolation, not full virtualization. For maximum security, use a VM.
❌ Same user context: Container runs as your UID, not a separate security principal.
❌ DNS resolution in filtered mode: Hostnames are resolved to IPs during setup. If IPs change, you need to re-run setup.
❌ IPv6 filtering not implemented: Current iptables rules only filter IPv4. IPv6 is disabled in the namespace.
- Maximum isolation: Use
--no-internetmode - Selective access: Use
--allow-hostsmode with minimal host list - Default mode: OK for general work with non-sensitive code
- Review mounts: Check what directories are mounted before running
- Update regularly: Rebuild container periodically to get security updates
- Update network rules: Re-run setup if allowed hosts change
- Limit exposure: Only mount what you need
Problem: Error during ./build-claude-container.sh
Solutions:
- Check internet connection (needs to download Debian image and Claude installer)
- Verify Podman is installed:
podman --version - Check disk space:
df -h - Try rebuilding:
podman rmi claude-sandbox:latest && ./build-claude-container.sh
Problem: Can't read/write files in container
Solutions:
- Verify user namespace mappings:
podman unshare cat /proc/self/uid_map - Check file permissions on host
- Try running without SELinux labels: already done via
--security-opt label=disable
Problem: No internet access in default mode
Solutions:
- Check host network:
ping -c 1 8.8.8.8 - Verify Podman networking:
podman network ls - Try explicit network: Add
--network=slirp4netnsto podman run command in script
Problem: Can't access local services
Expected behavior: Local network is isolated by design in all modes.
Problem: "Network namespace 'claude-restricted' not found" when using --allow-hosts
Solutions:
- Setup namespace:
sudo ./setup-claude-netns.sh allowed-hosts.conf - Or use systemd:
sudo systemctl start claude-netns.service - Check status:
./status-claude-netns.sh - Verify namespace exists:
sudo ip netns list
Problem: "Permission denied" when setting up network namespace
Solution: The setup script requires root privileges. Use sudo ./setup-claude-netns.sh
Problem: Connections fail in --allow-hosts mode even though host is listed
Solutions:
- Check namespace status:
./status-claude-netns.sh - Verify iptables rules:
sudo ip netns exec claude-restricted iptables -L OUTPUT -n -v - Test from namespace:
sudo ip netns exec claude-restricted curl -v https://yourhost.com - Check if hostname resolved: Host may have changed IP, re-run setup
- Verify DNS works:
sudo ip netns exec claude-restricted nslookup github.com
Problem: Systemd service fails to start
Solutions:
- Check service status:
sudo systemctl status claude-netns.service - View logs:
sudo journalctl -u claude-netns.service -n 50 - Verify allowed-hosts.conf exists and path is correct in service file
- Check script permissions: Scripts should be executable (
chmod +x)
Problem: /usr/local/bin/claude: not found
Solutions:
- Rebuild container:
./build-claude-container.sh - Check if installer succeeded during build
- Manually verify:
podman run --rm claude-sandbox:latest ls -la /usr/local/bin/claude
Problem: Current directory not found in container
Solutions:
- Use absolute paths (script should handle this automatically)
- Verify mount: Check script output for "Working directory: ..."
- Ensure you're running from a real directory (not a symlink)
Problem: Error messages when running ./claudebox
Solutions:
- Check image exists:
podman images | grep claude-sandbox - If not, build it:
./build-claude-container.sh - Check Podman is running:
podman info - Review error messages and check logs
Edit claudebox to add custom network settings:
# Example: Add custom DNS server
NETWORK_ARGS="$NETWORK_ARGS --dns=1.1.1.1"
# Example: Expose a port (use cautiously)
NETWORK_ARGS="$NETWORK_ARGS -p 8080:8080"To mount extra host directories into the container, list them in
~/.config/claudebox/extra-mounts.conf (one per line) — no need to edit the
script:
mkdir -p ~/.config/claudebox
cp extra-mounts.conf.example ~/.config/claudebox/extra-mounts.conf
# then edit ~/.config/claudebox/extra-mounts.confFormat (see extra-mounts.conf.example for details):
~/datasets # same path, read-write
~/reference:ro # same path, read-only
/srv/data/project:/data:rw # host_path:container_path:mode
A leading ~ is expanded, comments (#) and blank lines are ignored, and any
listed path that does not exist is skipped. This file lives in your personal
config dir, so it stays out of the shared repository.
Add resource constraints to the podman run command:
# Limit memory to 4GB
--memory=4g
# Limit CPU to 2 cores
--cpus=2
# Limit disk I/O
--device-write-bps=/dev/sda:1mbCore files:
Containerfile- Container image definition (Debian Trixie + Claude)build-claude-container.sh- Builds the container imageclaudebox- Runs Claude in the container with all optionsallowed-hosts.conf.example- Example configuration for allowed hosts
Network namespace management:
setup-claude-netns.sh- Creates network namespace with iptables rules (requires sudo)cleanup-claude-netns.sh- Removes network namespace and cleans up (requires sudo)status-claude-netns.sh- Shows status of network namespace and firewall rulesclaude-netns.service- Systemd service unit file for persistent namespaceinstall-systemd-service.sh- Installs systemd service (requires sudo)
Documentation:
README.md- This file
Host System (Debian Trixie)
│
├── Podman (rootless)
│ │
│ └── Claude Container (claude-sandbox:latest)
│ ├── Debian Trixie base
│ ├── Claude CLI (/usr/local/bin/claude)
│ │
│ ├── Mounts (from host):
│ │ ├── ~/.claudebox-home -> ~ [rw] (sandboxed home)
│ │ ├── $(pwd) -> $(pwd) [rw]
│ │ ├── ~/git/ -> ~/git/ [rw]
│ │ ├── ~/.claude/ -> ~/.claude/ [rw] (host config)
│ │ └── ~/.claude.json -> ~/.claude.json [rw] (host config)
│ │
│ ├── Network: slirp4netns
│ │ └── User-mode networking (internet, no LAN access)
│ │
│ └── User context:
│ ├── UID: 1000 (mapped via --userns=keep-id)
│ ├── GID: 1000 (mapped)
│ └── HOME: ~ (matches host)
Host System (Debian Trixie)
│
├── Network Namespace: claude-restricted
│ ├── veth-claude-ns (10.200.0.2)
│ ├── Default route via 10.200.0.1
│ └── iptables rules:
│ ├── DROP all by default
│ ├── ALLOW DNS (port 53)
│ ├── ALLOW established connections
│ └── ALLOW whitelisted IPs/ports only
│
├── Host veth: veth-claude (10.200.0.1)
│ ├── NAT to internet
│ └── IP forwarding enabled
│
└── Podman (rootless)
│
└── Claude Container (claude-sandbox:latest)
├── Network: ns:/var/run/netns/claude-restricted
├── Mounts: (same as default mode)
└── User context: (same as default mode)
This is a personal security tool. Feel free to fork and adapt to your needs.
Suggested improvements:
- IPv6 filtering support in network namespace
- Support for other base images (Alpine, Ubuntu, etc.)
- GPU support for local models
- Dynamic IP tracking (monitor DNS changes and update rules)
- Web UI for managing allowed hosts
- Integration with other container runtimes (docker, nerdctl)
Use freely for personal or commercial purposes. No warranty provided.
- BREAKING: Changed --allow-hosts to use network namespace with iptables (TRUE filtering)
- Added network namespace management scripts (setup, cleanup, status)
- Added systemd service for persistent network namespace
- Updated allowed-hosts.conf format to support host:port specifications
- Added comprehensive troubleshooting for network namespace setup
- Enhanced README with architecture diagrams and security considerations
- Initial release
- Debian Trixie base
- Three network modes (default, no-internet, allowed-hosts with DNS only)
- Smart mount handling for $(pwd) and ~/git/
- User namespace mapping for correct permissions