From c54932b532a0e525952a0544c378ba2349929130 Mon Sep 17 00:00:00 2001 From: boban Date: Mon, 15 Jun 2026 19:07:52 +0200 Subject: [PATCH] docs(spec): installer overhaul (Docker install, clusev user, DNS check) + themed MOTD Co-Authored-By: Claude Opus 4.8 (1M context) --- .../2026-06-15-installer-overhaul-design.md | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-15-installer-overhaul-design.md diff --git a/docs/superpowers/specs/2026-06-15-installer-overhaul-design.md b/docs/superpowers/specs/2026-06-15-installer-overhaul-design.md new file mode 100644 index 0000000..d92953f --- /dev/null +++ b/docs/superpowers/specs/2026-06-15-installer-overhaul-design.md @@ -0,0 +1,81 @@ +# Installer overhaul + themed MOTD — Design + +**Date:** 2026-06-15 · **Branch:** `feat/v1-foundation` · **Status:** approved · Beta-prep #4 (+ #5 MOTD) + +Make `install.sh` a true one-command bootstrap on a fresh Debian/Ubuntu host: install Docker, create +a dedicated `clusev` user, check the domain's DNS, bring the stack up, and end with a clean banner +(dedicated-user creds + admin creds + dashboard URL only). Also install a product-themed MOTD shown at +every host login. Builds on the existing 7-phase idempotent installer (secrets, stack, migrate, admin, +sentinel) — extend, don't rewrite. + +## Operator-approved decisions +- **Docker auto-install:** Debian/Ubuntu (apt, official Docker repo) only; other OS → clear message + manual link. +- **Dedicated user:** login user `clusev` with a random password, in the `docker` group, owns the install dir. +- **Run mode:** `sudo ./install.sh` (root required for Docker install + user creation). +- **Domain:** DNS-check → if it resolves to this server, proceed with TLS; else warn + offer "take the domain anyway (HTTP until DNS points here)" or "continue on the IP". + +## Flow (extends the current phases) +**Phase 0 — root + OS.** Require root (`[ "$(id -u)" = 0 ]` else `die "Bitte mit sudo ausführen: sudo ./install.sh"`). +Read `/etc/os-release`; set `OS_FAMILY` from `ID`/`ID_LIKE` (debian|ubuntu → apt). Non-apt + Docker missing → die with a manual-install hint. + +**Phase 1 — preflight + Docker.** If `docker` + `docker compose` present → skip. Else on apt: +install via the official repo — `install -m0755 -d /etc/apt/keyrings`, fetch +`https://download.docker.com/linux/$ID/gpg` → keyring, add the `deb [signed-by=…]` repo, `apt-get update`, +`apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin`, +`systemctl enable --now docker`. Verify `docker compose version` after. Keep `openssl` check. + +**Phase 2 — dedicated `clusev` user.** If `id clusev` fails → `useradd -m -s /bin/bash clusev`, set a +random password (`CLUSEV_USER_PW=$(openssl rand -base64 18)`, `chpasswd`), `usermod -aG docker clusev`. +Always: `chown -R clusev:clusev` the install dir. Set `HOST_UID`/`HOST_GID` in `.env` to clusev's +uid/gid (so containers run as clusev). Idempotent: existing user kept, password NOT regenerated (only +shown if freshly created — track `USER_CREATED=1`). + +**Phase 3 — inputs + domain DNS check.** Keep the interactive domain/email prompts (+ env + bare-IP +fallback). When a domain is given: resolve it (`getent ahosts "$domain" | awk '{print $1}' | sort -u`) +and get the server's public IP (`curl -fsS https://api.ipify.org` with a `hostname -I` fallback). If the +domain's A/AAAA set contains the server IP → `DOMAIN_OK=1`, proceed with TLS. Else warn with the +resolved-vs-server IPs and offer (interactive): [1] take the domain anyway — "Cert kommt automatisch, +sobald DNS hierher zeigt; bis dahin nur HTTP", [2] continue on the IP (clear the domain). Non-interactive +default: take the domain anyway (on-demand TLS will issue once DNS is correct) — but print the warning. + +**Phases 4-8 — unchanged in spirit:** `.env`/secrets (idempotent), session hardening, build image, start +stack, restart-sentinel units (render `User=clusev` + the real path now), wait for DB, migrate + caches, +first admin (one-time random password). The sentinel `chown`/unit-user becomes `clusev`. + +**Phase 9 — MOTD (beta-prep #5).** Install a themed dynamic MOTD: write an executable +`/etc/update-motd.d/00-clusev` (Debian/Ubuntu run-parts MOTD) that prints the Clusev wordmark in the +product's signal-orange ANSI (256-color `\033[38;5;208m`), a hairline, and the live access line +(`https://` or `http://`), plus "verwaltet vom Benutzer clusev · docker compose -f +docker-compose.prod.yml ps". Ship the template under `docker/motd/00-clusev` and `install`-copy it +(chmod +x), substituting the URL. Idempotent (overwrite our file only; never touch other MOTD parts). +On non-Debian (no update-motd.d) write `/etc/motd` static as a fallback. + +**Closing banner.** Print ONLY (signal-orange, no phase noise): "Installation erfolgreich." then a boxed +summary — Dashboard URL/IP · Admin-Login (email + the one-time admin password) · (if freshly created) +the `clusev` host user + its one-time password · "Diese Passwörter werden nur jetzt angezeigt." No other +output after. Secrets appear ONLY in this banner (never logged/echoed elsewhere). + +## Files +- `install.sh` — phases 0–3 + 9 added, sentinel rendering → `clusev`, closing banner reworked. +- `docker/motd/00-clusev` (NEW) — the themed MOTD template (with a `__CLUSEV_URL__` placeholder). +- `.env` writes — `HOST_UID`/`HOST_GID` = clusev's; existing keys unchanged. +- (README documents all of this — beta-prep #3, separate.) + +## Security +- Docker installed from the official GPG-verified apt repo (no `curl|bash get.docker.com`). +- `clusev` is in the `docker` group (docker = root-equivalent) — this IS the dedicated admin user, by design; documented. +- Random passwords via `openssl rand`, shown once in the closing banner, never logged. +- Root is required up-front and clearly stated; the script stays idempotent + `set -euo pipefail`. +- DNS check uses the resolved A/AAAA vs the server IP; a mismatch never silently enables a wrong-host cert (on-demand TLS already gates issuance to the configured domain). + +## Testing +- `shellcheck install.sh` + `docker/motd/00-clusev` clean (no errors). +- The MOTD script renders the banner with a substituted URL (run it locally, check ANSI + the URL line). +- Idempotency: re-running on an already-installed host must not regenerate secrets/passwords or recreate + the user (dry-reason: the closing banner shows the freshly-created creds only on first run). +- A full root install can only be validated on a fresh Debian/Ubuntu VM (out of CI) — the operator runs + it there; the build verifies via shellcheck + a structured read-through + the MOTD render. + +## Out of scope +- #3 professional README (documents this), #2 de-Claude history rewrite (last). Non-apt Docker + auto-install (RHEL/dnf). Unattended/cloud-init variants.