hub v0.159.0 (R-1154, after security review): the enroll reuse path re-issues nothing, it warns the operator

The first draft (5121536f, unpushed) re-issued the claim code and the off-site login on a
reuse whose old box was stale or switched off. A security review flagged it: the caller
holds only the household's retrieval passphrase, and both re-issues are operator
session-only actions (05 section 17.5, 09 decision 252, R-1055/R-1059). Now the reuse
path classifies the old box and re-issues nothing: gone -> host_reinstall_over_old_host
(warning, mailed to the operator: delete the old host and re-run, or re-issue by hand);
live -> host_reenroll_while_live (info). The mint path after the operator's delete is
unchanged and still re-issues. The once-per-silence record and the claim engine comment
edit are reverted. 05 section 8, README and CHANGELOG updated; tests red-proved.
This commit is contained in:
admin committed 2026-10-19 03:29:44 +02:00
1 parent 5121536f10
commit b505ce4ba0
9 files changed
+265 -268

No files matched your search

+5 -4
View File
@@ -152,9 +152,10 @@ Events are the primary monitoring mechanism. Each event has: customer_id, event_
- `whole_guest_backup_refused` (warning) / `whole_guest_backup_refusal_cleared` (info) — the same checker, a
SEPARATE state (R-1150, v0.159.0): the agent's `localapi:backup-cadence-cap` reports that it refused a whole-guest
backup a guest asked for beyond its schedule (R-973). Operator-only; hub-origin only (not POST-able by a box).
- `host_reenroll_reissued` / `host_reenroll_not_reissued` (info) — `POST /host-enroll` reused an existing host
record and re-issued the claim code and the off-site login (the old box is gone), or did not (the box is live)
(R-1154, v0.159.0). Operator-only; hub-origin only.
- `host_reinstall_over_old_host` (warning) / `host_reenroll_while_live` (info) — `POST /host-enroll` reused an
existing host record while the old box looks gone (the operator is mailed: delete the old host and re-run, or
re-issue by hand) or while it is live (recorded) (R-1154, v0.159.0). Nothing is re-issued on that path.
Operator-only; hub-origin only.
- `host_leaf_changed` — `HostLeafChecker` (`monitor/host_leaf.go`, every 60s): an agent's reported
local-API **leaf fingerprint** changed (an agent re-key) — proactive, fleet-wide, independent of any
controller's channel-health check. Trust-on-first-report baseline; empty fp = unknown (never alerts).
@@ -187,7 +188,7 @@ Config retrieval uses a separate per-customer retrieval password (not the API ke
|--------|------|-------------|
| `POST` | `/api/v1/host-enroll` | Mint-or-reuse a host credential, body `{customer_id}` (auth: `X-Retrieval-Password` header) → `{host_id, api_key}` |
The Day-0 first-boot handshake (`documentation/audits/SPIKE-day0-firstboot-handshake-2026-06-26.md`) enrolls a Proxmox host's agent **using the customer's retrieval passphrase only** — the operator-tier global key never enters the field deploy path. `host-enroll` mints the per-host credential on the first call and **reuses it byte-for-byte** on every later call (idempotent — re-running the host-bootstrap never orphans a running agent's key), backed by `Store.GetHostByCustomer`. Auth is checked **before** any mint, so a wrong passphrase never creates a host row. Behaviour: first → `201`, reuse → `200`, wrong/missing passphrase → `401`, unknown customer → `404`, missing `customer_id` → `400`. **Reinstall (R-1154, v0.159.0):** on a reuse whose old box is gone — its host report AND its controller report both older than `alerting.stale_threshold` (or absent), or the household switched it off on purpose, and no controller wait connection open — the enroll also runs the clean-slate re-issues (claim code, off-site login), once per silence; a live box re-enrolling gets nothing re-issued (`internal/api/reenroll.go`, `05` §8).
The Day-0 first-boot handshake (`documentation/audits/SPIKE-day0-firstboot-handshake-2026-06-26.md`) enrolls a Proxmox host's agent **using the customer's retrieval passphrase only** — the operator-tier global key never enters the field deploy path. `host-enroll` mints the per-host credential on the first call and **reuses it byte-for-byte** on every later call (idempotent — re-running the host-bootstrap never orphans a running agent's key), backed by `Store.GetHostByCustomer`. Auth is checked **before** any mint, so a wrong passphrase never creates a host row. Behaviour: first → `201`, reuse → `200`, wrong/missing passphrase → `401`, unknown customer → `404`, missing `customer_id` → `400`. **Reinstall (R-1154, v0.159.0):** a reuse never re-issues the claim code or the off-site login (operator session-only powers — the passphrase must not reach them; they run on a mint, after the operator deleted the old host). When the old box looks gone — its host report AND its controller report both older than `alerting.stale_threshold` (or absent), or switched off on purpose, and no controller wait connection open — the operator gets a warning mail saying to delete the old host and re-run, or re-issue by hand (`internal/api/reenroll.go`, `05` §8).
The global-key `POST /api/v1/admin/hosts` (operator/HQ pre-mint) remains as the escape hatch pending the enrollment-cutover lock-down; it and `GET /config/{id}` are unchanged by this endpoint.