A single bash script that runs a coding harness (pi by default) inside a rootless Ubuntu 24.04 container with only the current directory mounted at /project, an explicit egress allowlist and an explicit environment: nothing is passed in that was not named on the command line or in a config file. Network enforcement here is empirical rather than assumed (tests/spike_network.sh, docs/networking.md). The original design -- a sidecar proxy on an internal network -- does not work rootless on this host, so allow mode runs pasta with --map-host-loopback, installs an inet aisb ruleset inside the container that drops every egress path except the gate's, and relays through a squid gate bound to the host's loopback that resolves names itself and refuses anything unlisted. The entrypoint checks that the default-drop rule really exists before setpriv drops to uid 1000 with an empty capability set, and exits 77/78 rather than run unenforced. The Containerfile, the entrypoint, the squid config generator and the ruleset renderer all live in bin/ai-sandbox, and the image is tagged by content hash, so a policy change can never silently reuse a stale image. Tests are pure bash: 161 assertions start no container and touch no network, 256 run real ones, including proof that a raw socket, a stripped proxy env and DNS all stay blocked while an allowlisted host works over both http and CONNECT. LLM-Generated: true
49 lines
2.4 KiB
Bash
Executable File
49 lines
2.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# tests/unit/05_policy.sh -- how allow entries are classified.
|
|
source "$TESTS/lib.sh"
|
|
|
|
PROJ="$WORK/proj"; mkdir -p "$PROJ"; cd "$PROJ" || exit 1
|
|
export AISB_CONFIG_DIR="$WORK/cfg-empty"; mkdir -p "$AISB_CONFIG_DIR"
|
|
|
|
direct_line() { printf '%s' "$1" | grep '^direct=' | cut -d= -f2-; }
|
|
|
|
t_name "domains go to the gate, not to netfilter"
|
|
sb --print-policy --net allow --allow api.openai.com
|
|
assert_contains "gate_domains=api.openai.com" "domain listed for the gate"
|
|
assert_eq "" "$(direct_line "$OUT")" "no netfilter entry for a domain in gate mode"
|
|
assert_contains "proxy=100.64.0.1:18081" "gate address is the proxy"
|
|
|
|
t_name "IP and CIDR entries become direct rules"
|
|
sb --print-policy --net allow --allow 10.0.0.5:5432
|
|
assert_eq "direct=10.0.0.5 5432" "$(printf '%s' "$OUT" | grep '^direct=')" "explicit host:port"
|
|
sb --print-policy --net allow --allow 10.1.0.0/16
|
|
assert_eq "direct=10.1.0.0/16 80,443" "$(printf '%s' "$OUT" | grep '^direct=')" "CIDR gets the default ports"
|
|
sb --print-policy --net allow --allow 10.0.0.5:5432 --allow 10.9.9.9
|
|
assert_eq "direct=10.0.0.5 5432;10.9.9.9 80,443" "$(printf '%s' "$OUT" | grep '^direct=')" "several direct entries"
|
|
|
|
t_name "ipv6"
|
|
sb --print-policy --net allow --allow "[::1]:8080"
|
|
assert_eq "direct6=::1 8080" "$(printf '%s' "$OUT" | grep '^direct6=')" "bracketed [ipv6]:port parses"
|
|
assert_eq "direct=" "$(printf '%s' "$OUT" | grep '^direct=')" "and stays out of the ipv4 rules"
|
|
assert_contains "gate_domains=" "policy printed for an ipv6 entry"
|
|
|
|
t_name "--no-gate resolves names up front"
|
|
sb --print-policy --net allow --no-gate --allow localhost
|
|
assert_contains "direct=127.0.0.1 80,443" "localhost resolves to a direct rule"
|
|
assert_contains "proxy=none" "--no-gate has no proxy"
|
|
sb --print-policy --net allow --no-gate --allow nope.invalid
|
|
assert_rc 1 "an unresolvable name is an error, not a silent no-op"
|
|
assert_contains "could not resolve" "and it says so"
|
|
|
|
t_name "ports"
|
|
sb --allow api.openai.com:not-a-port --print-policy
|
|
assert_rc 1 "non-numeric port rejected"
|
|
sb --print-policy --net allow --allow api.openai.com:8443
|
|
assert_contains "gate_domains=api.openai.com" "domain+port still routed through the gate"
|
|
|
|
t_name "mixed list"
|
|
sb --print-policy --net allow --allow api.openai.com --allow 10.0.0.5:5432 --allow files.pythonhosted.org
|
|
assert_contains "api.openai.com" "domain 1"
|
|
assert_contains "files.pythonhosted.org" "domain 2"
|
|
assert_contains "10.0.0.5 5432" "ip entry"
|