crafty-controller: changelog + report for healthcheck fix (route un-withheld; 502 backend-scheme tracked separately)
This commit is contained in:
@@ -1,5 +1,25 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-06-26 — crafty-controller healthcheck fix (curl-absent + http-vs-TLS probe)
|
||||
- **crafty-controller**: container was permanently `unhealthy` → route withheld (`routeUnpublished`).
|
||||
Two independent healthcheck root causes, both fixed in one change:
|
||||
- **Docker healthcheck** ran `curl -fk https://localhost:8443`, but the `crafty-4:4.4.8` image has
|
||||
**no `curl` and no `wget`** (`exec: "curl": not found`, FailingStreak 150). Replaced with a
|
||||
dependency-free **python3 TLS-socket** liveness probe (`/usr/bin/python3` is present): completes a
|
||||
TLS handshake to `127.0.0.1:8443` (unverified context mirrors the old `-k`; Crafty's cert is
|
||||
self-signed). `start_period` 30s → 60s for cold-boot headroom (cert gen + migrations).
|
||||
- **Controller-side probe** (`.felhom.yml healthcheck.checks`) was `type: http` against Crafty's
|
||||
**TLS-only** 8443 → `probeHTTP` sent plaintext HTTP, got a TLS record → `HealthProbe.Healthy=false`,
|
||||
which `manager.go` re-applies to override Docker's verdict back to `unhealthy`. Changed `http` →
|
||||
`tcp` (`probeTCP` dial succeeds against a TLS listener). Both layers had to change together.
|
||||
- Live-validated on guest 9201 (`demo-felhom`): synced → recreated via the update path → Docker
|
||||
`State.Health: healthy` (ExitCode 0), `health_probe.healthy: true` (tcp :8443, 5ms), http-vs-TLS
|
||||
WARNs stopped, stable green 3+ min, Traefik now **publishes** the route (`crafty-controller@docker`).
|
||||
- **Known follow-up (separate, out of this fix's scope):** the public URL still returns **502** — a
|
||||
distinct pre-existing bug the un-withheld route exposed: Traefik proxies `http://…:8443` to Crafty's
|
||||
HTTPS-only backend. Needs a Traefik HTTPS-backend + self-signed `serversTransport`
|
||||
(`insecureSkipVerify`) in the controller-generated Traefik config — tracked separately.
|
||||
|
||||
## 2026-06-23 — gokapi: index redirect + admin username display
|
||||
- **gokapi**: seed `RedirectUrl` repointed from Gokapi's GitHub default → `https://${SUBDOMAIN}.${DOMAIN}/admin`.
|
||||
Gokapi's bare root `/` redirects to `RedirectUrl`; the controller's "Megnyitás" link is always the bare
|
||||
|
||||
@@ -1,54 +1,59 @@
|
||||
# REPORT — gokapi reproducible headless setup (fix public "maintenance mode")
|
||||
# REPORT — crafty-controller healthcheck fix
|
||||
|
||||
## Problem
|
||||
`share.demo-felhom.eu` (Gokapi) served **"Server is in maintenance mode, please try again in a few
|
||||
minutes"** on the public URL. Diagnosis: Gokapi was **never configured** — both volumes empty (no
|
||||
`config.json`, 0 data files), the one-time `/setup` wizard never completed. Its docker healthcheck
|
||||
(`200` on :53842) passed even unconfigured, masking it ("healthy" since the Jun-14 deploy). The
|
||||
unconfigured `/setup` was publicly reachable → an **unauthenticated admin-takeover window** (also flagged
|
||||
by the commit security review).
|
||||
**Date:** 2026-06-26 · **Repo:** `app-catalog-felhom.eu` (catalog-only) · **Commit:** `68ce009`
|
||||
**Guest:** 9201 (`demo-felhom`) on `felhom-pve` · **Controller:** v0.82.0 (unchanged)
|
||||
|
||||
## Why the obvious fix didn't work
|
||||
Verified against the Gokapi docs (v1.9.6 **and** v2.0.1) and the v1.9.6 binary: **no Gokapi version
|
||||
supports env-var headless setup** for admin credentials (only `GOKAPI_PORT`). Tested live —
|
||||
`GOKAPI_USERNAME`/`GOKAPI_PASSWORD` were correctly injected and Gokapi ignored them. `--deployment-password`
|
||||
on a fresh config also fails (it requires a pre-existing `config.json`). The only non-interactive path is
|
||||
a **pre-seeded `config.json` + `--deployment-password`**.
|
||||
## Scope
|
||||
Catalog-only fix of the crafty-controller healthcheck (no Go change, no controller/agent rebuild, no
|
||||
version bump). Built on the prior diagnosis (root cause: image has no `curl`).
|
||||
|
||||
## Fix (committed `3ec67ef`)
|
||||
`templates/gokapi/docker-compose.yml` — entrypoint seeds `config.json` on first boot (admin user, public
|
||||
URL `https://${SUBDOMAIN}.${DOMAIN}/`, SQLite, local storage, **Encryption Level 0** so it restarts
|
||||
without a master-password prompt) with `Password`/`SaltAdmin`/`SaltFiles` cleared, then runs
|
||||
`/app/run.sh --deployment-password "$GOKAPI_PASSWORD"` (one-shot; sets the password + regenerates salts)
|
||||
**before** `exec /app/run.sh`. Gated on `config.json` absence (first boot only). `.felhom.yml` —
|
||||
`GOKAPI_PASSWORD` deploy field (`type: password`, `generate: password:24`, shown, locked). Seed pinned to
|
||||
Gokapi **v1.9.6 / ConfigVersion 21**.
|
||||
## Files changed (2)
|
||||
- `templates/crafty-controller/docker-compose.yml` — Docker healthcheck `test` `curl` → python3 TLS
|
||||
socket probe; `start_period` 30s → 60s.
|
||||
- `templates/crafty-controller/.felhom.yml` — controller-side probe `type: http` → `type: tcp` (8443
|
||||
is TLS-only).
|
||||
|
||||
Security: the admin account is claimed during first boot, before the webserver serves → the public
|
||||
`/setup` admin-takeover window is closed (addresses the security-review finding; the suggested env-creds
|
||||
fix is impossible on this version, so this seed mechanism is the equivalent that actually works).
|
||||
Effective new Docker healthcheck:
|
||||
```
|
||||
["CMD","/usr/bin/python3","-c","import socket,ssl; ssl._create_unverified_context().wrap_socket(socket.create_connection(('127.0.0.1',8443),timeout=3)).close()"]
|
||||
interval 30s · timeout 5s · retries 3 · start_period 60s
|
||||
```
|
||||
|
||||
## Validation (live, guest 9201)
|
||||
1. Discovered the env approach fails (live test); reverted it.
|
||||
2. Captured a valid `config.json` by completing `/setup` once in-browser; templated it (cleared
|
||||
salts/password, templated URL).
|
||||
3. Validated seed + `--deployment-password` two-phase entrypoint on throwaway containers.
|
||||
4. Committed the catalog change, synced, **removed + redeployed gokapi fresh** → logs showed
|
||||
`[felhom] first boot — seeding Gokapi config and claiming admin account` → `New password has been set
|
||||
successfully` → healthy; `config.json` = admin + real URL.
|
||||
5. **Browser end-to-end:** `https://share.demo-felhom.eu/` no longer maintenance (redirects per config);
|
||||
`/login` with `admin` + the generated password → Gokapi admin panel. Logged out after.
|
||||
## Two root causes (both required)
|
||||
1. **Docker probe** used `curl`, absent in `crafty-4:4.4.8` (`exec: "curl": not found`, FailingStreak 150).
|
||||
2. **Controller-side probe** did plaintext `http://…:8443` against a TLS port → `HealthProbe.Healthy=false`
|
||||
→ `manager.go:471` overrides `running` back to `unhealthy`. Fixing only #1 would have flipped back at
|
||||
the next controller probe. Verified empirically.
|
||||
|
||||
Final demo: 25 containers, 0 unhealthy; gokapi healthy. Admin credentials stored out-of-band.
|
||||
## Deploy path (real flow)
|
||||
1. Commit + push to `main` (`68ce009`).
|
||||
2. Controller git-sync via `POST /api/sync` (Host: felhom.demo-felhom.eu) → `[sync] Updated
|
||||
crafty-controller/docker-compose.yml` + `.felhom.yml`; `app.yaml` untouched.
|
||||
3. Recreate via the UI update path `POST /api/stacks/crafty-controller/update` (`docker compose up -d`;
|
||||
restart would not pick up a healthcheck change).
|
||||
|
||||
## Follow-ups applied
|
||||
- Gokapi's `RedirectUrl` repointed from the upstream default (Gokapi's GitHub) → `https://${SUBDOMAIN}.${DOMAIN}/admin`,
|
||||
so the bare root and the controller's "Megnyitás" link reach the Gokapi login instead of GitHub
|
||||
(applied to live + seed; verified in-browser: `/` → `/admin` → login).
|
||||
- `app_info.default_creds` added (shows admin username on the app-info page); `first_steps` updated for
|
||||
the headless flow.
|
||||
## Verification (acceptance gate)
|
||||
- **New healthcheck live:** `docker inspect .Config.Healthcheck` = python3 cmd, `StartPeriod` 60s.
|
||||
- **Executes + passes:** `State.Health` → `Status: healthy`, `FailingStreak: 0`, Log `ExitCode: 0`.
|
||||
- **Controller-side probe clear:** `GET /api/stacks/crafty-controller` → `health_probe.healthy: true`,
|
||||
`details: [{type: tcp, target: :8443, healthy: true, latency: 5ms}]`; http-vs-TLS `[WARN] Health probe
|
||||
crafty-controller …` entries stopped at the 07:46 sync, none after.
|
||||
- **Green + stable:** `Up 3 minutes (healthy)`, state `running` across a probe cycle (override gone).
|
||||
- **Route un-withheld:** Traefik now publishes `crafty-controller@docker` (in its access log) — the
|
||||
`routeUnpublished` / "URL nem elérhető" condition is resolved.
|
||||
|
||||
## Known minor / optional
|
||||
- Seed is tied to Gokapi v1.9.6's `config.json` schema — re-capture if the image is bumped.
|
||||
- The controller's "Megnyitás" link is always the bare subdomain root (no per-app path support); apps
|
||||
whose UI isn't at `/` are handled by pointing their index redirect at the right path (as done here).
|
||||
## Open item (separate task — NOT this fix)
|
||||
Public URL `https://minecraft.demo-felhom.eu` still returns **502**. Distinct, pre-existing bug exposed
|
||||
by the now-published route: Traefik proxies `http://172.18.0.6:8443` to Crafty's **HTTPS-only**
|
||||
self-signed backend (access log: `502 … "crafty-controller@docker" "http://172.18.0.6:8443"`). Crafty
|
||||
serves no plain-HTTP port (only 8443 listens). A working fix needs Traefik HTTPS backend
|
||||
(`scheme=https`) **plus** a `serversTransport` with `insecureSkipVerify` for the self-signed cert —
|
||||
the latter cannot be set via Docker labels in Traefik v3, so it requires the controller-generated
|
||||
Traefik config / felhom-controller Go (out of this catalog-only task). Investigation of fix options is
|
||||
in progress per request; plan to follow before any implementation.
|
||||
|
||||
## Observations (documented, not acted on)
|
||||
1. `probeHTTP` can only target `http://` backends — no TLS support. A general fix would teach it
|
||||
`https`/honor a `scheme` field so future TLS apps can use `type: http`/`api` checks. Own task.
|
||||
2. Catalog-wide audit: other apps whose Docker healthcheck uses a possibly-absent binary
|
||||
(`curl`/`wget`), or whose `.felhom.yml` uses `type: http` against a TLS port. Own task.
|
||||
|
||||
Reference in New Issue
Block a user