Files
felhom-agent/scripts/mkfs-guarded-harness.sh
T
admin 3f382bf762 v0.61.0: audit fixes B1 (random temp staging) + D1 (mkfs wrapper member/RO re-checks) + D2 (empty-lsblk fail-safe) + D3 (blank-format anti-retarget)
From AUDIT-blast-radius-hostroot-localapi-2026-07-02.md. Each fix ships with a
non-hollow test + a companion red-proof (shown failing on the pre-fix impl).
Sudoers install-source grants became globs — deploy the sudoers drop-in with
the binary. A1 (stale-lock pool-membership) deliberately excluded (spike).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-03 07:25:50 +02:00

108 lines
4.3 KiB
Bash

#!/bin/bash
#===============================================================================
# mkfs-guarded-harness — audit-D1 test harness for configs/felhom-mkfs-guarded.sh.
#
# Runs the REAL wrapper against throwaway loop devices with:
# - a PATH-shimmed `lsblk` that fakes the member FSTYPE signal (FIXTURE_FSTYPE),
# - a private mount namespace (unshare -m) that bind-mounts a RECORDER over
# /usr/sbin/mkfs.ext4 — so a wrapper that decides to format provably "formats"
# (the recorder logs the argv) while NO real mkfs can ever run, and
# - /dev/null bind-mounted over pvs for the pvs-absent case.
#
# Nothing real is formatted; nothing leaks outside the namespace; the loop files
# are throwaway temps. Run as root on a Linux host (losetup/unshare needed).
#
# Usage: mkfs-guarded-harness.sh <path-to-wrapper-under-test>
# Exit 0 = all cases behave like the FIXED wrapper should. Running it against the
# pre-fix wrapper is the red-proof: the member/RO cases will FAIL (recorder fired).
#===============================================================================
set -euo pipefail
WRAPPER="${1:?usage: mkfs-guarded-harness.sh <wrapper>}"
WRAPPER="$(readlink -f "$WRAPPER")"
[[ -r "$WRAPPER" ]] || { echo "no wrapper at $WRAPPER" >&2; exit 2; }
[[ "$(id -u)" == 0 ]] || { echo "must run as root (losetup/unshare)" >&2; exit 2; }
WORK="$(mktemp -d /tmp/mkfs-harness.XXXXXX)"
LOOP=""; LOOP_RO=""
cleanup() {
[[ -n "$LOOP" ]] && losetup -d "$LOOP" 2>/dev/null || true
[[ -n "$LOOP_RO" ]] && losetup -d "$LOOP_RO" 2>/dev/null || true
rm -rf "$WORK"
}
trap cleanup EXIT
# --- throwaway loop devices (a plain RW one + a kernel-read-only one) ---
truncate -s 64M "$WORK/plain.img"
truncate -s 64M "$WORK/ro.img"
LOOP="$(losetup -f --show "$WORK/plain.img")"
LOOP_RO="$(losetup -f --show -r "$WORK/ro.img")"
echo "loop devices: rw=$LOOP ro=$LOOP_RO"
# --- PATH shim: lsblk fakes only the `-nro FSTYPE` read when FIXTURE_FSTYPE is set ---
mkdir -p "$WORK/shim"
cat > "$WORK/shim/lsblk" <<'EOF'
#!/bin/bash
if [[ -n "${FIXTURE_FSTYPE:-}" && "${1:-}" == "-nro" && "${2:-}" == "FSTYPE" ]]; then
echo "$FIXTURE_FSTYPE"
exit 0
fi
exec /usr/bin/lsblk "$@"
EOF
chmod 0755 "$WORK/shim/lsblk"
# --- the mkfs recorder bound over /usr/sbin/mkfs.ext4 inside the namespace ---
cat > "$WORK/fake-mkfs" <<'EOF'
#!/bin/bash
echo "mkfs.ext4 $*" >> "$MKFS_MARKER"
exit 0
EOF
chmod 0755 "$WORK/fake-mkfs"
# run_case <name> <dev> <fixture_fstype> <hide_pvs 0|1> <want refuse|format> <want_msg_substr>
pass=0; fail=0
run_case() {
local name="$1" dev="$2" fixture="$3" hidepvs="$4" want="$5" msg="$6"
local marker="$WORK/marker.$RANDOM"; rm -f "$marker"
local hide_cmds=""
if [[ "$hidepvs" == 1 ]]; then
for p in /usr/sbin/pvs /sbin/pvs; do
[[ -e "$p" ]] && hide_cmds+="mount --bind /dev/null $p; "
done
fi
set +e
out="$(unshare -m bash -c "
set -e
mount --bind '$WORK/fake-mkfs' /usr/sbin/mkfs.ext4
$hide_cmds
export PATH='$WORK/shim':\$PATH MKFS_MARKER='$marker' FIXTURE_FSTYPE='$fixture'
exec bash '$WRAPPER' '$dev' ext4
" 2>&1)"
rc=$?
set -e
local fired=no; [[ -s "$marker" ]] && fired=yes
local verdict=FAIL
if [[ "$want" == refuse ]]; then
[[ $rc -ne 0 && "$fired" == no && "$out" == *"$msg"* ]] && verdict=PASS
else
[[ $rc -eq 0 && "$fired" == yes && "$(cat "$marker")" == *"$dev"* ]] && verdict=PASS
fi
if [[ "$verdict" == PASS ]]; then pass=$((pass+1)); else fail=$((fail+1)); fi
printf '%-4s %-38s rc=%-3s mkfs-fired=%-3s want=%s\n' "$verdict" "$name" "$rc" "$fired" "$want"
[[ "$verdict" == FAIL ]] && printf ' output: %s\n' "$out"
return 0
}
echo "--- wrapper under test: $WRAPPER ---"
run_case "refuse zfs_member" "$LOOP" "zfs_member" 0 refuse "zfs_member"
run_case "refuse linux_raid_member" "$LOOP" "linux_raid_member" 0 refuse "linux_raid_member"
run_case "refuse crypto_LUKS" "$LOOP" "crypto_LUKS" 0 refuse "crypto_LUKS"
run_case "refuse swap" "$LOOP" "swap" 0 refuse "swap"
run_case "refuse LVM2_member" "$LOOP" "LVM2_member" 0 refuse "LVM"
run_case "refuse read-only device" "$LOOP_RO" "" 0 refuse "read-only"
run_case "pvs-hidden still catches LVM" "$LOOP" "LVM2_member" 1 refuse "LVM"
run_case "plain blank disk formats" "$LOOP" "" 0 format ""
echo "--- $pass passed, $fail failed ---"
[[ $fail -eq 0 ]]