d1bd44d2d5
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.4 KiB
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)
handleDiskEjectnow, beforeUnmount, resolves the authoritative protection role of the storage mounted atwherevia the newroleForMountPath(the agent's own storage-view + host-topology classification —RoleForStoragekeyed onMountPath == where— never the caller's claim). It refuses 403 with noUnmountunless the role isuser-data.- Fail-safe: a view error or no storage target found at
where→ treated assystem(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.handleDisksanddeviceRolenow share the same seam (no production behaviour change — default is the real reader).
A2 — non-hollow test
TestEject_RoleGatedasserts asystemmount and abackup(PBS) mount are refused with noUnmount, auser-datamount ejects (Unmount called once), and an unresolvable mount fails safe to refused — the same non-hollowness (executor-not-invoked) the wipe tests use.TestEject_UnmountAndDependentsupdated 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-agentonfelhom-pve(prior binary kept as.bak-0.23.0), service restarted —felhom-agent --version→0.24.0, local-api listening on192.168.0.162:8443.
Live validation (felhom-pve, guest 9201)
GET /disks(guest 9201): rolesfelhom-usb=user-data,local=system,felhom-pbs=backup,local-lvm=system.- Hand-issued
POST /disks/eject {where:"/var/lib/vz"}(thelocalsystem storage's real mount) → HTTP 403, bodymount is system/backup-protected — eject refused (role: system); agent loggedprotected — 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-hollowTestEject_RoleGated(assertsUnmountis 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.