58 lines
4.0 KiB
Markdown
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 `&`→`&`, 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.
|