Base Docker image for project-specific dev containers. Provides a secure sandbox for running Claude Code and Codex with full permissions behind an allowlist-based firewall.
- Docker (or Docker Desktop)
This image is designed to be used with dev-container-generic, which provides the devcontainer.json, docker-compose, setup scripts, and agent configuration. Build this image first, then clone the generic config into your project.
- OS: Ubuntu 24.04, non-root user
dev(uid 1000) - Languages: Python 3 + uv, Node.js 22 LTS
- Build tools: make, cmake, ninja-build, pkg-config, build-essential
- C libraries: libssl-dev, libffi-dev, libsqlite3-dev
- Dev tools: git, git-lfs, git-delta, gh, ripgrep, fd-find, entr, shellcheck, fzf, tmux, jq, curl, wget, tree, sqlite3, nano, vim, zip
- AI tools: Claude Code CLI, OpenAI Codex CLI
- Shell: Bash with fzf keybindings and persistent history
- Firewall: iptables allowlist with ipset, IPv6 locked down
./build.sh
# or with a custom tag:
./build.sh mytagThe image is tagged as devcontainer-base:<tag> (default latest). The image is directly runnable — it includes the firewall entrypoint, so no per-project Dockerfile is needed for most projects.
The firewall script (firewall/init-firewall.sh) is copied into the image at /usr/local/bin/init-firewall.sh. It is run at container start by the image's ENTRYPOINT (firewall-entrypoint.sh, as root via containerUser: root) and:
- Detects DNS servers from
/etc/resolv.conf - Flushes all iptables/ip6tables rules and ipsets
- Resolves whitelisted domains from
/etc/firewall-domains.d/*.conf - Fetches GitHub and Cloudflare IP ranges via API (if configured with
github-meta/cloudflare-ipsdirectives) - Adds FORWARD rules for Docker bridge interfaces (DinD egress filtered through the same domain allowlist)
- Allows whitelisted IPs on ports 80/443 and SSH (port 22), rejects all other outbound
- Sets default DROP policy on INPUT/OUTPUT/FORWARD
- Locks down IPv6 entirely (loopback only)
- Self-verifies by testing that httpbin.org is blocked and api.github.com is reachable
Base domains are in firewall/domains.d/00-base.conf. Project-specific domains can be volume-mounted as /etc/firewall-domains.d/10-project.conf.
The firewall script includes FORWARD rules for inner Docker containers:
- Inter-container traffic on Docker bridges (
docker0,br-+) is allowed - Inner container egress is filtered through the same
allowed-domainsipset - Host-to-container traffic via port mappings (DNAT) is allowed
- NAT via MASQUERADE for outbound inner container traffic
Important: init-firewall.sh flushes all iptables rules, including any Docker chains. It must run before dockerd starts, or dockerd must be stopped first. When using the DinD devcontainer feature, the entrypoint should stop dockerd, run the firewall, then restart dockerd.
# Comments and blank lines are fine
api.example.com
cdn.example.com
# Special directives (fetch dynamic IP ranges)
github-meta
cloudflare-ips
- User
devhas no general sudo access — only a narrow sudoers rule for theagent-persistencedevcontainer feature (chown/chmodon/mnt/agent-persistence) - The firewall script runs as root via the image's ENTRYPOINT (
containerUser: root) - SSH restricted to allowlisted IPs only (port 22)
- DNS restricted to nameservers listed in
/etc/resolv.conf - All outbound traffic dropped unless explicitly allowed on ports 80, 443, or 22
- IPv6 completely locked down
- Inner Docker containers (DinD) subject to the same domain allowlist via FORWARD chain