3f382bf762
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
108 lines
4.3 KiB
Bash
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 ]]
|