Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.clawker.dev/llms.txt

Use this file to discover all available pages before exploring further.

Clawker runs every agent in an isolated Docker container with a default-on network firewall. This guide explains the container security controls and how to customize them.

Network Firewall

The firewall is deny-by-default. When enabled, it blocks all outbound traffic — DNS queries for unlisted domains return NXDOMAIN, and TLS connections to unlisted destinations are reset. Only domains you explicitly allowlist are reachable. This is intentional. The hardcoded allowlist is kept to the bare minimum required for Claude Code itself to function. Everything else — including GitHub, npm, PyPI, or any other service your project needs — must be explicitly configured by you. See the Firewall guide for the full architecture, configuration, CLI commands, and troubleshooting.

Hardcoded Allowed Domains

These are the only domains allowed by default — they are required for Claude Code to function (API access, OAuth, telemetry):
DomainPurpose
api.anthropic.comClaude API
platform.claude.comOAuth token exchange
.claude.aiOAuth authorization, downloads
mcp-proxy.anthropic.comMCP tool server proxy
sentry.ioError tracking
statsig.anthropic.comFeature flags
statsig.comFeature flags
.datadoghq.comTelemetry (all Datadog subdomains/regions)
.datadoghq.euTelemetry (Datadog EU regions)
GitHub, npm, PyPI, and all other services are NOT hardcoded. The generated clawker.yaml template includes github.com and api.github.com as a convenience starting point, but these are project-level config entries — not system defaults. If you remove them from your config or start from scratch, GitHub will be blocked.You must explicitly add every domain your agent needs to reach via add_domains in your .clawker.yaml or clawker firewall add <domain> at runtime. See Firewall Configuration for details.

Disabling the Firewall

The global firewall toggle lives in settings.yaml (not the project config):
# ~/.config/clawker/settings.yaml
firewall:
  enable: false
Disabling the firewall removes all outbound network restrictions. The agent can reach any endpoint on the internet. Only do this in trusted environments where you accept the risk of unrestricted egress.

Agent Awareness Prompt

Every Clawker container image includes an agent prompt file at /etc/claude-code/CLAUDE.md. This file is automatically loaded by Claude Code and gives the agent awareness of its containerized environment:
  • What it can and cannot do (filesystem, network, Docker)
  • How to diagnose firewall blocks (NXDOMAIN, connection resets, certificate errors)
  • What clawker firewall commands the user can run on the host to unblock domains
  • Environment variables available for troubleshooting (CLAWKER_PROJECT, CLAWKER_AGENT, CLAWKER_FIREWALL_ENABLED, etc.)
This means when the agent hits a blocked domain, it can explain the problem and suggest the right clawker firewall add command — without the user needing to know the details.

Container PID 1 and Privilege Drop

The container’s PID 1 is clawkerd, the per-container daemon that supervises Claude Code. clawkerd runs as root inside the container, but it never executes user-controlled code in its own process. The user CMD (Claude Code by default) is forked with kernel-side privilege drop via SysProcAttr.Credential — between fork() and execve() the kernel performs setgroups → setgid → setuid atomically, so there is no user-mode code path between root and the unprivileged claude user (UID 1001). The clawker control plane talks to clawkerd over a CN-pinned mTLS Session — only a peer holding a cert with CN clawker-controlplane chained to the clawker CA can dispatch commands. See Container Internals → clawkerd for the full PID-1 contract.

Docker Socket

By default, the Docker socket is not mounted into containers:
security:
  docker_socket: false
Enabling docker_socket: true mounts /var/run/docker.sock read-write into the container. This gives the agent full control over your Docker daemon — it can create, modify, and delete any container, image, or volume on your host. Only enable this if your workflow genuinely requires Docker-in-Docker.

Linux Capabilities

Agent containers require no special privileges for firewall enforcement. eBPF cgroup programs are attached from outside the container by the clawker control plane — raw socket creation is blocked by the eBPF sock_create program rather than by container capabilities. The supporting infrastructure containers use minimal capabilities. The clawker control plane (clawker-controlplane) runs with CAP_BPF + CAP_SYS_ADMIN to load, attach, and pin eBPF programs and maps. The custom CoreDNS build runs with CAP_BPF + CAP_SYS_ADMIN to open the pinned dns_cache BPF map and update it on DNS responses. Both mount only /sys/fs/bpf — no broader host access. See DNS Cache Population below for why CoreDNS needs these capabilities. Agent containers default to no extra capabilities (cap_add: []). The cap_add field is available if your workflow needs additional Linux capabilities:
security:
  cap_add: []  # default — agent containers are fully unprivileged
  # cap_add:
  #   - SYS_PTRACE   # only add capabilities your workflow actually needs

IPv6 Support

Clawker’s firewall is IPv4-first. There are two cases to understand:
  • Dual-stack sockets (IPv4-mapped IPv6, ::ffff:x.x.x.x). Most modern programs — ssh, curl, Node.js, Python, Go — open dual-stack IPv6 sockets by default and connect to IPv4 targets via IPv4-mapped addresses. The eBPF cgroup/connect6 program detects these and applies the same full routing logic as IPv4: DNS redirect to CoreDNS, gateway lockdown, per-domain route lookup via the DNS cache, and Envoy fallback for unknown destinations. From the user’s perspective, dual-stack traffic is enforced identically to pure IPv4.
  • Native IPv6. Connections to real IPv6 addresses (e.g., 2606:4700::1 or anything forced via -6) are denied. The egress stack (Envoy listeners, CoreDNS forward zones, eBPF DNS cache) is IPv4-only, and there is no safe way to transparently route native IPv6 traffic today. If your workflow requires IPv6 connectivity, prefer hostnames so the container resolves an A record, or open an issue describing the use case.

DNS Cache Population

Per-domain TCP routing in eBPF uses a dns_cache BPF map keyed by destination IP. When a container connects to 140.82.121.4, the cgroup hook looks up the IP in the map, recovers the domain hash, and checks the global route_map for a matching {domain_hash, dst_port} rule. This only works if the cache is kept consistent with live DNS responses. Clawker ships a custom CoreDNS build with an in-tree dnsbpf plugin (internal/dnsbpf/). The plugin intercepts every DNS response for an allowlisted zone, extracts the A records, and writes each (ip → domain_hash, ttl) entry to the pinned dns_cache map in real time. This replaces an earlier startup-time seeding approach that was vulnerable to DNS round-robin rotation: seeded IPs would go stale as CDNs cycled backends, silently blackholing traffic until the firewall was reloaded. The plugin-based approach tracks whatever the upstream resolver returns on every query, so any IP a container actually uses has been observed and hashed at the moment of connection. The plugin only writes to the BPF map on successful A-record answers. NXDOMAIN and other non-success responses are passed through untouched, so blocked domains never leak into the cache. The custom CoreDNS image is built from a SHA-pinned alpine:3.21 base with the CoreDNS binary copied in — no shell, no package manager, no additional utilities. Its elevated capabilities (CAP_BPF + CAP_SYS_ADMIN) are scoped to the minimum needed for bpf(BPF_OBJ_GET) and bpf(BPF_MAP_UPDATE_ELEM) on the pinned map, and the only host access is a bind mount of /sys/fs/bpf.

TCP/SSH Port-Level Routing

Raw TCP and SSH protocols have no domain-level metadata (no SNI equivalent), so Clawker cannot inspect the intended destination. Instead, eBPF routes all outbound traffic on the configured port to Envoy, which forwards it to the whitelisted domain. For example, a proto: ssh rule for github.com on port 22 means every port 22 connection from the container goes to GitHub, regardless of the destination the agent specified. An attacker cannot reach arbitrary SSH servers — the traffic is locked to the whitelisted target. This means only one domain can be whitelisted per TCP/SSH port — a tracked limitation, deferred for after the control plane work. See the Firewall guide for details.

Host Proxy

The host proxy is a lightweight daemon that runs on your host machine and forwards credentials into containers. It’s enabled by default:
security:
  enable_host_proxy: true
The host proxy is required for Git HTTPS credential forwarding and browser-based OAuth flows (e.g., gh auth login). See the Credential Forwarding guide for details.

Troubleshooting

For firewall troubleshooting, see the Firewall guide.