SEC-1c-1c Plan-Review — SMTP + Filament Password-Reset

Projekt: Steve-TradingBot · Phase: SEC-1c-1c (parallel zu SEC-1c-1b) · Author: claude-opus-4-7[1m]
Generated: 2026-05-13 20:22 UTC · master HEAD: 2fbc7b5
Status: NO CODE Plan-Review only — Operator-GO erforderlich vor Implementation.
Empfehlung: webgo SMTP via STARTTLS 587 + Filament-native passwordReset() + Admin-Email-Update + SPF/DKIM-DNS

SECRET-HYGIENE-PIN: SMTP-Username + SMTP-Passwort wurden im Conversation-Transcript bereitgestellt (Operator-Risk-Acceptance, gleiche Logik wie Telegram/OpenAI/Anthropic-Tokens vom 2026-05-13). Werte landen ausschließlich in gui/.env mode 600 in der Implementation-Phase — niemals in diesem PDF, in Memory-Pins, in Commit-Messages oder in nachfolgenden Chat-Antworten. Rotation der SMTP-Credentials wird Teil von SEC-1e Final Token-Rotation.

0 — Executive Summary

Ziel: aktivieren der Filament-eingebauten Password-Reset-Flow, sodass Operator kein manuelles tinker-Reset mehr braucht und PWs niemals den Server verlassen (Self-Service via E-Mail-Token).

Voraussetzungs-Chain: (a) funktionierender Laravel-Mailer, (b) echte Admin-E-Mail-Adresse, (c) Filament-Panel mit ->passwordReset(), (d) DNS-Records (SPF, optional DKIM) für Mail-Deliverability.

Live-Findings:

SMTP-Provider: s137.goserver.host (webgo-Hosting-Infrastruktur, da Operator-Domain bei webgo liegt). Credentials sind operator-bereitgestellt. Setup ist Standard-SMTP-AUTH.


1 — SMTP-Provider Analyse (webgo / goserver.host)

ParameterWertEmpfehlung
SMTP-Hosts137.goserver.hostdirekt verwenden
Port (Optionen)465 SSL/TLS oder 587 STARTTLS587 STARTTLS (Standard für Submission, besser firewall-kompatibel; UFW erlaubt outgoing nach default-allow)
EncryptionTLS (STARTTLS auf 587)MAIL_ENCRYPTION=tls in Laravel
AuthUSER + PASSWORDMAIL_USERNAME=<webgo-account> + MAIL_PASSWORD=<> — nur in gui/.env mode 600
Inbound (MX)nicht nötigwir senden nur outbound; Reset-Token-Mail landet beim Operator (talk@kw-baustoffe.de o.ä.)
Reverse-DNS / EHLOnginx-Host vs SMTP-EHLOwebgo kümmert sich (relay-server); kein Setup-Bedarf auf unserer Seite
Connection-Test (geplant in Step 1.6)php artisan tinker; Mail::raw('SMTP-test',...);vor Filament-Aktivierung verifizieren

Warum 587 STARTTLS über 465 SSL/TLS?


2 — Vorbedingung: Admin-E-Mail-Update

Aktuell: users.id=23, email='admin@example.local' — passt zu keiner echten Inbox.

Wahl der neuen E-Mail-Adresse

OptionAdresseProContra
(a) Operator-Inboxtalk@kw-baustoffe.dereal existierende Inbox; kontrolliert vom Operator; Reset-Mails landen dortFrom-Address & To-Address sind unterschiedliche Domains
(b) eigene Domain-Inboxadmin@gewerbespeicher-rechner.deeinheitliche Brand-Domainsetzt Mailbox bei webgo voraus — Operator-Setup-Aufwand
(c) bestehende webgo-Mailboxunbekanntnutzt bestehenden AccountOperator müsste Mailbox-Adresse mitteilen

Empfehlung: (a) — pragmatisch, Operator-Inbox aus dem System-Prompt bekannt, keine zusätzliche Setup-Arbeit.

SQL-Update

BEGIN;
UPDATE users
   SET email = '<new-email>',
       updated_at = NOW()
 WHERE id = 23
   AND email = 'admin@example.local';
-- 1 row updated expected
SELECT id, email, role FROM users WHERE id = 23;
COMMIT;

Boundary: kein Schema-Change, nur 1-row data-update. pg_dump vorher (durable rule).


3 — Filament Password-Reset Architektur

Vorhandene Bausteine (in vendor/)

ClassFunktion
Filament\Auth\Pages\PasswordReset\RequestPasswordResetPublic-Page: Email-Eingabe-Form, sendet Token-Mail
Filament\Auth\Pages\PasswordReset\ResetPasswordPublic-Page: Token-Validierung + neues PW setzen
Filament\Auth\Notifications\ResetPasswordMail-Notification-Klasse mit Token-Link
password_reset_tokens TabelleLaravel-Default; existiert bereits — Schema: email PK + token + created_at

Aktivierung im AdminPanelProvider

EIN-Zeilen-Änderung in app/Providers/Filament/AdminPanelProvider.php:

$panel
    ...
    ->login(StrictLoginPage::class)
    ->passwordReset()         // NEU: aktiviert RequestPasswordReset + ResetPassword
    ->multiFactorAuthentication([...])
    ->requiresMultiFactorAuthentication()
    ...

Resultierende Routen

Flow-Diagramm

1. Operator klickt "Forgot password?" auf https://<domain>/admin/login
2. RequestPasswordReset-Page erscheint -> Operator gibt Email ein
3. Filament generiert Token, INSERT in password_reset_tokens
4. Filament sendet Mail via Laravel-Mailer (= webgo SMTP)
5. Operator empfängt Mail mit Link https://<domain>/admin/password-reset/reset/<token>
6. Operator klickt Link -> ResetPassword-Page (PW-Eingabe-Form)
7. Operator gibt neues PW ein, Filament bcrypt-Hash + UPDATE users
8. Token wird DELETE aus password_reset_tokens (one-time)
9. Operator wird zu /admin/login redirected
10. Erst-Login -> MFA-Setup-Forced-Page (SEC-1b-6) sofern noch nicht enrolled

MFA-Interaktion

Wichtig: Password-Reset macht kein MFA-Reset. Der app_authentication_secret bleibt in der DB. Falls Operator-MFA-Setup verloren ist, ist das ein separater Break-Glass-Pfad (siehe SEC-1b-6 Runbook). Aktuell hat admin@example.local keinen MFA-Setup (app_authentication_secret IS NULL) — nach erfolgreicher PW-Reset wird also direkt forced-MFA-Enrollment ausgelöst (SEC-1b-6 Verhalten).


4 — DNS-Konfiguration für Mail-Deliverability

Status

Empfohlene SPF-Erweiterung (von Operator im webgo-DNS-Panel zu setzen)

; SPF für outbound-Mails via webgo SMTP-Relay
gewerbespeicher-rechner.de.  TXT  "v=spf1 include:goserver.host ~all"

Bedeutung:

Aber: nur relevant wenn From-Address = *@gewerbespeicher-rechner.de

Wenn der MAIL_FROM_ADDRESS eine fremde Domain ist (z. B. eine webgo-eigene Mailbox-Domain), greift SPF von jener Domain — webgo kümmert sich.

Decision-Point: welche From-Address wird genutzt? Davon hängt SPF-Bedarf ab.


5 — gui/.env Soll-Werte

Zu setzende Variablen (Klartext nur in gui/.env mode 600, nicht hier):

MAIL_MAILER=smtp
MAIL_HOST=s137.goserver.host
MAIL_PORT=587
MAIL_USERNAME=<operator-provided>     # webgo SMTP-User
MAIL_PASSWORD=<operator-provided>     # webgo SMTP-Passwort
MAIL_ENCRYPTION=tls                      # STARTTLS auf 587
MAIL_FROM_ADDRESS=<operator-decision>   # siehe Decision-Point unten
MAIL_FROM_NAME="Steve TradingBot"

Boundary: nur diese 7 Zeilen werden hinzugefügt; alle bestehenden .env-Einträge bleiben unberührt. Backup-Copy vor Änderung (Pflicht).


6 — AdminPanelProvider Änderung

Eine Zeile zu addieren:

->passwordReset()

Position: nach ->login(StrictLoginPage::class) und vor ->multiFactorAuthentication([…]) (Reihenfolge unkritisch, aber konsistente Convention).

Boundary: AdminPanelProvider hat aktuell folgende panel-Builder-Calls:


7 — Implementation-Sequenz (15 Sub-Steps)

#AktionRisikoZeit
1.0Pre-flight-Snapshot (git, Bot/Worker PIDs, BINANCE_TESTNET, managed_proposals, ufw, nginx-Status)03 min
1.1Backup: pg_dump GUI-DB + gui/.env-Copy nach /root/sec-1c-1c-backup-<ts>/ mode 60003 min
1.2SPF-Record bei webgo-DNS-Panel setzen (Operator-Aufgabe, kann parallel)LOW5 min
1.3gui/.env ergaenzen um 7 MAIL_*-Zeilen (Klartext bleibt in .env, nicht im Output)LOW5 min
1.4php artisan config:clear in GUI-Container (kein Restart; .env wird beim nächsten Request neu gelesen)LOW1 min
1.5SMTP-Connection-Smoke-Test via php artisan tinker: Mail::raw('test',...)->send(); → Operator-InboxMED (Auth-Failure möglich)5 min
1.6SQL: UPDATE users SET email = '<real>' WHERE id=23 AND email='admin@example.local'LOW2 min
1.7AdminPanelProvider ergaenzen um ->passwordReset()LOW2 min
1.8php artisan config:clear + filament:cache-components (kein Restart)LOW2 min
1.9nginx -t + ggf. reload (sollte nicht nötig sein; aber: prophylaktisch)01 min
1.10Smoke-Test 1: curl -k https://81.169.213.37/admin/password-reset/request → expect 200 (page erscheint)LOW2 min
1.11Smoke-Test 2: Browser-Test der Forgot-Password-Flow — Email eingeben, Mail empfangen, Link klicken, neues PW setzenHIGH (Operator-Aufgabe)5 min
1.12Verifizieren: Login mit neuem PW → forced MFA-Setup-Page (SEC-1b-6) → Authenticator-App scannen → Recovery-Codes sichern → eingeloggtHIGH (Operator-Aufgabe)10 min
1.13Tests via Safe-Runner: bash gui/scripts/run_tests_safe.sh --filter MfaTotpTest + Subset von Filament-Resource-Tests — verify keine RegressionLOW10 min
1.14Optional: Closure-Pin + Memory-Update + Commit (falls Repo-Files geändert wurden: app/Providers/Filament/AdminPanelProvider.php)LOW5 min
1.15STOP-Report02 min

Σ SEC-1c-1c: ~60 min + Operator-Browser-Test-Zeit.


8 — Rollback-Pfade

Failure-ZeitpunktRollback
nach Step 1.3 (.env-Edit)cp /root/sec-1c-1c-backup-<ts>/gui.env /projekte/Steve-TradingBot/gui/.env && config:clear
nach Step 1.5 (SMTP-Test failed)diagnostisch: AUTH-Fehler? Port-Block? Encryption-Mismatch? Operator-Decisions reviewen. .env zurück oder fix-iterate.
nach Step 1.6 (Email-Update kollidiert?)UPDATE users SET email = 'admin@example.local' WHERE id=23 (aus pg_dump-Backup wiederherstellbar)
nach Step 1.7 (passwordReset() bricht panel)Zeile aus AdminPanelProvider entfernen + config:clear
nach Step 1.11 (Reset-Link 404 oder Token invalid)Diagnostik: password_reset_tokens-Tabelle? Token-Lifetime? Logs. Fix-iterate.
komplettes Rollbackpg_dump-restore + .env-restore + AdminPanelProvider-Edit rückgängig + config:clear — < 5 min

9 — Stop-Regeln


10 — NO-GO-Bedingungen


11 — Operator-Decision-Points

  1. MAIL_FROM_ADDRESS:
  2. MAIL_FROM_NAME: Steve TradingBot (Empfehlung) oder andere?
  3. Neue Admin-Email für users.id=23: talk@kw-baustoffe.de (Empfehlung) oder andere?
  4. SPF-Record-Setup im webgo-DNS-Panel: Operator-Aufgabe; will Operator das vorher selbst setzen, parallel, oder nach SMTP-Test?
  5. Port-Wahl: 587 STARTTLS ⭐ oder 465 SSL/TLS?
  6. Email-Verification: Filament hat eingebaute ->emailVerification()-Feature — jetzt aktivieren (alle neuen User müssen Email bestätigen) oder als BACKLOG?
  7. Erst-Login-Pfad: nach SEC-1c-1c ist Forgot-Password als Erst-Login möglich. Soll der tinker-Reset-Pfad weiterhin verfügbar bleiben (BACKLOG: Operator-Runbook) oder ausgeschlossen werden?
  8. Reihenfolge zu SEC-1c-1b: SMTP-Setup (SEC-1c-1c) vor Filament-APP_URL-Cutover (SEC-1c-1b)? Empfehlung: ja — Forgot-Password funktioniert dann sowohl auf https://81.169.213.37/admin/login (IP-Fallback) als auch später auf https://gui.…/admin/login.

12 — Phasen-Einordnung

PhaseStatus / Vorbedingung
SEC-1c-1a (HTTPS+Domain+nginx-vhosts)✅ CLOSED 2026-05-13
SEC-1c-1c (SMTP+Password-Reset)🆕 dieses Plan-Review
SEC-1c-1b (APP_URL+SESSION_DOMAIN+TrustedProxies+SECURE_COOKIE Filament-Cutover)geplant nach SEC-1c-1c
SEC-1c-2 (SESSION_ENCRYPT+CSP+HSTS-staged)nach SEC-1c-1b
SEC-1c-3 (DB-Least-Privilege)parallel möglich
SEC-1c-4 (Admin-Rotation)nach SEC-1c-1b/1c; nutzt jetzt PW-Reset-Flow statt manueller Setup
SEC-1c-5 (Audit-Alerts)parallel
SEC-1c-6 (SSH-Finetuning)parallel
SEC-1d (Passkey/WebAuthn)nach SEC-1c-1b (stable APP_URL)
SEC-1e (Final Secret-Rotation)Pre-Mainnet-Pflicht-Gate. Erweitert um SMTP-User+Passwort (siehe Secret-Hygiene-Pin ganz oben).

13 — Boundaries (Plan-Review-End)

master HEAD2fbc7b5 unverändert
git statusunverändert
Bot in-container PID363 unverändert
Worker in-container PID1 unverändert
BINANCE_TESTNETtrue (whitelist-grep)
managed_proposals / history0 / 0
UFWactive
nginxactive
fail2banactive (3 jails)
SMTP-Setup0 (geplant)
email-UPDATE0 (geplant)
AdminPanelProvider-Edit0 (geplant)
SMTP-Werte im PDF / Memory / Output0 (durable Hygiene-Regel)
docker / restart / reload0
Mainnet0
Push0

14 — Empfehlung & nächste Schritte

Empfehlung: SEC-1c-1c als 1 atomarer Block mit den 15 Sub-Steps, vor SEC-1c-1b durchführen (sodass Forgot-Password-Flow auf der noch-aktiven IP-only-Filament-URL https://81.169.213.37/admin/login testbar ist).

Begründung: das Risiko in SEC-1c-1c ist deutlich geringer als in SEC-1c-1b (kein Restart, kein Session-Domain-Wechsel, nur additive Änderungen). Wenn SEC-1c-1c funktioniert, ist der Filament-Cutover in SEC-1c-1b auch über Forgot-Password recovery-bar — doppelte Safety.

Status

PLAN-REVIEW fertig. Warte auf:

Kein Code geschrieben. Kein docker/git/test-Touch. Pure analysis only.