#!/bin/bash # rootpw-emission.sh — R-61 slice 1 regression harness for build-felhom-iso.sh's root-password # emission. Runs ON THE BUILD HOST (needs docker + the assistant image present for the build # script's preflight, but --dry-run never starts a container). Asserts, against a real dry-run # with a fake source ISO: # 1. the 0600 sibling .rootpw.txt exists and names the ISO it belongs to; # 2. its plaintext MATCHES the hash actually rendered into answer.toml # (openssl passwd -6 -salt cross-check — file <-> answer, not file <-> itself); # 3. the plaintext appears NOWHERE in the build's stdout/stderr; # 4. the manifest heredoc in the build script carries the rootpw.txt POINTER and never # references the plaintext variable (the manifest gets pasted into committed REPORTs — # the plaintext must be structurally unable to ride along). # Red-proof (documented, run manually): add `root-password-plain : ${ROOT_PLAIN}` to the manifest # heredoc -> assertion 4 fails -> restore. set -uo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BUILD="$HERE/../build-felhom-iso.sh" fail=0 say() { echo "TEST: $*"; } check() { if eval "$2"; then echo " ok: $1"; else echo " FAIL: $1"; fail=1; fi; } SCRATCH="$(mktemp -d "${TMPDIR:-/tmp}/felhom-rootpw-test.XXXXXX")" trap 'chmod -R u+w "$SCRATCH" 2>/dev/null; rm -rf "$SCRATCH"' EXIT mkdir -p "$SCRATCH/tmp" "$SCRATCH/out" # fake source ISO (the dry run only sha-verifies it; nothing reads its content as an ISO) FAKE_ISO="$SCRATCH/proxmox-ve_9.9-1.iso" head -c 65536 /dev/urandom > "$FAKE_ISO" FAKE_SHA="$(sha256sum "$FAKE_ISO" | awk '{print $1}')" say "dry-run build (pairing, nested-canary) with FELHOM_ISO_KEEP_WORK=1" BUILD_LOG="$SCRATCH/build.log" FELHOM_ISO_KEEP_WORK=1 TMPDIR="$SCRATCH/tmp" bash "$BUILD" \ --pve-iso "$FAKE_ISO" --iso-sha256 "$FAKE_SHA" \ --profile "$HERE/../profiles/nested-canary.profile" \ --pairing --out "$SCRATCH/out" --dry-run > "$BUILD_LOG" 2>&1 rc=$? check "dry-run exited 0" "[ $rc -eq 0 ]" ROOTPW_FILE="$(ls "$SCRATCH"/out/*.rootpw.txt 2>/dev/null | head -1)" check "rootpw sibling file exists" "[ -n \"$ROOTPW_FILE\" ] && [ -f \"$ROOTPW_FILE\" ]" check "rootpw file mode is 0600" "[ \"\$(stat -c %a \"$ROOTPW_FILE\")\" = 600 ]" PW="$(awk '{print $1}' "$ROOTPW_FILE" 2>/dev/null)" ISO_NAME="$(awk '{print $2}' "$ROOTPW_FILE" 2>/dev/null)" check "password field has the throwaway shape" "[[ \"$PW\" == felhom-throwaway-* ]]" check "file names the ISO it belongs to" "[ \"$ISO_NAME.rootpw.txt\" = \"$(basename "$ROOTPW_FILE")\" ]" # cross-check: the plaintext must correspond to the hash RENDERED INTO THE ANSWER (kept workspace) ANSWER="$(ls "$SCRATCH"/tmp/felhom-iso.*/answer.toml 2>/dev/null | head -1)" check "kept workspace has answer.toml" "[ -n \"$ANSWER\" ] && [ -f \"$ANSWER\" ]" # the hash contains literal $-signs — compare OUTSIDE check()'s eval and pass only a verdict in HASH="$(sed -n 's/^root-password-hashed = "\(.*\)"$/\1/p' "$ANSWER" 2>/dev/null)" SALT="$(printf '%s' "$HASH" | awk -F'$' '{print $3}')" RECOMPUTED="$(openssl passwd -6 -salt "$SALT" "$PW" 2>/dev/null)" HASH_MATCH=no; [ -n "$HASH" ] && [ "$RECOMPUTED" = "$HASH" ] && HASH_MATCH=yes check "plaintext matches the answer's rendered hash" "[ \"$HASH_MATCH\" = yes ]" # the plaintext must never surface on stdout/stderr check "plaintext absent from build stdout/stderr" "! grep -qF \"$PW\" \"$BUILD_LOG\"" # manifest heredoc guard (source-level: dry-run emits no manifest; the live build's manifest is # additionally verified in the ISO-build leg). Pointer present, plaintext variable absent. # the heredoc text contains ${VAR} references — grep it outside check()'s eval, pass verdicts in MANIFEST_SRC="$(awk '/^cat > "\$OUT_ISO.manifest.txt" <