Files
felhom-controller/REPORT.md
T

58 lines
4.0 KiB
Markdown

# REPORT — Catalog-driven initial-login extraction (show an app's auto-generated password)
**Repo:** `felhom-controller` · **Version:** `v0.83.0`**`v0.84.0`** · **Date:** 2026-06-26
**Commit:** `1705d71` · **Paired catalog commit:** `app-catalog-felhom.eu` `8fd9c00`
**Deployed:** guest 9201 (`demo-felhom`) on `felhom-pve`, golden/bootstrap mechanism.
## Problem
Some apps generate a random first-login password into a file at first boot instead of taking it from a
deploy field — Crafty writes one to `/crafty/app/config/default-creds.txt` (its built-in default is
rejected as "too short"). Customers had to read the container logs to find it; the static
`app_info.default_creds` hint can't carry a per-install secret.
## Fix — a general, catalog-driven mechanism (Crafty is the first consumer)
- **`.felhom.yml`** gains an optional `initial_credentials` block:
`{file, format: json|regex|plain, container?, username_key/password_key (json),
username_pattern/password_pattern (regex), note}`.
- **`internal/stacks/metadata.go`** — new `InitialCredentials` struct + `Metadata.InitialCreds`
(deep-copied in `deepCopyStack`).
- **`internal/stacks/initialcreds.go`** — `ReadInitialCredentials(stack)` reads the file **live** from
the running container (`docker exec <c> cat <file>`; path passed as a single arg, no shell) and parses
it via the pure, unit-tested `parseInitialCreds` (json/regex/plain). Container defaults to the stack's
main container (`findProbeContainer`). Returns a non-Available result (card hidden) when the container
is down / file missing / parse fails. **Never persists the secret** to `app.yaml` — read on demand.
- **`internal/web/handlers.go`** — `appDetailHandler` populates `InitialCreds` for deployed apps with a
spec; **`app_info.html`** renders a "Kezdeti belépési adatok" card: username + masked password with
Megjelenítés/Másolás (value read from a hidden element — never inlined into a JS literal), clearly
labelled as the **initial** password (valid only until the customer changes it in-app).
- **Tests:** `parseInitialCreds` json (+ default key) / regex (+ missing-pattern) / plain / unknown-format.
`go build`, `go vet`, `go test ./internal/stacks/...` all green.
## Catalog (`8fd9c00`)
crafty-controller `.felhom.yml` gains the `initial_credentials` block (json, username/password keys, a
note) and `first_steps` now points at the app page instead of "find it in the logs".
## Security posture
This surfaces a **live working credential** on the app page — same exposure class as the existing
post-deploy password reveal and the `default_creds` card, and rendered with the same mask/reveal UX. It
relies on the dashboard being **auth-gated in production**; the demo's public-unauth dashboard is a
separate, pre-existing tracked issue (not introduced here). The secret is never written to controller
state — only read live from the volume where it already lives.
## Verification (live, guest 9201, controller v0.84.0)
| Check | Result |
|---|---|
| Spec synced + parsed | on-disk `.felhom.yml` carries `initial_credentials`; `/api/stacks/crafty-controller` meta shows it |
| Card renders | `/apps/crafty-controller` HTML contains the "Kezdeti belépési adatok" card |
| Username | `initcred-user` cell renders `admin` |
| Password (live-extracted) | hidden value element carries the exact password from the container's `default-creds.txt` (HTML-escaped `&``&amp;`, decoded back by JS `textContent`) |
| Controls | `Felhasználónév` label + `icRevealPw`/`icCopyPw` reveal/copy wired |
**Method:** fetched the exact HTML the browser receives via the in-guest API (Host: felhom.demo-felhom.eu).
The reveal/copy is client-side JS — wiring confirmed in markup, not browser-clicked this run.
## Observation (carry, not acted on)
The whole-dashboard public-unauth exposure on the demo remains the right place to fix credential
visibility broadly — tracked separately; this feature should not be read as endorsing an unauthenticated
dashboard.