Files
felhom-agent/REPORT.md
T
2026-07-01 17:01:08 +02:00

5.1 KiB

REPORT — Impl-1: format-safety foundation (agent v0.54.0)

Date: 2026-07-01 · Repo: felhom-agent · Class: Risky/supervised (destructive Format/mkfs path + sudoers). Built/tested/deployed normally; live validation proves the guard REFUSES dangerous targets (safe) and formats only the throwaway /dev/sdd.

Impl-1 of the drive-enrollment series (SPIKE-drive-enrollment-2026-07-01), built BEFORE the enrollment feature to harden the existing format path.

1. Baseline → target

felhom-agent 7639ab5 (v0.53.0) → v0.54.0.

2. Root cause + files changed

Format delegated authorization to its caller and only checked DataBearing (has-data) — insufficient: the OS disk is data-bearing yet catastrophic; and the sudoers permitted mkfs.* /dev/*.

  • internal/storage/claim.go (new) — the unclaimed-disk filter: classifyClaim (pure) + gatherClaimFacts (SystemDisks + lsblk member-FSTYPE + foreign-mount + RO + authoritative pvs/zpool; fail-safe → CLAIMED).
  • internal/storage/hostops.go — mandatory guard at the top of Format (refuse unless unclaimed); Binaries.MkfsGuarded; Format now execs the guarded wrapper; SudoHostOps gains a HostReader.
  • configs/felhom-mkfs-guarded.sh (new) — the root wrapper (below-the-agent gate).
  • configs/felhom-agent.sudoersFELHOM_FORMAT drops raw mkfs.*, allowlists only the wrapper; read-only pvs/zpool status -P added to FELHOM_DISK.
  • internal/storage/claim_test.go (new), hostops_disk_test.go (updated), internal/capability/manifest.go (mkfs sample → wrapper). CHANGELOG.md, README.md, version.
  • Commit 5209830 on main (parent 05f22a9).

3. Green gate

go build ./..., go vet ./..., go test ./... — clean (local + build server 180).

4. Tests + red-proof

  • TestClassifyClaim (pure, table-driven): every claim signal (system/LVM-PV/ZFS/mdraid/member-FSTYPE/ foreign-mount/RO/undeterminable) → CLAIMED; fail-safe (gather error) → CLAIMED; clean disk + our-own- drive-re-init → unclaimed. Red-proof: the "system/OS disk (non-data-bearing)" case — removing the isSystem check flips it to unclaimed=true → the test FAILS (proves the guard adds safety beyond DataBearing, which would call a blank OS disk benign). Verified fail, reverted.
  • TestFormatGuard_{RefusesSystemDisk,RefusesLVMMember,AllowsUnclaimed}: the guard is wired into Format — refuses claimed devices (no mkfs), allows unclaimed (invokes the wrapper).
  • TestParseLsblkNodes; capability manifest coverage test updated + green.

5. Deploy + live validation (felhom-pve — SAFE, only /dev/sdd formatted)

Published Gitea felhom-agent/0.54.0 (sha 0500dd9bd15b03e67e8c93c0ac7aa8008d7413c335e6f852aeaf074d3b364eda). Deployed to felhom-pve in order: install wrapper (0755) → swap sudoers (visudo-validated) → agent binary (backup .bak-0.53.0) → restart. (Note: my manual scp of the Windows working-copy sudoers had CRLF and failed visudo; fixed live by CR-stripping + re-install. The committed repo file is LF and the host-install fetch_raw-from-Gitea path is unaffected.)

Proof Result
P-1 wrapper on /dev/sda (OS) via agent sudo path REFUSED "system/OS disk (/dev/sda backs /boot/efi)", exit 1, no mkfs
P-2 wrapper on /dev/sda3 (the LVM PV) REFUSED (system), no mkfs
P-3 raw sudo mkfs.ext4 -F /dev/sda as felhom-agent DENIED ("a password is required" — not allowlisted; Part B)
P-4 wrapper on /dev/sdd (unclaimed throwaway) ACCEPTED → mkfs.ext4 → blkid /dev/sdd = TYPE="ext4"
agent-guard live reads (as felhom-agent) sudo pvs / sudo lsblk <cols> / sudo zpool status -P all OK → the guard can gather on the live host
post-deploy health agent 0.54.0 active; --selftest=read = 5 storages (3b-fix intact); 9201 running

Part A (agent guard) is unit-tested (logic, incl. red-proof) and its live host reads are confirmed working as the agent; Part B (wrapper) is live-proven on real devices (sda/sda3 refused, sdd accepted) + raw-mkfs denied.

6. NOT exercised / deferred

  • The /disks/format local-API endpoint was not invoked live (needs a guest local-API token). The guard it calls is identical to the unit-tested code, and its live reads + the wrapper below it are proven on real devices — so the stack is validated, just not through the HTTP entrypoint.
  • Impl-3 residual: a raw disk passed through to ANOTHER VM looks unused to the host — a host-level filter can't detect it; the shared-box operator gate (Impl-3) closes that. Impl-1 closes everything host-visible (a strict improvement over the prior no-guard state).

7. Observations

  • The guard's mount rule ALLOWS Felhom's own drives (mounted under /mnt/felhom-drives) for re-init; their DATA is protected by the separate DataBearing wipe-confirm, not this guard. So the spec's "refuse sdb/sdc" was not applicable to the guard (they're re-initable Felhom drives) — I did not mkfs them (customer data), per the STOP.
  • The throwaway /dev/sdd is now ext4 (was ntfs); unmounted, not enrolled. Safe to unplug.