Add agent-sandbox launcher, images and CI #1

Merged
benvin merged 9 commits from benvin/scaffold into main 2026-10-24 16:19:58 +11:00
Member

Claude Code needs a sandbox that only sees the launch directory and can only reach allowlisted hosts, enforced by network topology rather than proxy env vars.

  • add agent-sandbox launcher: per-session internal network, pod and router; mounts only $PWD
  • refuse launch from $HOME, its parents or the allowlist dir; abort on any invalid allowlist line
  • add router image (dnsmasq + nginx SNI/Host/TCP routing) and read-only loopback forwarder sidecar
  • add Fedora sandbox image with Claude Code and nested rootless podman
  • add default allowlist, unit tests, nfpm RPM, Makefile
  • add pre-commit, lint, test, build and tag release pipelines
Claude Code needs a sandbox that only sees the launch directory and can only reach allowlisted hosts, enforced by network topology rather than proxy env vars. - add `agent-sandbox` launcher: per-session internal network, pod and router; mounts only `$PWD` - refuse launch from `$HOME`, its parents or the allowlist dir; abort on any invalid allowlist line - add router image (dnsmasq + nginx SNI/Host/TCP routing) and read-only loopback forwarder sidecar - add Fedora sandbox image with Claude Code and nested rootless podman - add default allowlist, unit tests, nfpm RPM, Makefile - add pre-commit, lint, test, build and tag release pipelines
unkin-agent added 6 commits 2026-10-23 22:29:32 +11:00
Add README
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
4ee52cb615
Author
Member
  • agent-sandbox:89 — a wildcard plus the same exact name (.example.org + example.org or example.org:22) writes example.org twice into both nginx hostnames maps; nginx fails with conflicting parameter "example.org" and the session never starts → leave exact hosts that a .domain entry already covers out of $map (dnsmasq still needs their host-record), and add that case to tests/test.sh
  • nit: agent-sandbox:175 — the router is the root-run, internet-facing nginx and does not need label=disable → mount the private tmp dir with :ro,Z and keep SELinux confinement
- agent-sandbox:89 — a wildcard plus the same exact name (`.example.org` + `example.org` or `example.org:22`) writes `example.org` twice into both nginx `hostnames` maps; nginx fails with `conflicting parameter "example.org"` and the session never starts → leave exact hosts that a `.domain` entry already covers out of `$map` (dnsmasq still needs their host-record), and add that case to tests/test.sh - nit: agent-sandbox:175 — the router is the root-run, internet-facing nginx and does not need `label=disable` → mount the private tmp dir with `:ro,Z` and keep SELinux confinement
unkin-agent added 1 commit 2026-10-23 22:37:54 +11:00
Skip exact hosts covered by a .domain in nginx maps; confine router with SELinux
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
edbe3faeb0
Author
Member
  • agent-sandbox:173 — an invalid allowlist line does not abort: die runs inside $(router_config …) / $(allowlist_entries …) where errexit is off, so the error prints and the session starts with only the entries sorted before the bad line (verified: api.anthropic.com, bad host, zzz.example.com → rc=0, only api.anthropic.com configured) → validate first in the parent shell (e.g. allowlist_entries "$ALLOWLIST" >/dev/null || exit 1 before line 173, or shopt -s inherit_errexit), and add a test that router_config fails on a bad mid-file entry
  • agent-sandbox:203 — launching from $HOME (or any parent of $CONFIG_DIR/$ALLOWLIST) mounts the allowlist read-write, so claude can widen the next session's egress → refuse to start when readlink -f "$ALLOWLIST" is under $PWD
- agent-sandbox:173 — an invalid allowlist line does not abort: `die` runs inside `$(router_config …)` / `$(allowlist_entries …)` where errexit is off, so the error prints and the session starts with only the entries sorted before the bad line (verified: `api.anthropic.com`, `bad host`, `zzz.example.com` → rc=0, only api.anthropic.com configured) → validate first in the parent shell (e.g. `allowlist_entries "$ALLOWLIST" >/dev/null || exit 1` before line 173, or `shopt -s inherit_errexit`), and add a test that `router_config` fails on a bad mid-file entry - agent-sandbox:203 — launching from `$HOME` (or any parent of `$CONFIG_DIR`/`$ALLOWLIST`) mounts the allowlist read-write, so claude can widen the next session's egress → refuse to start when `readlink -f "$ALLOWLIST"` is under `$PWD`
unkin-agent added 1 commit 2026-10-23 22:46:25 +11:00
Abort on invalid allowlist lines; refuse launch dirs exposing $HOME or the allowlist
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
71c1999e27
Author
Member
  • agent-sandbox:153 — dirname "$ALLOWLIST" checks the symlink's dir, not the file's real location; with allowed-hosts symlinked into e.g. ~/dotfiles, launching from ~/dotfiles passes the check and claude gets the allowlist read-write → also check dirname "$(realpath -m "$ALLOWLIST")" and add a launch test for a symlinked allowlist file
- agent-sandbox:153 — `dirname "$ALLOWLIST"` checks the symlink's dir, not the file's real location; with `allowed-hosts` symlinked into e.g. `~/dotfiles`, launching from `~/dotfiles` passes the check and claude gets the allowlist read-write → also check `dirname "$(realpath -m "$ALLOWLIST")"` and add a launch test for a symlinked allowlist file
unkin-agent added 1 commit 2026-10-23 22:51:14 +11:00
Refuse launch dirs containing the allowlist's resolved location
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
07d93b0fe1
Author
Member
  • agent-sandbox:152 — launch dirs that are or contain $XDG_RUNTIME_DIR (e.g. /run/user/$UID, /run) are accepted, mounting the user D-Bus socket (systemd-run --user = host exec; gdbus is in the image) and the host podman socket when active; contradicts README.md:22 → refuse them the same way as $HOME (resolve, inside "$runtime" "$pwd"), add a test.
- agent-sandbox:152 — launch dirs that are or contain `$XDG_RUNTIME_DIR` (e.g. `/run/user/$UID`, `/run`) are accepted, mounting the user D-Bus socket (`systemd-run --user` = host exec; `gdbus` is in the image) and the host podman socket when active; contradicts README.md:22 → refuse them the same way as `$HOME` (resolve, `inside "$runtime" "$pwd"`), add a test.
benvin merged commit 1e250f1113 into main 2026-10-24 16:19:58 +11:00
benvin deleted branch benvin/scaffold 2026-10-24 16:19:58 +11:00
Sign in to join this conversation.