Files
felhom-controller/REPORT.md
T

3.5 KiB

REPORT — v0.122.0: customer-claim password gate (closes DRILL-day0-vm F-4/F-5)

Date: 2026-07-12 · Version: controller v0.122.0 (from v0.121.0) · MinAgent: 0.81.0 (UNCHANGED) · Requires: hub v0.50.0

What shipped

The dashboard password is now customer-owned, set through a one-time claim code the hub emails to the registered address. The old "no password → open dashboard" default is gone: a fresh box (claim-code hash delivered, no password) serves ONLY the claim page — every other route answers the claim page (302 → /claim) or 401 (API). This closes the DRILL-day0-vm F-4 exposure (every Day-0 dashboard was open on the internet, with the full deploy API = RCE-equivalent) and F-5 (the unauthenticated geo toggle is now behind the gate too).

  • internal/web/claim.goclaimGateActive() / effectiveClaimCode() (ACK-cached settings beats the config bake by generation), the claim page (GET /claim), submit (POST /claim: verify code → set own password via SetPasswordHash → mark claimed → consume generation → session), and POST /claim/request-new-code (forwards to the hub reset-request). Code checks: bcrypt match AND generation-not-consumed (single-use) AND ≤ 72 h. Per-source + global limiter (5 → 15-min lockout) raising the allowlisted claim_lockout event. Pre-auth CSRF = HMAC over web.session_secret (fixes CTRL-007). Min password 12.
  • Gate wiring (auth.go/csrf.go/server.go/cmd): gate atop RequireAuth; a SET password disables it (auth wins — claimed boxes never regress). /claim* + /static/* reachable pre-auth. Legacy-open (no password, no hash) → red transition banner (layout.html). Login "Elfelejtett jelszó" link.
  • internal/report/claim_sync.go — caches the ACK claim {hash, generation} into settings.json idempotently by generation (one-way: newer advances; same/older/nil never rewrites; hub outage never clears). Report carries claimed (set-only). Hub bakes web.claim_code_* for gate-from- first-boot.
  • --print-reset-code root escape hatch (one-time local code above cached/baked/consumed gen).

Tests + red-proofs

  • Gate-coverage signature test (TestClaimGate_EveryRouteGated): every route → claim page/401, a deploy POST reaches no handler. Plus happy-path / reuse-refused / expired / lockout+window-reopen (fake clock), legacy-open vs password-set, claim-sync idempotence.
  • Four §10 red-proofs proven (mutate → FAIL → revert → green): (1) gate skip-line, (2) single-use generation — hub RotateClaimCode bump AND controller consumed-generation check, (3) reset non-DoS (clearing claimed → fail), (4) rate-limiter (both scopes disabled → fail).
  • Green gate: go build ./... && go vet ./... clean; all touched packages pass. Pre-existing env-sensitive internal/backup volume-dump failures are unrelated (fail identically on the clean baseline — no tar/zstd toolchain on the dev box).

Live validation (drill guest 9201 on qm 300, controller 0.122.0, pre-floor)

Deployed 0.122.0 → the ACK delivered the claim hash → gate flipped ON within a report cycle. Proven via the REAL Cloudflare edge (curl --resolve): / → 302 → "A szerver beállítása" claim page; /claim → 200; /api/host-metrics → 401. Controller log: claim-sync: hub claim code cached (generation 1). Hub log: claim code emailed to the registered address of demo-vm-felhom. The customer-side claim + reset + the floor raise are the operator's supervised steps.