From b1fb0e766a4c3832cfc99c7eba720faf155a591e Mon Sep 17 00:00:00 2001 From: boban Date: Fri, 19 Jun 2026 16:54:03 +0200 Subject: [PATCH] feat(install): fixed default admin e-mail admin@clusev.local Default the first-admin login e-mail to a predictable admin@clusev.local when no --email / CLUSEV_ADMIN_EMAIL is given, instead of a host-derived admin@ address the operator would have to guess. Combined with the default password 'clusev' (forced change on first login), a fresh install now has a fully predictable initial login. Override at install time or change it later under Settings -> Profile. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 16 +++++++++------- app/Console/Commands/Install.php | 11 +++++++++-- install.sh | 2 +- tests/Feature/InstallCommandTest.php | 10 ++++++++++ 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index d74facc..8a33646 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,9 @@ The installer is idempotent (safe to re-run) and, in one pass: 1. Installs **Docker** (Debian/Ubuntu, from Docker's official repository) if it isn't already present. 2. Creates a dedicated **`clusev`** system user — in the `docker` group, owning and running the stack — with a random password. -3. Asks for a **domain** (leave empty for IP access) and an **admin e-mail**, or reads `CLUSEV_DOMAIN` - / `CLUSEV_ADMIN_EMAIL` from the environment for an unattended install. If you give a domain it +3. Asks for a **domain** (leave empty for IP access) and an **admin e-mail** (leave empty for the + default **`admin@clusev.local`**), or reads `CLUSEV_DOMAIN` / `CLUSEV_ADMIN_EMAIL` from the + environment for an unattended install. If you give a domain it checks whether DNS already points here: if so a certificate is obtained automatically; if not, it warns you and lets you take the domain anyway (HTTP until DNS is correct) or continue on the IP. 4. Generates all secrets (once — never regenerated on re-run), builds the image, starts the stack, @@ -54,12 +55,13 @@ The installer is idempotent (safe to re-run) and, in one pass: 5. Installs a themed host login banner (MOTD) showing the dashboard address and live stack status. When it finishes, the terminal prints a single summary: the **dashboard URL**, the **admin login** -(your e-mail + the default password **`clusev`**), and the **`clusev` host user** + its random -password (shown **only once** — note it down). +(your e-mail — `admin@clusev.local` if you left it empty — plus the default password **`clusev`**), +and the **`clusev` host user** + its random password (shown **only once** — note it down). -Log in with your admin e-mail and the password **`clusev`** — the panel then **forces you to set a -new password immediately**. Do this right away: `clusev` is a known default, so the account is only -safe once you've changed it. 2FA is **optional but recommended** — enable TOTP and/or a security key +Log in with your admin e-mail (default **`admin@clusev.local`**) and the password **`clusev`** — the +panel then **forces you to set a new password immediately**. Do this right away: `clusev` is a known +default, so the account is only safe once you've changed it. You can change the login e-mail later +under **Settings → Profile**. 2FA is **optional but recommended** — enable TOTP and/or a security key from **Settings → Security** whenever you like. ## Access, domain & TLS diff --git a/app/Console/Commands/Install.php b/app/Console/Commands/Install.php index db8894f..efc64d7 100644 --- a/app/Console/Commands/Install.php +++ b/app/Console/Commands/Install.php @@ -20,6 +20,14 @@ class Install extends Command /** Standard default admin password — forced to change on first login (must_change_password). */ private const DEFAULT_PASSWORD = 'clusev'; + /** + * Standard default admin e-mail (login + Let's-Encrypt contact) when none is given. A fixed, + * predictable value so a fresh, unattended install always logs in at admin@clusev.local — + * not a host-derived address the operator would have to guess. Override with --email / + * CLUSEV_ADMIN_EMAIL, or change it later under Settings → Profile. + */ + private const DEFAULT_EMAIL = 'admin@clusev.local'; + protected $signature = "clusev:install {--email= : Admin-Login + Let's-Encrypt-E-Mail} {--name=Administrator : Anzeigename des Admins}"; @@ -34,8 +42,7 @@ class Install extends Command return self::SUCCESS; } - $host = parse_url((string) config('app.url'), PHP_URL_HOST) ?: 'localhost'; - $email = (string) ($this->option('email') ?: "admin@{$host}"); + $email = (string) ($this->option('email') ?: self::DEFAULT_EMAIL); if (! filter_var($email, FILTER_VALIDATE_EMAIL)) { $this->error("Ungueltige Admin-E-Mail: {$email}"); diff --git a/install.sh b/install.sh index fdc653c..c6ae311 100755 --- a/install.sh +++ b/install.sh @@ -117,7 +117,7 @@ if [ -t 0 ]; then printf '%s' "HTTP-Port (Standard 80): "; read -r in_port || true [ -n "${in_port:-}" ] && HTTP_PORT="$in_port" fi - printf '%s' "Admin E-Mail (Login + Let's Encrypt): "; read -r in_email || true + printf '%s' "Admin E-Mail (leer = admin@clusev.local): "; read -r in_email || true [ -n "${in_email:-}" ] && ADMIN_EMAIL="$in_email" fi diff --git a/tests/Feature/InstallCommandTest.php b/tests/Feature/InstallCommandTest.php index a81c7be..01095e5 100644 --- a/tests/Feature/InstallCommandTest.php +++ b/tests/Feature/InstallCommandTest.php @@ -26,6 +26,16 @@ class InstallCommandTest extends TestCase $this->assertTrue((bool) $user->must_change_password, 'first login must be forced to change it'); } + public function test_uses_the_fixed_default_email_when_none_is_given(): void + { + $this->artisan('clusev:install')->assertSuccessful(); + + $user = User::sole(); + $this->assertSame('admin@clusev.local', $user->email, 'a fresh install must default to the predictable admin@clusev.local'); + $this->assertTrue(Hash::check('clusev', $user->password)); + $this->assertTrue((bool) $user->must_change_password); + } + public function test_is_a_noop_when_an_admin_already_exists(): void { User::factory()->create();