From 9aaa1c4705a9b3e6362e0f3680e4ebf449d055dd Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Sat, 20 Jun 2026 08:04:04 +0200 Subject: [PATCH] fix(jail-bootstrap): pin pkgs to host versions + validate inputs Tighten agent-jail-bootstrap.sh per review of #96: - pin each package to the host's EXACT installed version (pkg query '%v' -> install name-version from the host's mounted cache); fail loudly if the host lacks it, instead of pulling a different version into the jail - set -eu; validate jail name ([A-Za-z0-9_-], non-empty) so it can't escape the bastille jails root; assert the jail root exists before touching it - guard every host source (binaries, npm modules) so a missing source fails clearly rather than producing a half-bootstrapped jail Relies on the existing host pkg-cache reachability from the jail (offline install). Co-Authored-By: Claude Opus 4.8 --- packaging/freebsd/agent-jail-bootstrap.sh | 69 ++++++++++++++++++++--- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/packaging/freebsd/agent-jail-bootstrap.sh b/packaging/freebsd/agent-jail-bootstrap.sh index dee2b63..236f2af 100755 --- a/packaging/freebsd/agent-jail-bootstrap.sh +++ b/packaging/freebsd/agent-jail-bootstrap.sh @@ -1,26 +1,79 @@ #!/bin/sh -# Agent jail bootstrap — install minimum runtime into a fresh Bastille jail. +# Agent jail bootstrap — install the minimum runtime into a fresh Bastille jail, +# pinned to the EXACT package versions the host already has. The jail reaches the +# host's pkg cache (no internet needed), so installing the host's exact versions +# guarantees host/jail parity and works offline. +# # Usage: sudo agent-jail-bootstrap.sh -set -e +# +# Run order: bastille create -> this script -> vault provision -> register. +set -eu + +JAIL_NAME="${1:-}" + +# The jail name becomes a path component, so reject anything that could escape +# /usr/local/bastille/jails//root (empty, traversal, odd characters). +case "${JAIL_NAME}" in + '') + echo "usage: $0 " >&2 + exit 2 + ;; + *[!A-Za-z0-9_-]*) + echo "error: invalid jail name '${JAIL_NAME}' (allowed: A-Z a-z 0-9 _ -)" >&2 + exit 2 + ;; +esac -JAIL_NAME="$1" JAIL_ROOT="/usr/local/bastille/jails/${JAIL_NAME}/root" +if [ ! -d "${JAIL_ROOT}" ]; then + echo "error: jail root not found: ${JAIL_ROOT} — create the jail first" >&2 + exit 1 +fi + echo "=== Bootstrap ${JAIL_NAME} ===" -# Install runtime packages (versions pinned to match host) -pkg -c "${JAIL_ROOT}" install -y python312 node24 npm-node24 bash curl +# Runtime packages. Each is pinned to the host's installed version (the host's +# cache supplies it), so the jail matches the host exactly. If the host is +# missing one, fail loudly rather than pulling a different version into the jail. +PKGS="python312 node24 npm-node24 bash curl" -# Copy colibri binaries from host +for p in ${PKGS}; do + ver="$(pkg query '%v' "${p}" 2>/dev/null || true)" + if [ -z "${ver}" ]; then + echo "error: host has no '${p}' installed — install it on the host first" >&2 + echo " (versions are pinned to the host; the cache has nothing to serve otherwise)" >&2 + exit 1 + fi + echo " ${p}-${ver}" + pkg -c "${JAIL_ROOT}" install -y "${p}-${ver}" +done + +# Copy colibri binaries from the host (same FreeBSD base, so shared libs match). for bin in colibri colibri-daemon colibri-probe colibri-mcp colibri-test-agent colibri-host-status colibri-runtime-inventory; do - cp /usr/local/bin/${bin} "${JAIL_ROOT}/usr/local/bin/${bin}" + src="/usr/local/bin/${bin}" + if [ ! -x "${src}" ]; then + echo "error: missing host binary ${src} — build/stage it before bootstrap" >&2 + exit 1 + fi + cp "${src}" "${JAIL_ROOT}/usr/local/bin/${bin}" chmod 755 "${JAIL_ROOT}/usr/local/bin/${bin}" done -# Copy npm global agents from host (jails have no internet) +# Copy npm global agents from the host (jails have no internet). NPM_PREFIX="/home/clawdie/.npm-global" mkdir -p "${JAIL_ROOT}${NPM_PREFIX}/bin" "${JAIL_ROOT}${NPM_PREFIX}/lib/node_modules" + +if [ ! -d "${NPM_PREFIX}/lib/node_modules/@earendil-works" ]; then + echo "error: missing ${NPM_PREFIX}/lib/node_modules/@earendil-works on host" >&2 + exit 1 +fi cp -a "${NPM_PREFIX}/lib/node_modules/@earendil-works" "${JAIL_ROOT}${NPM_PREFIX}/lib/node_modules/" + +if [ ! -e "${NPM_PREFIX}/bin/pi" ]; then + echo "error: missing ${NPM_PREFIX}/bin/pi on host" >&2 + exit 1 +fi cp -a "${NPM_PREFIX}/bin/pi" "${JAIL_ROOT}${NPM_PREFIX}/bin/pi" echo "Done — ${JAIL_NAME} ready for vault provision." -- 2.45.3