pin the base image to a digest, with TTL refresh and audit labels
A tag is a moving pointer, and three things conspired to make the base immutable-in-practice while mutable-in-theory: image_hash() hashed the string "ubuntu:24.04" rather than what it resolves to, ensure_image() returns early when the tag exists, and podman build's pull policy is "missing" -- so even 'build --rebuild' rebuilt on the cached base. A Canonical patch simply never arrived, and nothing said so. Now the tag is resolved to a registry digest (skopeo, falling back to a pull unless AISB_BASE_PULL_FALLBACK=0), recorded per-ref under $AISB_CACHE_DIR/base/ at 0600, and used in FROM *and* in image_hash(): refreshing the base produces a new image instead of mutating one a sandbox may be running. Freshness is a TTL (AISB_BASE_TTL_DAYS=7, 0 every build, -1 never) plus an explicit 'update' / 'update --check'. Two rules keep it usable rather than merely strict. A stale pin beats a mutable tag: offline or with AISB_BASE_AUTO_UPDATE=0 the recorded digest is still used, and only a build with no digest at all is allowed -- announced as UNPINNED, labelled that way, warned about by doctor. And preview modes stay deterministic: --print-* never reach the network, inspect is the surface that asks the machine now. Both images carry aisb.base-digest / .node / .pi-package / .pi-version labels plus /etc/aisb/manifest.txt of resolved apt versions, which answers "which squid was enforcing this?" after the build log is gone. Tags are named after the base instead of the hardcoded u24- prefix. Bug found on the way: POL[base_digest]="$(base_pin ...)" silently produced unpinned labels, because an assignment inside a command substitution is discarded with the subshell. Helpers return text; callers assign. Also fixed: the manifest step used jq on npm's dependency objects as if they were strings, and a missing Node checksum used to skip verification quietly -- it now warns. 311 assertions pass (unit + e2e), shellcheck clean. LLM-Generated: true
This commit is contained in:
@@ -23,6 +23,31 @@ sb --print-policy --net none --install hexd > /dev/null
|
||||
IMG3="$("$AISB" --print-policy --net none --install hexd | grep '^image=' | cut -d= -f2)"
|
||||
[[ "$IMG3" != "$IMG" ]] && _ok "extra packages produce a different tag" || _bad "extra packages change the tag"
|
||||
|
||||
t_name "the image says what it was built from"
|
||||
BASE_DIGEST="$("$AISB" --print-policy --net none 2>/dev/null | grep '^base_digest=' | cut -d= -f2)"
|
||||
if curl -m8 -sS -o /dev/null http://example.com 2>/dev/null && [[ "$BASE_DIGEST" == sha256:* ]]; then
|
||||
_ok "the base was pinned to $BASE_DIGEST"
|
||||
LABEL_DIGEST="$(podman image inspect "$IMG" --format '{{ index .Config.Labels "aisb.base-digest" }}' 2>/dev/null)"
|
||||
assert_eq "$BASE_DIGEST" "$LABEL_DIGEST" "the image label carries the same digest"
|
||||
else
|
||||
echo " (skipped the digest assertions: registry unreachable or base unpinned)"
|
||||
fi
|
||||
OUT="$(podman image inspect "$IMG" --format '{{ range $k, $v := .Config.Labels }}{{$k}}={{$v}} {{end}}' 2>&1)"
|
||||
assert_contains "aisb.node=" "the Node version is recorded on the image"
|
||||
assert_contains "aisb.pi-version=" "and the harness version"
|
||||
OUT="$(podman run --rm --network none --entrypoint cat "$IMG" /etc/aisb/manifest.txt 2>&1)"
|
||||
assert_contains "nftables=" "the manifest records the resolved nftables version"
|
||||
assert_contains "node=v" "and the Node version actually installed"
|
||||
assert_contains "pi-coding-agent" "and the harness package version"
|
||||
|
||||
sb build --gate
|
||||
GIMG="$("$AISB" --print-policy --net none 2>/dev/null | grep '^gate_image=' | cut -d= -f2)"
|
||||
podman image exists "$GIMG" && _ok "the gate image exists: $GIMG" || _bad "the gate image exists"
|
||||
OUT="$(podman image inspect "$GIMG" --format '{{ index .Config.Labels "aisb.role" }}' 2>&1)"
|
||||
assert_eq "gate" "$(printf '%s' "$OUT" | tail -1)" "the gate image is labelled as the gate"
|
||||
OUT="$(podman run --rm --network none --entrypoint cat "$GIMG" /etc/aisb/manifest.txt 2>&1)"
|
||||
assert_contains "squid=" "and its manifest records the squid version doing the filtering"
|
||||
|
||||
t_name "the entrypoint fails closed without a policy"
|
||||
podman run --rm --network none --cap-drop=ALL "$IMG" true > "$WORK/fc.out" 2>&1
|
||||
assert_eq 78 "$?" "no /run/aisb policy -> exit 78, nothing ran"
|
||||
|
||||
@@ -151,4 +151,5 @@ t_name "doctor"
|
||||
sb doctor
|
||||
assert_rc 0 "doctor reports no problems"
|
||||
assert_contains "bind mount + uid mapping" "doctor verifies uid mapping"
|
||||
assert_contains "base pin:" "doctor reports which base digest the image was built on"
|
||||
assert_contains "rootless" "doctor verifies rootless podman"
|
||||
|
||||
Reference in New Issue
Block a user