Files
felhom-agent/configs/felhom-mkfs-guarded.sh
T
admin 98f3cdd76a
gates / gates (push) Successful in 25s
Security review of 912f7c1: the mkfs wrapper trusts no sibling claim; the internal-place helper fails closed
- felhom-mkfs-guarded: the 0.158.0 exemption for a sibling part mounted at /mnt/<name> + under /mnt/felhom-drives is
  removed — the agent can make that corroborating mount itself. Every mount of every node outside /mnt/felhom-drives
  refuses (kernel mount table); an unlistable disk refuses. CandidatePartition.Formattable is false for every part of a
  disk with a mounted node, so it is never offered for formatting (attach still is).
- felhom-internal-place: a failed lvs listing, a listing without the pool, or a blkid exit other than 0/2 refuses
  (they read as "nothing promised" / "no signature" before).
Every new test was seen failing against the 0.158.0 versions.

Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
2026-10-16 04:02:00 +02:00

107 lines
5.3 KiB
Bash

#!/bin/bash
#===============================================================================
# felhom-mkfs-guarded — the ONLY mkfs path the felhom-agent sudoers permits (Impl-1 Part B,
# SPIKE-drive-enrollment-2026-07-01 §SQ3). Defense-in-depth BELOW the agent: even a buggy or
# compromised agent cannot mkfs a catastrophic target through this — it re-checks, as root, the
# cheap catastrophic cases (OS/system disk, LVM physical volume, a foreign mount, a read-only
# device, and any LVM/ZFS/mdraid/LUKS/swap member signature — audit D1) and refuses.
#
# The agent's full unclaimed-disk filter (internal/storage/claim.go) is the PRIMARY guard; this
# wrapper is a deliberately minimal, auditable second gate. It is NOT the place for the full filter.
#
# Usage: felhom-mkfs-guarded <device> <fstype:ext4|xfs>
#===============================================================================
set -euo pipefail
die() { echo "felhom-mkfs-guarded: REFUSED: $*" >&2; exit 1; }
dev="${1:-}"; fstype="${2:-}"
[[ -n "$dev" && -n "$fstype" ]] || die "usage: felhom-mkfs-guarded <device> <fstype>"
# The device must be a REAL block-device node under /dev — no symlink (e.g. /dev/disk/by-*), no traversal.
[[ "$dev" == /dev/* ]] || die "device must be under /dev ($dev)"
[[ "$dev" != *..* ]] || die "path traversal ($dev)"
[[ -b "$dev" ]] || die "not a block device ($dev)"
[[ ! -L "$dev" ]] || die "device must be a real node, not a symlink ($dev)"
# Whole-disk of the target (a partition's parent, else the disk itself).
pk="$(lsblk -ndo PKNAME "$dev" 2>/dev/null || true)"
whole="$dev"; [[ -n "$pk" ]] && whole="/dev/$pk"
# 1) OS/system disk — does the target's whole-disk back /, /boot or /boot/efi?
while read -r src mnt _rest; do
case "$mnt" in
/|/boot|/boot/efi)
spk="$(lsblk -ndo PKNAME "$src" 2>/dev/null || true)"
swhole="$src"; [[ -n "$spk" ]] && swhole="/dev/$spk"
[[ "$swhole" == "$whole" || "$src" == "$dev" || "$src" == "$whole" ]] && die "system/OS disk ($dev backs $mnt)"
;;
esac
done < /proc/mounts
# 2) LVM physical volume anywhere on the target disk or its partitions. pvs is resolved by ABSOLUTE
# path (audit D1: `command -v pvs` silently skipped this check when pvs wasn't on the caller's
# PATH); if neither candidate exists, check 5's LVM2_member FSTYPE loop still catches a PV
# independently — pvs-absent never silently drops LVM detection.
pvsbin=""
for c in /usr/sbin/pvs /sbin/pvs; do
[[ -x "$c" ]] && { pvsbin="$c"; break; }
done
if [[ -n "$pvsbin" ]]; then
while read -r pv; do
pv="${pv//[[:space:]]/}"; [[ -z "$pv" ]] && continue
pvpk="$(lsblk -ndo PKNAME "$pv" 2>/dev/null || true)"
pvwhole="$pv"; [[ -n "$pvpk" ]] && pvwhole="/dev/$pvpk"
[[ "$pvwhole" == "$whole" ]] && die "device holds an LVM physical volume ($pv)"
done < <("$pvsbin" --noheadings -o pv_name 2>/dev/null || true)
fi
# 3) mounted OUTSIDE Felhom's own drive area = a live foreign filesystem → catastrophic. Mounts under
# /mnt/felhom-drives are our own drives (the agent detaches before a re-init) → allowed.
# Agent v0.158.0: read from the kernel's mount table for EVERY node of the disk (lsblk's MOUNTPOINT shows one mount
# per node; a bind elsewhere hid behind it), and a disk whose nodes cannot be listed REFUSES (it passed before).
# Deliberately NO exemption for a sibling that is Felhom's own enrolled drive (raw /mnt/<name> + /mnt/felhom-drives):
# the only corroboration available here is a mount the agent itself can make, so trusting it would let a compromised
# agent unlock a host owner's disk. Consequence (felhom.eu `16` §13): the free part next to an in-use Felhom drive
# cannot be formatted; the dashboard does not offer it.
PROC_MOUNTS=/proc/mounts
node_mounts() { awk -v d="$1" '$1 == d { print $2 }' "$PROC_MOUNTS"; }
disk_nodes="$(lsblk -nrpo NAME "$whole" 2>/dev/null || true)"
[[ -n "$disk_nodes" ]] || die "cannot list the nodes of $whole — refusing"
while read -r node; do
[[ -z "$node" ]] && continue
while read -r mp; do
[[ -z "$mp" ]] && continue
case "$mp" in
/mnt/felhom-drives|/mnt/felhom-drives/*) : ;;
*) die "device (or a partition) is mounted at $mp ($node)" ;;
esac
done < <(node_mounts "$node")
done <<< "$disk_nodes"
# 4) read-only device (audit D1): a device the kernel marks RO is never a formattable data disk.
wbase="${whole#/dev/}"
rof="/sys/block/$wbase/ro"
if [[ -r "$rof" ]]; then
ro="$(cat "$rof" 2>/dev/null || true)"
[[ "$ro" == "1" ]] && die "read-only device ($whole)"
fi
# 5) member/active FSTYPEs anywhere on the target disk or its partitions (audit D1). Mirrors
# claim.go memberFSTypes exactly: a member of LVM/ZFS/mdraid/LUKS or active-swap signature is
# always a claim, never a plain formattable data disk. This also independently catches an LVM PV
# when pvs is not installed (check 2's belt-and-suspenders).
while read -r fst; do
[[ -z "$fst" ]] && continue
case "$fst" in
LVM2_member|zfs_member|linux_raid_member|crypto_LUKS|swap)
die "device holds a $fst signature ($whole)" ;;
esac
done < <(lsblk -nro FSTYPE "$whole" 2>/dev/null || true)
# Passed the catastrophic checks → format. exec so the mkfs exit status is the wrapper's.
case "$fstype" in
ext4) exec /usr/sbin/mkfs.ext4 -F "$dev" ;;
xfs) exec /usr/sbin/mkfs.xfs -f "$dev" ;;
*) die "unsupported fstype ($fstype)" ;;
esac