Clawker ships a deny-by-default network firewall that restricts all container egress to explicitly allowed domains. The firewall runs as a shared stack — an Envoy egress proxy, a custom CoreDNS resolver with aDocumentation Index
Fetch the complete documentation index at: https://docs.clawker.dev/llms.txt
Use this file to discover all available pages before exploring further.
dnsbpf plugin, and a set of eBPF cgroup programs — managed automatically by clawker on an isolated Docker bridge network. One firewall stack serves all clawker-managed containers on the host (1:N). It provides DNS-level blocking, per-domain TCP routing, and TLS-level inspection without granting your agent containers any special privileges.
Architecture
The firewall is composed of two managed Docker containers plus a set of eBPF programs attached from outside each agent container:- Envoy (
envoyproxy/envoy, TLS listener10000, sequential TCP listeners from10001) — TLS termination with per-domain certificates for all allowed domains. Envoy terminates TLS, inspects HTTP traffic (paths, methods, response codes visible), then re-encrypts upstream. Default deny (connection reset) for unrecognized SNI. - CoreDNS (
clawker-coredns:latest, port53) — a custom CoreDNS build fromcmd/coredns-clawker. Provides deny-by-default DNS filtering (NXDOMAIN for anything not in the allowlist, Cloudflare malware-blocking upstream1.1.1.2/1.0.0.2) and embeds the first-partydnsbpfplugin that writes every resolved IP to the BPFdns_cachemap in real time — this is what lets the BPFconnect4/connect6programs do per-domain TCP routing. Runs withCAP_BPF + CAP_SYS_ADMINand a/sys/fs/bpfbind mount, the minimum required to update the pinned cache map. - eBPF cgroup programs (
connect4,sendmsg4,recvmsg4,connect6,sendmsg6,recvmsg6,sock_create) — loaded and attached from outside the agent container by the clawker control plane. Owns the pinned BPF maps under/sys/fs/bpf/clawker/(container_map,route_map,dns_cache,bypass_map,metrics_map). The agent container itself runs fully unprivileged — no Linux capabilities, no firewall scripts, no init-time network gymnastics.
clawker-net bridge network with deterministic static IPs computed from the network gateway by replacing its last octet: Envoy at <network>.200, CoreDNS at <network>.201 (so e.g. 192.168.215.200 / .201 on a default Docker bridge with gateway 192.168.215.1). Agent containers join the same network with --dns pointed at CoreDNS, and the eBPF connect4/connect6 programs redirect all outbound TCP to Envoy. CoreDNS forwards Docker internal names (host.docker.internal, monitoring stack containers) back to Docker’s embedded DNS at 127.0.0.11 so internal networking keeps working.
How It Works
- When you run
clawker runorclawker container createwith the firewall enabled, clawker brings the firewall stack up automatically (this happens transparently — see Control Plane if you want the lifecycle details). - The
clawker-netbridge network is created, the eBPF programs are loaded into the kernel and their maps pinned under/sys/fs/bpf/clawker/, then CoreDNS and Envoy are launched. CoreDNS opens the pinneddns_cachemap on startup, so the eBPF state must exist before CoreDNS boots — this ordering is preserved on every reload. - Project rules from
.clawker.yamlare merged with system-required rules (additive merge, dedup bydestination:protocol:port). - Envoy, CoreDNS, and the global
route_mapare (re)generated from the merged ruleset. - Agent containers join the firewall network; the eBPF cgroup programs attach to each container’s cgroup, and an entry is written to
container_map(cgroup_id → container config). Presence incontainer_mapis what gates enforcement — theroute_mapitself is global. - As the agent resolves DNS, the
dnsbpfplugin writesIP → {domain_hash, TTL}entries into thedns_cachemap. On each outbound TCP connection, theconnect4/connect6programs look up the destination IP indns_cache, then look up{domain_hash, dst_port}inroute_mapto decide which Envoy listener to redirect to. - Envoy and CoreDNS are health-probed continuously. When the last clawker-managed agent container exits, the firewall stack and eBPF state are drained and flushed cleanly.
Because
route_map is global, clawker firewall add, clawker firewall remove, and clawker firewall reload immediately propagate rule changes to all running agent containers via an atomic route-map sync — no agent restart, no firewall stack restart.IPv4, IPv6, and dual-stack
The BPFconnect6 program applies the full connect4 routing logic to IPv4-mapped IPv6 addresses (::ffff:x.x.x.x). Dual-stack clients — SSH, curl, Node.js, Go’s default resolver — use IPv4-mapped sockets, so they get the same per-domain routing through Envoy as native IPv4.
Default Allowed Domains
The hardcoded allowlist is deliberately bare-bones — only domains required for Claude Code itself are included (API access, OAuth, telemetry):| Domain | Purpose |
|---|---|
api.anthropic.com | Claude API |
platform.claude.com | OAuth token exchange |
.claude.ai | OAuth authorization, downloads |
mcp-proxy.anthropic.com | MCP tool server proxy |
sentry.io | Error tracking |
statsig.anthropic.com | Feature flags |
statsig.com | Feature flags |
.datadoghq.com | Telemetry (all Datadog subdomains/regions) |
.datadoghq.eu | Telemetry (Datadog EU regions) |
A leading dot (e.g.,
.datadoghq.com) is the wildcard convention — it matches the apex domain and all subdomains. Use this for services with region-specific subdomains. Without the leading dot, only the exact domain is allowed.Common Setups
Here are examples for common services your agent will likely need. None of these are included by default — add what you need to your.clawker.yaml.
Git SSH (GitHub, GitLab, Bitbucket)
SSH git operations (git clone [email protected]:...) require an explicit SSH rule. The add_domains shorthand only covers HTTPS (port 443) — SSH needs a dedicated rules entry on port 22:
git push and git clone over SSH will fail with a connection reset. The agent’s SSH keys are forwarded from your host (see Credential Forwarding), but the firewall still needs to allow the traffic through.
For GitLab or Bitbucket, add equivalent rules:
Package Registries
Full Working Example
A typical project that uses GitHub and npm:Configuration
The firewall is configured in two places:- Global toggle:
firewall.enableinsettings.yaml(enable/disable the entire firewall) - Per-project rules:
security.firewallsection of.clawker.yaml(which domains to allow)
.clawker.yaml:
add_domains
A convenience shorthand for allowlisting entire domains. Each entry is automatically converted to a TLS allow rule on port 443 with no path restrictions (allow-all routing through TLS inspection).
.datadoghq.com) enables wildcard matching — the apex domain and all subdomains are allowed. Without the leading dot, only the exact domain is matched. Use wildcards for services with region-specific subdomains (e.g., Datadog’s us5.datadoghq.com, eu1.datadoghq.com).
rules
Full rule specification for fine-grained control:
| Field | Type | Description |
|---|---|---|
dst | string | Domain name or IP address. Prefix with . for wildcard (e.g., .datadoghq.com matches all subdomains) |
proto | string | tls, http, tcp, or ssh |
port | int | Destination port |
action | string | allow or deny (default: allow) |
path_rules | list | Optional path prefix rules (each entry has path and action). Supported on tls and http protocols |
path_default | string | Default action for paths not matching any path_rules entry (default: deny) |
Protocol behavior
tls(default) --- HTTPS traffic. Envoy terminates TLS with a per-domain certificate, inspects HTTP traffic (paths visible in access logs), then re-encrypts upstream. Withpath_rules, per-path routing is applied; withoutpath_rules, all traffic to the domain is allowed.http--- Plain HTTP traffic. Envoy inspects the Host header for domain matching and applies path rules directly. No TLS involved.tcp--- Raw TCP forwarding to a specific port. No domain or path inspection. See the port-level routing note below.ssh--- SSH traffic forwarding. Functionally identical totcpbut semantically distinct. See the port-level routing note below.
Path rules
Path rules give you fine-grained control over which URL paths are allowed for a domain. Envoy uses prefix matching --- a rule for/api/v1 matches /api/v1, /api/v1/users, /api/v1/models/list, etc.
When path_rules is specified, path_default controls what happens to paths that don’t match any rule. It defaults to deny, meaning only explicitly allowed paths get through.
Pattern 1: Allow specific paths, deny everything else (default deny)
/v1/chat/completions and /v1/models are forwarded. Everything else (e.g., /admin, /internal) gets a 403 Blocked by clawker firewall response.
Pattern 2: Deny specific paths, allow everything else
/evil, which get a 403.
Mixed protocol examples:
Global vs Project Rules
- System-required rules (from
cfg.RequiredFirewallRules()) are always present --- Claude API, OAuth, error tracking, and feature flags - Project rules come from
add_domainsandrulesin your.clawker.yaml - Rules merge additively --- project rules add to (never replace) system rules
- Dedup key:
destination:protocol:port--- duplicate rules are silently ignored - This means you cannot accidentally override or remove a system-required rule
CLI Commands
All firewall operations are available underclawker firewall:
| Command | Purpose |
|---|---|
clawker firewall status | Show firewall health, running containers, rule count |
clawker firewall list | List all active egress rules |
clawker firewall add DOMAIN | Add a domain allow rule |
clawker firewall remove DOMAIN | Remove a domain rule |
clawker firewall reload | Force regenerate Envoy/CoreDNS configs |
clawker firewall up | Start the firewall daemon (usually automatic) |
clawker firewall down | Stop the firewall daemon |
clawker firewall enable --agent AGENT | Attach a container to the firewall network |
clawker firewall disable --agent AGENT | Remove a container from the firewall network |
clawker firewall bypass | Temporary unrestricted egress (eBPF bypass flag + timed re-enable) |
clawker firewall rotate-ca | Regenerate CA and all domain certificates |
Certificate Management
The firewall uses a self-signed certificate authority for TLS inspection. All HTTPS traffic is terminated at Envoy with per-domain certificates, inspected at the HTTP level (making request paths, methods, and response codes visible), then re-encrypted upstream.- An ECDSA P256 CA is auto-generated during
clawker buildand baked into agent container images viaupdate-ca-certificates - Per-domain certificates are generated for every TLS rule --- Envoy terminates TLS for all allowed domains
- Domains with
path_rulesget per-path routing; domains without get allow-all routing --- both go through TLS inspection - The CA keypair is persisted in the firewall data directory and shared between the bundler and firewall manager
- Use
clawker firewall rotate-cato regenerate the CA and all domain certs
After rotating the CA, you must restart any running agent containers for them to pick up the new certificate.
Tools with custom CA bundles
Some tools (notably Python packages installed viauv/uvx, like semgrep) ship their own CA certificate bundles and ignore the system trust store. Clawker sets SSL_CERT_FILE and CURL_CA_BUNDLE in the container environment to point these tools at the system store, which includes the firewall CA.
If a tool still reports certificate errors, it may need its own environment variable. Add it to your project config:
SSL_CERT_FILE and CURL_CA_BUNDLE are set automatically in the container. They point at the system CA bundle which includes the firewall CA. Use ${SSL_CERT_FILE} when configuring additional tools rather than hardcoding paths.Bypass (Escape Hatch)
For situations where you need temporary unrestricted network access:Disabling the Firewall
To disable the firewall entirely, setfirewall.enable to false in your settings.yaml (not the project config):
Troubleshooting
Health check failures
Runclawker firewall status to see the health of the stack (Envoy, CoreDNS, and the eBPF subsystem). Envoy is probed over clawker-net on its internal admin port (HTTP GET /), and CoreDNS is probed on its host-published health port 18902 (HTTP GET /health). The eBPF subsystem is considered healthy when the pinned programs and maps under /sys/fs/bpf/clawker/ are present — they survive across firewall stack restarts by design.
If a container is unhealthy, try:
Blocked domains
Check which rules are active withclawker firewall list. If a domain you need is missing, add it:
.clawker.yaml:
DNS resolution failures
CoreDNS returns NXDOMAIN for any domain not in the allowlist. If an agent reports DNS failures for a domain it should be able to reach, verify the domain is in your rules withclawker firewall list.
Docker internal names (host.docker.internal, monitoring stack containers like otel-collector) are forwarded by CoreDNS back to Docker’s embedded DNS and should resolve automatically. If they don’t, check that CoreDNS is running on clawker-net with clawker firewall status.
Certificate trust errors
If an agent reports TLS certificate errors (CERTIFICATE_VERIFY_FAILED, unable to get local issuer certificate):
-
Check if the tool uses the system trust store. Most tools (Go, curl, wget) do. Python tools installed via
uv/uvxmay not --- see Tools with custom CA bundles above. - Rotate the CA if the certificate is expired or corrupted:
clawker build) and restart containers.
Stale dns_cache or route_map after upgrade
On startup the eBPF loader detects pinned maps whose key/value sizes have changed (for example, after a clawker upgrade that ships a new route_key layout) and removes them before reloading. If you still suspect a stale pin, bring the firewall fully down and back up: