docs(spec): installer overhaul (Docker install, clusev user, DNS check) + themed MOTD
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>feat/v1-foundation
parent
02741539ca
commit
c54932b532
|
|
@ -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://<domain>` or `http://<server-ip>`), 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.
|
||||
Loading…
Reference in New Issue