@
hub v0.54.0: change operator login password from the Configuration UI Adds a "Login password" card on /configuration. The password was previously settable only via the hub-config ConfigMap (auth.password_hash) + redeploy. - store: hub_settings key operator_password_hash + Get/SetOperatorPasswordHash - server: passwordHash field -> configPasswordHash (seed); new effectivePasswordHash() (DB override wins, else seed) is now the single source for the CSRF gate, RequireAuth, and handleLogin - POST /configuration/password (handleChangePassword): requires current password, 8-72 byte new + confirm, bcrypt cost 10, persists DB override; existing sessions kept valid; ConfigMap stays the break-glass reset path - UI: current/new/confirm form + inline mismatch pre-check + 6 flashes - tests + red-proofs: override precedence, happy-path via handleLogin, wrong-current rejection, mismatch/too-short/no-op, template render - docs: CHANGELOG, README (auth+config), REUSE, REPORT Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LbMm4T7Ayzs1unB9pN6Uqd @
This commit is contained in:
@@ -2,26 +2,58 @@
|
||||
|
||||
> **Overwrite** this file with a summary of the most recent task only (uniform with the other repos; not cumulative). The cumulative hub history lives in [hub/CHANGELOG.md](hub/CHANGELOG.md); the scripts history lives in [scripts/CHANGELOG.md](scripts/CHANGELOG.md).
|
||||
|
||||
## Controller-driven escrow ceremony — felhom.eu leg (installer + docs) — 2026-07-13
|
||||
## Hub v0.54.0 — operator login password changeable from the UI — 2026-07-13
|
||||
|
||||
Companion commits to felhom-agent **v0.88.0** (`1c3a3ef`) + felhom-controller **v0.127.0**
|
||||
(`08a966b`) — the customer-facing recovery-code wizard. felhom.eu commit `375cb08`.
|
||||
### What & why
|
||||
|
||||
- **host-install v1.16.0:** the `FELHOM_ESCROW` sudoers alias ships via the EXISTING canonical
|
||||
sudoers fetch (no new step; header documents it). Hub `hostInstallVersion` synced to 1.16.0 in
|
||||
the same commit (`hostinstall_gates.py` green; hub green gate run; **no hub deploy** — the
|
||||
const rides the next hub train, display-only lag).
|
||||
- **RUNBOOK-escrow-ceremony.md rewritten:** the controller wizard is the PRIMARY path; the CLI is
|
||||
the operator fallback (text mode unchanged); **F1 threat-model paragraph** (R transits the CF
|
||||
tunnel once at reveal — accepted 2026-07-13, same trust class as claim code/login password;
|
||||
agent→controller leg never leaves the box; LAN-direct delivery PARKED); stale-blob warning +
|
||||
supersede/void semantics documented; CLI staged-secret rule spelled out (a CLI run without the
|
||||
staged secret mints a hash-less blob → the new Scenario-F warning).
|
||||
- Deploy + live validation evidence: felhom-agent/REPORT.md + felhom-controller/REPORT.md
|
||||
(agents 0.88.0 on demo host + drill VM, 63/63 capabilities; controllers 0.127.0 on both guests;
|
||||
Scenario F fired live on BOTH boxes' hash-less blobs; the drill blob repaired —
|
||||
`restic_pw_sha256` now covers the local password; one-shot claim + 410 proven endpoint-exact).
|
||||
- **Operator follow-ups:** (1) one supervised wizard pass with Viktor's drill login (the full
|
||||
browser leg incl. re-auth + reveal — CC cannot type the customer-owned password), ideally also
|
||||
on the demo box to clear ITS legacy stale warning; (2) publish agent 0.88.0 + vouch in the
|
||||
Day-0 manifest at the next publish train (deployed hosts got direct deploys).
|
||||
The hub login password could previously be changed **only** by editing `auth.password_hash` in the
|
||||
`hub-config` ConfigMap and redeploying — no in-app path existed (operator hit this wall). Added a
|
||||
**Configuration → Login password** card that changes the password at runtime, persisted in the DB,
|
||||
with the ConfigMap kept as the break-glass reset path.
|
||||
|
||||
### Design (matches the controller-version-floor precedence pattern)
|
||||
|
||||
- **Store** (`internal/store/store.go`): new `hub_settings` key `operator_password_hash` with
|
||||
`GetOperatorPasswordHash()` / `SetOperatorPasswordHash()` (thin wrappers over the existing
|
||||
`getSetting`/`setSetting`). No schema change.
|
||||
- **Server** (`internal/web/server.go`): the static `Server.passwordHash` field is renamed
|
||||
`configPasswordHash` (the hub.yaml SEED). New `effectivePasswordHash()` = **DB override wins, else
|
||||
config seed** — and it is now the single source for every auth check (CSRF gate, `RequireAuth`
|
||||
session + Basic-Auth paths, `handleLogin`).
|
||||
- **Handler** `POST /configuration/password` (`handleChangePassword`): requires the current password
|
||||
(verified against the effective hash), new password 8–72 bytes, matching confirmation, rejects a
|
||||
no-op. On success bcrypts (cost 10) and persists the override. Existing sessions stay valid; CSRF
|
||||
enforced by the central `ServeHTTP` gate; no secret logged.
|
||||
- **UI** (`templates/configuration.html`): current/new/confirm fields, inline client-side mismatch
|
||||
pre-check, six flash outcomes.
|
||||
|
||||
### Recovery posture (operator's explicit choices)
|
||||
|
||||
- Requires the **current** password to change it (blocks a walk-up attacker on an open session).
|
||||
- ConfigMap `auth.password_hash` remains the **break-glass fallback** — blank the DB row (or edit the
|
||||
manifest + redeploy) to reset a forgotten password.
|
||||
|
||||
### Tests & red-proofs (`internal/web/change_password_test.go`)
|
||||
|
||||
- `TestEffectivePasswordHash_DBOverrideWins` — override wins; clearing falls back to the seed.
|
||||
- `TestChangePassword_HappyPath` — end-to-end through `handleLogin` (new works, old dead).
|
||||
- `TestChangePassword_WrongCurrentRejected` — security anchor: no override written.
|
||||
- `TestChangePassword_ValidationRejections` — mismatch / too-short / no-op refused, no override.
|
||||
- `TestConfigurationPage_RendersPasswordCard` — form renders through the production template.
|
||||
- Red-proofs verified: dropping the current-password check → WrongCurrentRejected fails; breaking the
|
||||
override precedence → precedence + happy-path login assertions fail.
|
||||
|
||||
### Gates
|
||||
|
||||
- `go build ./... && go vet ./... && go test ./...` (hub): green.
|
||||
- `python scripts/hub_confirm_gate.py`: green (no native confirm/prompt in templates).
|
||||
|
||||
### Docs
|
||||
|
||||
- `hub/CHANGELOG.md` (v0.54.0), `hub/README.md` (Authentication + Configuration sections),
|
||||
`REUSE.md` (`effectivePasswordHash`, `Get/SetOperatorPasswordHash` rows).
|
||||
|
||||
### Deploy
|
||||
|
||||
Hub image built + pushed as `v0.54.0`; `manifests/hub.yaml` tag bumped; ArgoCD synced; verified live
|
||||
on `hub.felhom.eu`.
|
||||
|
||||
Reference in New Issue
Block a user