Files
felhom-agent/REPORT.md
T
2026-06-12 09:50:28 +02:00

3.4 KiB

REPORT — felhom-agent v0.24.0: role-gate the eject path

What changed (Part A of the storage-fixes spec)

POST /disks/eject is now role-gated at the agent. Previously handleDiskEject validated scope + where, computed dependents, then unconditionally called Unmount — so a direct API call (or a compromised controller) could POST /disks/eject {where:"/var/lib/vz"} (or the PBS mount) and unmount a protected storage. The controller UI hid the button for system/backup, but the agent did not enforce it, contradicting the "system is never unmountable" invariant and the defense-in-depth the wipe path upholds.

A1 — refuse eject of system/backup (internal/localapi/disks.go)

  • handleDiskEject now, before Unmount, resolves the authoritative protection role of the storage mounted at where via the new roleForMountPath (the agent's own storage-view + host-topology classification — RoleForStorage keyed on MountPath == where — never the caller's claim). It refuses 403 with no Unmount unless the role is user-data.
  • Fail-safe: a view error or no storage target found at where → treated as system (most- protected) → refused. Consistent with the wipe gate's most-protected-on-ambiguity default.
  • Mirrors the wipe path's logging: "protected — eject refused by role".
  • hostReader() seam added (Options.HostReader, optional; defaults to the production *storage.ProcHostReader) so the role classification (SystemDisks) is unit-testable. handleDisks and deviceRole now share the same seam (no production behaviour change — default is the real reader).

A2 — non-hollow test

  • TestEject_RoleGated asserts a system mount and a backup (PBS) mount are refused with no Unmount, a user-data mount ejects (Unmount called once), and an unresolvable mount fails safe to refused — the same non-hollowness (executor-not-invoked) the wipe tests use.
  • TestEject_UnmountAndDependents updated to a user-data target so the role-gate permits it.

Build / deploy

  • Version bumped 0.23.0 → 0.24.0 (cmd/felhom-agent/main.go). CHANGELOG updated (newest on top).
  • Built on the build server (go1.26) with -ldflags -X main.version=0.24.0, installed to /usr/local/bin/felhom-agent on felhom-pve (prior binary kept as .bak-0.23.0), service restarted — felhom-agent --version0.24.0, local-api listening on 192.168.0.162:8443.

Live validation (felhom-pve, guest 9201)

  • GET /disks (guest 9201): roles felhom-usb=user-data, local=system, felhom-pbs=backup, local-lvm=system.
  • Hand-issued POST /disks/eject {where:"/var/lib/vz"} (the local system storage's real mount) → HTTP 403, body mount is system/backup-protected — eject refused (role: system); agent logged protected — eject refused by role … role=system; no unmount occurred.
  • POST /disks/eject {where:"/mnt/nonexistent"} (no storage target) → HTTP 403 (fail-safe).
  • /mnt/felhom-usb (the live user-data data drive) was deliberately not live-ejected to avoid disrupting the running guest; the positive user-data eject path is covered by the non-hollow TestEject_RoleGated (asserts Unmount is called exactly once for a user-data mount).
  • go test ./... green for the whole module.

Notes / not done

  • No golden change required for Part A — the agent runs host-side, not baked into guests.