Run Claude Code (Anthropic's AI coding assistant) in a security sandbox to limit its access to your system. This repository provides three different sandboxing approaches for different platforms and security requirements.
Claude Code is an AI agent that can read files, write code, and execute commands. While it's designed to be helpful and safe, defense-in-depth security practices suggest limiting any automated tool's access to only what it needs. Sandboxing provides:
- Filesystem isolation - Claude can only access your current project, not your entire home directory
- Capability restriction - Dropped privileges prevent potential privilege escalation
- Blast radius reduction - If something goes wrong, damage is contained
- Audit clarity - Clear boundaries make it easier to understand what Claude can and cannot do
| Platform | Recommended Approach | Command |
|---|---|---|
| Linux | Bubblewrap | ./bubblewrap_claude.sh |
| Linux (alternative) | Firejail | ./firejail_claude.sh |
| macOS | Apple Container | ./container_claude.sh |
┌─────────────────────────────────────────────────────────────────────────────┐
│ ISOLATION STRENGTH │
│ │
│ Weaker │
│ │ │
│ │ ┌──────────────┐ │
│ │ │ Firejail │ Namespaces + Seccomp │
│ │ │ │ Easy to configure, good defaults │
│ │ └──────────────┘ │
│ │ │
│ │ ┌──────────────┐ │
│ │ │ Bubblewrap │ Namespaces (manual config) │
│ │ │ │ Maximum control, minimal overhead │
│ │ └──────────────┘ │
│ │ │
│ │ ┌───────────────┐ │
│ │ │Apple Container│ Hypervisor (VM) │
│ │ │ │ Strongest isolation, higher overhead │
│ ▼ └───────────────┘ │
│ Stronger │
└─────────────────────────────────────────────────────────────────────────────┘
| Feature | Bubblewrap | Firejail | Apple Container |
|---|---|---|---|
| Platform | Linux | Linux | macOS |
| Isolation Type | Linux namespaces | Namespaces + seccomp | Lightweight VM |
| Startup Overhead | ~5ms | ~10ms | ~500ms-2s |
| Memory Overhead | Minimal | Minimal | 256MB+ |
| Escape Difficulty | Medium | Medium | Hard |
| Configuration | Manual | Profile-based | Containerfile |
| Syscall Filtering | Manual | Built-in | N/A (different kernel) |
| Learning Curve | Steep | Moderate | Moderate |
Best for: Linux users who want minimal overhead and maximum control.
Bubblewrap (bwrap) uses Linux namespaces to create an isolated environment:
┌─────────────────────────────────────────────────────────────────┐
│ HOST SYSTEM │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ BWRAP SANDBOX │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Mount NS │ │ PID NS │ │ User NS │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ /usr (RO) │ │ Isolated │ │ Mapped UID │ │ │
│ │ │ /lib (RO) │ │ process │ │ │ │ │
│ │ │ $PWD (RW) │ │ tree │ │ │ │ │
│ │ │ ~/.claude(RW│ │ │ │ │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ │ ┌─────────────┐ │ │
│ │ │ Claude │ │ │
│ │ │ Code │ │ │
│ │ └─────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Namespace isolation:
- Mount namespace - Custom filesystem view with selective bind mounts
- PID namespace - Isolated process tree (can't see/signal host processes)
- Network - Shared (required for Claude API)
# Debian/Ubuntu
sudo apt install bubblewrap
# Fedora/RHEL
sudo dnf install bubblewrap
# Arch Linux
sudo pacman -S bubblewrap# Navigate to your project
cd /path/to/your/project
# Run Claude in sandbox
./bubblewrap_claude.sh
# Pass arguments to Claude
./bubblewrap_claude.sh -p "explain this codebase"| Path | Access | Purpose |
|---|---|---|
/usr, /lib, /bin |
Read-only | System binaries and libraries |
/etc/resolv.conf, /etc/hosts |
Read-only | Network configuration |
/etc/ssl |
Read-only | TLS certificates |
$HOME/.gitconfig |
Read-only | Git identity |
$HOME/.ssh/known_hosts |
Read-only | SSH host verification |
$SSH_AUTH_SOCK |
Read-write | SSH agent (git auth) |
$HOME/.nvm, $HOME/.local |
Read-only | Node.js runtime |
$HOME/.npm |
Read-write | NPM package cache |
$HOME/.claude |
Read-write | Claude configuration |
$PWD |
Read-write | Your project |
/tmp |
tmpfs | Ephemeral scratch space |
--unshare-pid # Isolate process namespace
--die-with-parent # Kill sandbox if parent dies
--ro-bind # Read-only mounts for system paths
--tmpfs /tmp # Fresh /tmp on each runBest for: Linux users who want easier configuration with good security defaults.
Firejail wraps bubblewrap-style namespaces with additional security layers:
┌─────────────────────────────────────────────────────────────────┐
│ HOST SYSTEM │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ FIREJAIL SANDBOX │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ SECCOMP FILTER │ │ │
│ │ │ Blocks dangerous syscalls: ptrace, mount, etc. │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ CAPABILITY RESTRICTIONS │ │ │
│ │ │ caps.drop=all, nonewprivs, noroot │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ NAMESPACE ISOLATION │ │ │
│ │ │ Mount, PID, IPC namespaces │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌─────────────┐ │ │
│ │ │ Claude │ │ │
│ │ │ Code │ │ │
│ │ └─────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Additional protections over raw bubblewrap:
- Seccomp BPF - Syscall filtering blocks dangerous operations
- Capability dropping - All Linux capabilities removed
- No-new-privileges - Prevents privilege escalation via setuid binaries
# Debian/Ubuntu
sudo apt install firejail
# Fedora/RHEL
sudo dnf install firejail
# Arch Linux
sudo pacman -S firejailOption A: Use the wrapper script
./firejail_claude.shOption B: Install the profile globally
# Copy profile to firejail config
cp claude.firejail.profile ~/.config/firejail/claude.profile
# Run with profile
firejail --profile=claude claude--caps.drop=all # Drop ALL Linux capabilities
--nonewprivs # No privilege escalation via execve
--noroot # Disable root inside sandbox
--seccomp # Enable syscall filtering
--private-tmp # Isolated /tmp
--private-dev # Minimal /dev
--nodvd --nosound # Disable hardware access
--no3d --notv --novideo # Disable GPU/media devicesEdit claude.firejail.profile to customize. Common modifications:
# Disable network (for offline analysis)
net none
# Add additional read-only paths
read-only ${HOME}/reference-docs
# Allow specific additional writable paths
whitelist ${HOME}/scratch-areaBest for: macOS users who want the strongest isolation available.
Apple Container uses macOS's Virtualization.framework to run a lightweight Linux VM:
┌─────────────────────────────────────────────────────────────────┐
│ macOS HOST │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ VIRTUALIZATION.FRAMEWORK │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ LIGHTWEIGHT VM │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────┐ ┌──────────────────────────────┐ │ │ │
│ │ │ │ Linux Kernel│ │ Userspace │ │ │ │
│ │ │ │ (custom) │ │ ┌────────────────────────┐ │ │ │ │
│ │ │ │ │ │ │ Debian minimal │ │ │ │ │
│ │ │ │ │ │ │ ┌──────────────────┐ │ │ │ │ │
│ │ │ │ │ │ │ │ Node.js │ │ │ │ │ │
│ │ │ │ │ │ │ │ ┌────────────┐ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ Claude │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ Code │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ └────────────┘ │ │ │ │ │ │
│ │ │ │ │ │ │ └──────────────────┘ │ │ │ │ │
│ │ │ │ │ │ └────────────────────────┘ │ │ │ │
│ │ │ └─────────────┘ └──────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────────────────────────────────────────┐│ │ │
│ │ │ │ VIRTIO DEVICES ││ │ │
│ │ │ │ • virtio-fs: /workspace ←→ $PWD ││ │ │
│ │ │ │ • virtio-net: NAT networking ││ │ │
│ │ │ │ • virtio-vsock: Host communication ││ │ │
│ │ │ └─────────────────────────────────────────────────┘│ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Why VMs provide stronger isolation:
- Different kernel - Kernel exploits in the VM don't affect the host
- Hardware boundary - Hypervisor enforces separation at CPU level
- No shared namespaces - Complete process/memory isolation
- Minimal attack surface - Only virtio devices exposed
- macOS 13.0 (Ventura) or later
- Apple Container CLI
# Clone the repository
git clone https://github.com/apple/swift-container
cd swift-container
# Build
swift build -c release
# Install
sudo cp .build/release/container /usr/local/bin/# First run builds the container image (takes a few minutes)
./container_claude.sh
# Subsequent runs start quickly
./container_claude.sh -p "review this code"Modify Containerfile to customize the image:
# Change Node.js version
ARG NODE_VERSION=22
# Add additional tools
RUN apt-get install -y ripgrep fd-find
# Change base image
FROM ubuntu:24.04Rebuild after changes:
container build -t claude-sandbox --no-cache .| Host Path | Container Path | Access |
|---|---|---|
$PWD |
/workspace |
Read-write |
~/.claude |
/home/claude/.claude |
Read-write |
~/.npm |
/home/claude/.npm |
Read-write |
~/.gitconfig |
/home/claude/.gitconfig |
Read-only |
~/.ssh/known_hosts |
/home/claude/.ssh/known_hosts |
Read-only |
$SSH_AUTH_SOCK dir |
/run/host-ssh |
Read-write |
All three approaches implement the same security model:
┌─────────────────────────────────────────────────────────────────┐
│ CLAUDE'S ACCESS MODEL │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CAN READ │ │
│ │ • Current project directory ($PWD) │ │
│ │ • Git configuration (identity) │ │
│ │ • SSH known_hosts (host verification) │ │
│ │ • System binaries and libraries │ │
│ │ • Node.js runtime │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CAN WRITE │ │
│ │ • Current project directory ($PWD) │ │
│ │ • Claude config (~/.claude) │ │
│ │ • NPM cache (~/.npm) │ │
│ │ • Temporary files (/tmp - ephemeral) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CANNOT ACCESS │ │
│ │ ✗ Other home directory contents │ │
│ │ ✗ SSH private keys │ │
│ │ ✗ Browser data, passwords, credentials │ │
│ │ ✗ Other users' files │ │
│ │ ✗ System configuration (write) │ │
│ │ ✗ Hardware devices (except network) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ NETWORK ACCESS │ │
│ │ ✓ Outbound HTTPS (Claude API) │ │
│ │ ✓ Outbound SSH (git operations) │ │
│ │ ✓ DNS resolution │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
| Asset | Protection |
|---|---|
| SSH private keys | Not mounted into sandbox |
| Browser profiles | Not accessible |
| Credentials/secrets | Not in scope |
| Other projects | Not mounted |
| System config | Read-only or not mounted |
| Email, documents | Not accessible |
"bwrap: No such file or directory"
# Install bubblewrap
sudo apt install bubblewrap # Debian/Ubuntu"Permission denied" on bind mounts
# Check if the directory exists
mkdir -p ~/.claude ~/.npm"Warning: cannot find profile"
# Use --noprofile or install the profile
cp claude.firejail.profile ~/.config/firejail/claude.profileWhitelist not working
# Firejail whitelist requires the path to exist
mkdir -p ~/.claude"container: command not found"
# Build and install from source
git clone https://github.com/apple/swift-container
cd swift-container && swift build -c release
sudo cp .build/release/container /usr/local/bin/"Image not found"
# Rebuild the image
container build -t claude-sandbox .SSH agent not working in container
# Verify SSH_AUTH_SOCK is set and socket exists
echo $SSH_AUTH_SOCK
ls -la $SSH_AUTH_SOCKFirejail - Block all network:
firejail --net=none claudeFirejail - Allow only specific hosts:
Create /etc/firejail/claude-net.filter:
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
-A OUTPUT -d api.anthropic.com -p tcp --dport 443 -j ACCEPT
-A OUTPUT -d github.com -p tcp --dport 22 -j ACCEPT
-A OUTPUT -d github.com -p tcp --dport 443 -j ACCEPT
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
COMMIT
Then use:
firejail --netfilter=/etc/firejail/claude-net.filter claudeIf Claude needs access to shared libraries or data outside $PWD:
Bubblewrap:
# Add to the script
--ro-bind /path/to/shared/data /path/to/shared/dataFirejail:
./firejail_claude.sh --whitelist=/path/to/shared/dataApple Container:
# Add to container_claude.sh mount_args
mounts+=(--mount "type=bind,src=/path/to/shared/data,dst=/data,readonly")Improvements welcome! Areas of interest:
- macOS sandbox-exec implementation (App Sandbox)
- Windows equivalent (Windows Sandbox / WSL)
- Integration with Claude Code's native sandboxing
- Automated security testing
Patrick McCanna
Code reviewed with assistance from Claude (Anthropic).
MIT License - See individual files for details.
- Bubblewrap - Unprivileged sandboxing tool
- Firejail - SUID sandbox program
- Apple Container - Swift-based container runtime
- Anthropic Claude Code - The AI assistant being sandboxed