A sandbox for running autonomous coding agents with network and filesystem isolation.
Coding agents are most useful when they can run autonomously, but this introduces risks: rogue activities, data exfiltration, or unintended external communications. Agentbox provides a containerized sandbox around any agent, with sensible defaults for quick setup and enough flexibility to customize for your needs.
Running agents autonomously means giving them significant control over your system. An agent with unrestricted network access could exfiltrate code to external servers. An agent with full filesystem access could read sensitive credentials or modify critical files.
Agentbox implements defense in depth to mitigate these risks:
| Layer | Protection | What It Prevents |
|---|---|---|
| Network firewall | iptables default-deny policy | Data exfiltration, unauthorized API calls, C2 communication |
| Privilege separation | Root configures firewall, then drops to unprivileged user | Agent cannot modify firewall rules or access privileged operations |
| Capability dropping | All Linux capabilities removed via setpriv |
Prevents kernel-level exploits and privilege escalation |
| No new privileges | Blocks setuid/setgid binaries | Prevents exploitation of SUID binaries for privilege escalation |
| Seccomp filtering | Restricts available syscalls | Limits attack surface for container escapes |
| No sudo | sudo not installed in container | Eliminates common privilege escalation vector |
This is a best-effort, defense-in-depth approach. Container escape vulnerabilities could bypass all protections. The goal is to significantly raise the bar for malicious behavior while maintaining agent usability.
Requirements:
- Docker (Docker Desktop on macOS, native Docker on Linux)
- Go 1.21+ (for building from source)
Install:
git clone https://github.com/gbrindisi/agentbox.git
cd agentbox
make build
sudo make installRun:
agentbox init --profile claude-code
export ANTHROPIC_API_KEY=sk-...
agentbox run -- "2+2=?"Note: On macOS, use this to inherit an existing Claude Code session:
ANTHROPIC_API_KEY=$(security find-generic-password -w -s "Claude Code") agentbox run -- "2+2=?"The sandbox is defined in an Agentfile, a YAML configuration file auto-discovered in the current directory.
# agentbox configuration
# See: https://github.com/gbrindisi/agentbox
# Agent configuration
agent:
# Build script runs as root during image build to install tools
build_script: |
curl -fsSL https://claude.ai/install.sh | bash
# Copy claude to system-wide location (install script puts it in ~/.local/bin)
cp /root/.local/bin/claude /usr/local/bin/claude
# Command and arguments to run
command: ["claude"]
args: ["--dangerously-skip-permissions", "--print"]
# Environment variables to pass from host to container
# Supports glob patterns like CLAUDE_CODE_*
env_passthrough:
- ANTHROPIC_API_KEY
# - CLAUDE_CODE_*
# Workspace configuration
workspace:
# Directory to mount as /workspace in the container
# Default: current directory
path: /path/to/code
# Additional mounts (optional)
mounts:
- source: $HOME/.claude
target: /home/agent/.claude
readonly: false
# Network configuration
network:
# Composable presets for services your agent needs
presets:
- anthropic
- github
- npm
- pypi
# Additional custom domains or ips (optional):
# allow:
# - custom-api.example.com
# Container security settings (optional)
# container:
# no_new_privileges: true # Prevent privilege escalation (default: true)
# readonly_root: false # Make root filesystem read-only
# seccomp_profile: default # Seccomp profile: default, unconfined, or path
Install tools and dependencies at image build time. Runs as root during docker build, result is cached based on script content hash.
agent:
build_script: |
apt-get update && apt-get install -y nodejs npm
npm install -g my-toolUse --rebuild to force a rebuild (Docker layer cache still applies). Runtime execution always runs as unprivileged agent user.
Pass environment variables from host to container using glob patterns:
agent:
env_passthrough:
- ANTHROPIC_API_KEY # Exact match
- CLAUDE_CODE_* # Glob pattern
- AWS_* # All AWS variablesMount host directories into the container:
mounts:
- source: ~/.ssh
target: /home/agent/.ssh
readonly: true
- source: ~/.gitconfig
target: /home/agent/.gitconfig
readonly: trueThe firewall uses iptables with a default-deny OUTPUT policy. At container startup, allowed domains are resolved to IP addresses using dig and stored in an ipset for efficient matching. DNS queries are restricted to the container's DNS server only. IPv6 is blocked by default to prevent bypass.
A companion libsandbox.so library is injected via LD_PRELOAD to intercept connect() calls. When a connection to a blocked IP is attempted, instead of a silent timeout, the agent receives an informative error message:
agentbox: Connection to 203.0.113.50 blocked by sandbox firewall. This is not bypassable.
This helps agents understand why a connection failed and adjust their behavior accordingly, rather than retrying indefinitely or misdiagnosing the issue.
Control network access with composable presets or custom domain lists:
# Combine presets for the services your agent needs
network:
presets:
- anthropic
- github
- npm
# Add custom domains alongside presets
network:
presets:
- anthropic
allow:
- custom-api.example.comAvailable presets:
| Category | Preset | Domains |
|---|---|---|
| AI Providers | anthropic |
api.anthropic.com, anthropic.com, claude.ai |
openai |
api.openai.com, openai.com, platform.openai.com, cdn.openai.com | |
google-ai |
generativelanguage.googleapis.com, ai.google.dev, aistudio.google.com | |
mistral |
api.mistral.ai, mistral.ai | |
| Source Control | github |
github.com, api.github.com, raw.githubusercontent.com, objects.githubusercontent.com, codeload.github.com |
gitlab |
gitlab.com, registry.gitlab.com | |
bitbucket |
bitbucket.org, api.bitbucket.org | |
| Package Registries | npm |
registry.npmjs.org, npmjs.org, npmjs.com |
pypi |
pypi.org, files.pythonhosted.org | |
cargo |
crates.io, static.crates.io, index.crates.io | |
rubygems |
rubygems.org | |
| ML/AI Models | huggingface |
huggingface.co, cdn-lfs.huggingface.co |
Fine-tune container security:
container:
no_new_privileges: true # Prevent privilege escalation (default: true)
readonly_root: false # Make root filesystem read-only
seccomp_profile: default # default, unconfined, or path to custom profileCreate a new Agentfile from a profile template.
agentbox init # Uses claude-code profile
agentbox init --profile claude-code # Explicit profileRun an agent in the sandbox.
agentbox run # Run with auto-discovered Agentfile
agentbox run -c /path/to/Agentfile # Specific config file
agentbox run -w /path/to/project # Override workspace directory
agentbox run --rebuild # Force image rebuild
agentbox run --tty # Force TTY allocation
agentbox run --no-tty # Disable TTY allocation
agentbox run --debug # Enable debug output
agentbox run -- "your prompt here" # Pass arguments to agentValidate an Agentfile without running.
agentbox validate
agentbox validate -c /path/to/AgentfileOpen a debug shell in the container. Uses the same configuration as run but starts bash instead of the agent. Useful for testing firewall rules and inspecting the environment.
agentbox shell # Open shell with auto-discovered Agentfile
agentbox shell --rebuild # Force image rebuild before opening shell
agentbox shell --debug # Enable debug output- DNS at startup: Domain names are resolved to IPs when the container starts. Long-running containers won't pick up DNS changes.
- Container escapes: A container escape vulnerability would bypass all protections. This is defense in depth, not a security guarantee.
- Mount access: The agent has full access to all mounted directories within the container.
Files created by the agent are owned by UID 1000 (the agent user). On Linux, this may differ from your host UID.
Solutions:
- Run
chownafter the container exits - Match UIDs in your build script:
agent:
build_script: |
usermod -u 501 agent # Replace 501 with your UID
groupmod -g 501 agent
chown -R agent:agent /home/agentNote: The container must start as root to configure the firewall before dropping privileges, so runtime UID cannot be overridden.