arizuko

arizukocomponents › crackbox

crackbox

What it is

crackbox is the sandbox an agent runs inside — the containment boundary for one agent run. A compromised agent can reach exactly the hosts you allowed and nothing else, because the only way out of its box is through crackbox, and crackbox drops everything off the allowlist.

The shipped mechanism of that containment is egred: a forward HTTP/HTTPS proxy that is the agent’s sole route to the network. The agent literally runs in a jail — its own container, a Docker network with no default route, egress only via crackbox. You hand crackbox a short list of reachable hosts; everything else is silently blocked. The same jail works for a CI script, a vendor binary, or any other command you don’t fully trust.

Why it exists

An agent runs generated code with the network open. You want it to reach github.com and api.anthropic.com — not your internal services, not a crypto miner shipped in a compromised npm package, not wherever a prompt injection points it. Browser permission prompts don’t fit a headless workload. Per-process iptables is fiddly. A network-level egress firewall doesn’t know which process is calling. crackbox is the box the agent runs in, so it sits in the one place that does: the only path the workload has out.

In arizuko, crackbox is the containment boundary every agent container runs within — one of the capability hooks a deployment turns on. Today that box is a Docker container on a per-group internal: true network whose only egress is the crackbox proxy. Stronger isolation — the agent running inside a crackbox-spawned KVM micro-VM (pkg/host/) rather than a shared-kernel container — is the next phase, not yet wired into the agent spawn. See specs/11/12.

How it fits

agent (in its box) → crackbox proxy → allowed hosts only. The container sets HTTPS_PROXY=http://crackbox:3128 (or is redirected transparently with iptables), and the box has no other route out. crackbox reads the destination, checks it against the agent’s allowlist, and splices the connection on a match — or drops it.

The registry is source-IP → (id, allowlist). One pure matchHost function decides every connection: host name only — exact, subdomain-aware (--allow github.com covers api.github.com), case-insensitive, trailing dot stripped. No IP rules, no path rules, no per-method rules.

Two ways traffic arrives

Forward. The workload sets HTTPS_PROXY=http://crackbox:3128. crackbox accepts HTTP forward requests and CONNECT tunnels for HTTPS, reads the destination from the request line, splices on success.

Transparent. The client side runs iptables ... REDIRECT --to-ports 3127 on outbound :80/:443. crackbox reads the pre-NAT destination via SO_ORIGINAL_DST, peeks the SNI (443) or Host: (80), splices. Linux only.

Both share the same registry and the same allowlist check. Bind both, neither, or one — whatever fits the topology.

Threat model

Non-cooperating clients fail closed. crackbox run puts the workload on a Docker network with no default route; a client that ignores HTTPS_PROXY simply can't reach anything off-network. Setting HTTPS_PROXY="" doesn't help — there's no other path out. The trust assumption is that the source IP within the daemon's network is honest, so the topology must be such that only your workload can reach the proxy on its registered IP. crackbox run arranges this with a private Docker network; in service deployments you arrange it yourself.

Most tooling honours proxy environment variables natively: curl, wget, pip, npm, git, go, apt. Node's fetch needs a one-line shim to set the global agent. That's the only common gotcha.

What it doesn't do

Each one is a separate concern, composed with a separate tool.

Standalone usage

crackbox is a sibling component — usable outside arizuko. For one-off runs, crackbox run --allow X -- <cmd> is the whole interface: it spins up a private Docker network, runs the proxy on it, runs your workload, and tears everything down on exit.

# build the image once (from the arizuko repo root, not crackbox/)
git clone https://github.com/kronael/arizuko && cd arizuko
docker build -t crackbox:latest -f crackbox/Dockerfile .

# allowed -> 200
crackbox run --allow github.com -- curl -s -o /dev/null -w '%{http_code}\n' https://github.com

# unlisted -> blocked
crackbox run --allow github.com -- curl -s -o /dev/null -w '%{http_code}\n' https://example.com

# python image, install one package, nothing else reachable
crackbox run --image python:3 --allow pypi.org,files.pythonhosted.org -- pip install requests

--allow is required and must be non-empty; pass an unreachable placeholder like --allow none.invalid to block everything. Comma-separate for multiple hosts.

Commands

crackbox proxy serve [--config <path>] [--listen :3128] [--admin :3129] [--transparent :3127]
crackbox run --allow <list> [--id <name>] [--image <img>] -- <cmd>...
crackbox state [--admin <url>]

Configuration

Optional TOML, looked up in order: --config path, $XDG_CONFIG_HOME/crackbox/crackbox.toml, ~/.crackboxrc, /etc/crackbox.toml. Missing file is fine. Precedence: flags > env > config > defaults.

[proxy]
listen = ":3128"
admin_listen = ":3129"
transparent_listen = ":3127"   # set to "" to disable

[admin]
secret = ""                    # bearer token; empty disables auth

[state]
path = ""                      # registry persistence; empty = RAM-only
vardefaultused by
CRACKBOX_PROXY_ADDR:3128forward proxy listener
CRACKBOX_ADMIN_ADDR:3129admin API listener
CRACKBOX_TRANSPARENT_ADDR:3127transparent listener; empty disables
CRACKBOX_ADMIN_SECRET(unset)bearer token for mutating endpoints
CRACKBOX_STATE_PATH(unset)JSON file for registry persistence
CRACKBOX_IMAGEcrackbox:latestcrackbox run proxy image
CRACKBOX_SUBNET10.88.0.0/16crackbox run Docker subnet
CRACKBOX_ADMINhttp://localhost:3129crackbox state target

CRACKBOX_STATE_PATH empty keeps the registry RAM-only; restarts drop state. When set, every register / unregister rewrites the file atomically and startup reloads it. A corrupt or missing file resets to empty with a warning — a stale snapshot can never block startup.

Admin API

The daemon exposes an HTTP control plane on the admin listener (default :3129):

Bearer-token auth guards the mutating endpoints when CRACKBOX_ADMIN_SECRET is set; /v1/state and /health stay open. An empty secret leaves mutations unauthenticated and logs a warning.

Library

Go consumers can import the daemon's packages directly:

import "github.com/kronael/arizuko/crackbox/pkg/admin"   // Registry, API
import "github.com/kronael/arizuko/crackbox/pkg/proxy"   // forward + transparent
import "github.com/kronael/arizuko/crackbox/pkg/client"  // admin HTTP client

The CLI is the primary interface. The library is exported but not promised stable across point releases; pin the import path to a tag.