clusev/CHANGELOG.md

38 KiB
Raw Blame History

Changelog

Alle nennenswerten Änderungen an Clusev. Format nach Keep a Changelog, Versionierung nach SemVer.

Release-Prozess: Feature-Commits sammeln sich auf dem Branch. Ist ein Stand reif, wird er nach main gemerged, als vX.Y.Z getaggt und hier unter einer eigenen Versionsüberschrift dokumentiert. Nutzer beziehen ausschließlich getaggte Releases (Kanal stable, optional beta) — niemals Entwicklungs-Builds.

Unreleased

Keine offenen Änderungen — der nächste Stand wird hier gesammelt und als vX.Y.Z getaggt.

[0.9.8] - 2026-06-19

Hinzugefügt

  • update.sh — Ein-Befehl-Update (sudo ./update.sh). Holt den neuesten Stand (git pull --ff-only, verwirft nie lokale Änderungen), aktualisiert sich bei Bedarf selbst (hat sich update.sh im Pull geändert, startet es die neue Version neu) und übergibt dann an den idempotenten install.sh (Build, Neustart, Migration, MOTD). Setzt für root automatisch safe.directory (das Verzeichnis gehört dem clusev-Nutzer, sonst verweigert ein root-git pull) und ruft install.sh nicht-interaktiv auf, sodass nichts neu abgefragt wird.

Behoben

  • Re-Run von install.sh bewahrt jetzt Domain & ACME-E-Mail. Bislang schrieb der Installer APP_DOMAIN/ACME_EMAIL bei jedem Lauf — ein nicht-interaktiver Re-Run (also ein Update) ohne gesetzte CLUSEV_DOMAIN hätte eine konfigurierte Domain gelöscht. Beide defaulten jetzt auf den bestehenden .env-Wert; Erst-Installation (leer = Bare-IP) unverändert.

Geändert

  • README-Update-Abschnitt auf sudo ./update.sh umgestellt (der alte Zweischritt git pull && sudo ./install.sh funktioniert weiterhin); MOTD zeigt den Update-Befehl an.

[0.9.7] - 2026-06-19

Behoben

  • Update-Prüfung (System → Version & Releases) funktioniert jetzt in Produktion. Sie las das neueste Release aus dem lokalen .git — das es im Docker-Prod-Image gar nicht gibt, daher meldete „Nach Updates suchen" dort immer „Noch kein Release getaggt", selbst wenn längst eine neuere Version veröffentlicht war. Der neueste Tag im Kanal wird jetzt über die öffentliche, lesende Tag-API des Git-Hosts abgefragt (anonym — kein Token verlässt das Panel), mit lokalem .git nur noch als Dev-Fallback; das Ergebnis ist gecacht, sodass ein Seitenaufruf nie eine Netzwerkanfrage auslöst (nur der Knopf selbst). Kanal-Logik: stable bietet nur vX.Y.Z, beta auch Vorab-Tags. Schlägt die Abfrage fehl (offline o. Ä.), degradiert sie still statt zu blockieren.

[0.9.6] - 2026-06-19

Geändert

  • Erst-Admin nutzt jetzt eine feste Standard-E-Mail admin@clusev.local (statt einer aus dem Hostnamen abgeleiteten Adresse, die man hätte raten müssen). Zusammen mit dem Standard-Passwort clusev ist der Erst-Login auf jeder frischen Installation vorhersehbar (admin@clusev.local / clusev, Zwangswechsel beim ersten Login). --email / CLUSEV_ADMIN_EMAIL überschreiben weiter; später unter Einstellungen → Profil änderbar. Installer-Prompt, README und Banner aktualisiert.

Behoben

  • Command-Palette öffnete sich nach dem ersten Login von selbst und ließ sich nicht schließen (nur im Prod-Build). Ursache: app.js baute window.Echo auf Top-Level; auf Auth-Seiten fehlt die Laufzeit-Reverb-Config (window.__clusev) und der Prod-Build bäckt absichtlich keine VITE_REVERB_*-Fallbacks ein, also warf der Echo-Konstruktor — und brach das Skript vor der alpine:init-Registrierung ab. Dadurch waren cmdk/modalTrigger/dualChart nicht registriert, Alpine konnte x-data="cmdk(…)" nicht auflösen, entfernte aber x-cloak, und das Palette-Overlay fiel auf seine sichtbare Default-Darstellung zurück (undismissbar). Echo wird jetzt nur bei vorhandener Config konstruiert, in try/catch gekapselt (ein Realtime-Fehler legt die UI nie lahm) und bei livewire:navigated nachgezogen (Realtime auch auf dem per-SPA erreichten Dashboard). Im Dev mit Vite trat es nie auf, da Vite die VITE_REVERB_* aus der Dev-.env einbäckt.

[0.9.5] - 2026-06-17

Geändert

  • Erst-Admin nutzt jetzt das Standard-Passwort clusev (statt eines Einmal-Zufallspassworts) — vorhersehbarer Login. must_change_password erzwingt den Wechsel beim ersten Login, und Banner/MOTD/README weisen ausdrücklich darauf hin, es sofort zu ändern (ein bekanntes Default ist nur durch den erzwungenen Wechsel vertretbar). Idempotenz unverändert (kein Admin bei Re-Run).
  • Host-MOTD deutlich aufgewertet. Statt zwei Zeilen jetzt eine orange CLUSEV-Wortmarke, die Version, ein Live-Stack-Status („X/Y Dienste aktiv", grün/rot — bei jedem Login frisch aus docker compose ps), die Dashboard-URL, eine Login-Hinweiszeile (woher die Zugangsdaten kommen — ohne Secret) und die Verwaltungs-Befehle. install.sh setzt URL, Version und den Compose-Pfad ein; fällt bei fehlendem Docker robust auf „Status unbekannt" zurück (bricht nie einen Login ab).

Behoben

  • README-Voraussetzungen: git ist auf minimalen Debian/Ubuntu-Images nicht vorinstalliert, der git clone-Schritt scheiterte dort vor dem Installer. Der Install-Block installiert git jetzt explizit, und „Voraussetzungen" nennt es (ein Full-VM-Installtest hat das aufgedeckt).

[0.9.4] - 2026-06-17

Hinzugefügt

  • „DNS prüfen & Zertifikat anfordern" im Dashboard (System → Domain & TLS). Wurde eine Domain konfiguriert, deren DNS noch nicht auf den Server zeigte, lässt sich das Let's-Encrypt-Zertifikat jetzt per Knopf anstoßen, sobald das DNS stimmt: eine DNS-Vorabprüfung (zeigt die Domain hierher?) und — nur ohne klare Fehlanpassung — ein interner Handshake, der Caddys On-Demand-Ausstellung auslöst, plus ein persistierter Status (aktiv / DNS zeigt woanders / ausstehend). Sichtbar nur im eingebauten-TLS-Modus mit aktiver Domain (im Externer-Proxy-Modus ein Hinweis, bei Bare-IP nichts). Per-Nutzer gedrosselt (5/10 Min, auto-ablaufend — sperrt die Control-Plane nie aus) + auditiert; der Handshake ist fest auf den caddy-Dienst gepinnt (kein SSRF). Hinweis: das automatische On-Demand greift weiterhin von selbst beim ersten HTTPS-Aufruf — der Knopf gibt nur explizite Kontrolle + Statusrückmeldung.

[0.9.3] - 2026-06-17

Behoben

  • Modal-Trigger zuverlässig + mit Feedback. Modale gingen direkt nach einer Navigation teils erst beim 2.3. Klick auf (Konsole: „Could not find Livewire component in DOM tree"), und der Button gab kein Signal. Ursache: der globale Modal-Manager wurde bei jedem wire:navigate ab- und neu aufgebaut, sodass sein openModal-Listener kurz fehlte. Er bleibt jetzt über @persist für die ganze Sitzung bestehen — Modale öffnen beim ersten Klick. Zusätzlich zeigt jeder Modal-Trigger (in allen Views) ab Klick sofort einen Spinner und blendet einen Fehler-Toast ein, falls nach kurzer Zeit nichts aufgeht (neues <x-modal-trigger>-Element; der Toaster kennt jetzt eine rote Fehler-Variante). So kann ein Klick nie tot wirken und ein echtes Fehlschlagen ist nie stumm.

Sicherheit

  • Passwort-Reset-Timing weiter abgeflacht. Der Dummy-Verifikationspfad für unbekannte Konten spiegelt jetzt exakt die Kosten der echten Recovery-Code-Prüfung (gesperrtes SELECT in einer Transaktion statt eines schwereren bcrypt-Vergleichs), und Transportfehler beim Reset-Link werden geloggt statt stillschweigend verschluckt.
  • Brute-Force-/Rate-Limiting-Härtung (Audit-Nachgang). Alle neuen Limits laufen automatisch ab und sperren die Control-Plane nie dauerhaft (Bare-IP-Recovery + clusev:reset-admin bleiben offen):
    • Login zusätzlich pro IP (20/Min) und pro Konto (30/15 Min) begrenzt — ein verteilter Mehr-IP-Angriff auf das eine Admin-Konto wird gedeckelt statt linear zu skalieren; und der Login-Zweig für unbekannte E-Mails führt jetzt denselben bcrypt aus (Konstantzeit) und schließt so das Konten-Enumerations-Timing-Orakel.
    • TOTP-Replay-Schutz: jeder 6-stellige Code ist genau einmal gültig (zuletzt akzeptierter Zeit-Schritt wird persistiert), ein abgefangener Code lässt sich nicht im 30-Sek-Fenster wiederverwenden.
    • 2FA-Challenge & 2FA-Beweis-Reset zusätzlich pro Konto begrenzt (IP-unabhängiger Backstop), sodass die TOTP-/Backup-Code-Brute-Force-Fläche auch verteilt gedeckelt ist.
    • Globaler /livewire/update-Throttle (180/Min pro Identität) als Flut-Deckel über den Per-Aktion-Limitern.
    • Re-Auth gedrosselt (Passwortänderung/-Profil, 5/Min pro Nutzer) gegen Passwort-Brute-Force über eine gekaperte Session; SMTP-Testversand auf 3/10 Min pro Nutzer begrenzt (kein Spam-Relay).
    • DoS-Schutz: SSH bekommt einen kurzen, separaten Connect-Timeout (~5 s), damit ein toter/ langsamer Host einen Worker nicht über mehrere sequentielle Reads × 15 s blockiert; die Profil-Passwort-Policy auf 12 Zeichen + Groß/Klein + Ziffern angehoben (war 10).

[0.9.2] - 2026-06-15

Hinzugefügt

  • Ein-Befehl-Installer (sudo ./install.sh). Bootstrappt einen frischen Debian/Ubuntu-Host vollständig: installiert Docker aus dem offiziellen apt-Repo, legt einen dedizierten clusev-Login-Benutzer (Zufallspasswort, docker-Gruppe) an, prüft per DNS-Abgleich, ob die angegebene Domain auf diesen Host zeigt (sonst Warnung + Wahl „Domain trotzdem nehmen — HTTP bis DNS stimmt" oder „auf der IP bleiben"), baut/startet den Stack, migriert und legt den ersten Admin an. Das Abschluss-Banner zeigt ausschließlich Dashboard-URL, Admin-Login und — bei Erstanlage — den clusev-Host-Benutzer; Passwörter erscheinen nur dieses eine Mal. Idempotent (Re-Run baut/migriert neu, Secrets bleiben erhalten).
  • Themed Host-MOTD. Bei jeder Host-Anmeldung erscheint ein Login-Banner in der Produkt-Signalfarbe mit der Dashboard-Adresse.

Geändert

  • Professionelles README — Produktbeschreibung, Voraussetzungen, Installation (git clone + sudo ./install.sh), Domain/TLS (inkl. externem Reverse-Proxy), Updates, Konto-Wiederherstellung und AGPL-Lizenz; ohne interne Entwicklungs-Hinweise.

Sicherheit

  • Passwort-Reset in konstanter Zeit. Unbekannte E-Mail-Adressen durchlaufen jetzt denselben Verifikationspfad (Dummy-Hash- bzw. Code-Prüfung) wie existierende Konten — die Antwortzeit verrät nicht mehr, ob ein Konto existiert (Account-Enumeration über ein Timing-Orakel ausgeschlossen).

[0.9.1] - 2026-06-15

Geändert

  • Passwort-vergessen ist jetzt SMTP-bewusst. Mit konfiguriertem SMTP ist der Standard ein E-Mail-Reset-Link (15 Minuten gültig); ein Zweit-Schalter „Kein E-Mail-Zugang? Mit 2FA-Code zurücksetzen" blendet den Inline-2FA-Reset ein. Ohne SMTP nur der 2FA-Besitznachweis-Reset. Der clusev:reset-admin-Hinweis ist von der öffentlichen Seite verschwunden und steht jetzt in Einstellungen → Sicherheit (eingeloggt) + README.
  • System-Seite neu komponiert statt nur schmaler: 2-Spalten-Layout (links Konfiguration — Domain + TLS-Modus, rechts Status & Wiederherstellung), kritische Hinweise (Neustart, Bare-IP) als Banner über dem Raster.
  • Firewall/fail2ban-Bereich: eine einzeln installierte Box füllt jetzt die volle Breite mit interner 2-Spalten-Aufteilung (kein leerer Raum mehr); zwei Boxen weiterhin nebeneinander.

Sicherheit

  • Backup-Codes nur einmal sichtbar — wasserdicht. Codes werden ausschließlich direkt nach der Erzeugung (erster Faktor oder „Neu erzeugen") angezeigt; „Backup-Codes verwalten" zeigt sie NICHT mehr an, sondern bietet nur „Neu erzeugen". Der Reveal läuft über einen transienten Kanal (nicht über eine persistierte Livewire-Property), sodass ein abgefangener/replayter Snapshot sie nicht erneut rendert; das Reveal-Flag hat eine 10-Minuten-TTL, und der Download-Endpunkt verlangt eine frische, einmalige Berechtigung.
  • Bestätigungs-Dialoge signiert & einmalig. Destruktive Aktionen (Konto/Session/2FA/Hardening/…) laufen über server-ausgestellte, einmal gültige, signierte Tokens statt client-manipulierbarer Event-/Audit-Felder — Bestätigungs-Bypass und Audit-Fälschung sind ausgeschlossen; per-Aktion server-seitig auf das Ziel beschränkt.

Behoben

  • fail2ban „Gesperrte IPs"-Modal öffnet sofort — Skeleton + Lazy-Load statt blockierendem SSH-Read beim Öffnen.
  • Security-Keys-Untertitel korrigiert: ein Schlüssel kann alleiniger Faktor sein, mit TOTP oder beides — nicht mehr „nur als zweiter Faktor".
  • System-Seite: TLS-Modus-Schalter bricht nicht mehr um (kürzeres Label, Details in der Beschreibung); Domain-Feld zeigt nur noch einen Platzhalter (kein vorausgefüllter Beispielwert).
  • Einstellungen mobil: die Tab-Leiste ist breiter als der Schirm geworden — jetzt horizontal scrollbar und sauber abgeschnitten (auf Desktop weiterhin vertikal).

[0.9.0] - 2026-06-14

Hinzugefügt

  • Mehrere Benutzer-Accounts. Einstellungen → Benutzer: weitere (gleichberechtigte) Admin-Konten anlegen, auflisten, entfernen. Neuanlage erzeugt ein Einmal-Passwort (einmalig angezeigt) + erzwungenen Passwortwechsel beim ersten Login. Das Audit-Log hält fest, wer was getan hat.
  • Sitzungsverwaltung. Einstellungen → Sitzungen: aktive Sitzungen ansehen (Gerät, IP, zuletzt aktiv), „andere Geräte abmelden" (eigene) und „alle abmelden (global)"; je Benutzer „überall abmelden". Jede Abmeldung rotiert den remember-Token.
  • Automatischer Neustart. „Jetzt neu starten" in System schreibt eine Anforderung, die ein Host-Watcher (systemd) ausführt — kein Terminal-Befehl, kein Docker-Socket im Container.
  • SMTP-Konfiguration. Einstellungen → E-Mail: SMTP-Server hinterlegen (Passwort verschlüsselt), Testmail senden. Damit funktionieren Passwort-vergessen-Mails wirklich.
  • Audit-Log-Aufbewahrung. Konfigurierbare Aufbewahrungsdauer + täglicher clusev:prune-audit.
  • Datei-Manager zeigt Bilder an. Öffnet man eine Bilddatei (png/jpg/gif/webp/svg/…), wird sie als Vorschau angezeigt statt im Texteditor (SFTP→base64, max. 5 MB, Speichern für Bilder aus).
  • fail2ban: gesperrte IPs im Modal. Die lange Jail-/Bann-Liste wandert aus dem Panel in ein scrollbares Modal ("Gesperrte IPs ansehen") — mit Entsperren je IP.
  • Login & Passwortfelder: Anzeigen/Verbergen-Umschalter (Auge-Icon) in Login, Passwort ändern, Reset und Passwort-vergessen.

Geändert

  • Sitzungen liegen jetzt in der Datenbank (statt Redis), damit sie auflist- und widerrufbar sind. Einmaliger Effekt: beim Update werden alle bestehenden Sitzungen ungültig — einmal neu anmelden.
  • Einstellungen in eigene Tabs (Profil · Sicherheit · Benutzer · Sitzungen · E-Mail), jeweils als eigene Komponente.
  • System-Seite aufgeräumt: Inhalt auf eine angenehme Lesebreite begrenzt (nicht mehr randlos), echtes Speichern-Icon, „Caddy" in der Oberfläche durch neutrale Begriffe ersetzt, plus Hinweis auf die SSH-Notfall-Wiederherstellung (clusev:reset-admin).
  • Eingabefelder: E-Mail-Felder nutzen jetzt type="text" — die Validierung (inkl. Format) passiert ausschließlich serverseitig in der Livewire-Klasse (einheitliche, lokalisierte Meldungen).
  • Backup-Codes-Modal: Buttons sauber ausgerichtet; der Abschluss-Button heißt jetzt „Schließen" (er schließt nur). Alle Modal-Textfelder sind nicht mehr in der Größe ziehbar.

[0.8.0] - 2026-06-14

Hinzugefügt

  • TLS über externen Reverse-Proxy (z. B. Zoraxy). Neuer TLS-Modus in System → Domain & TLS: „Externer Reverse-Proxy" statt „Caddy (Let's Encrypt)". Im externen Modus holt Caddy kein Zertifikat mehr (/_caddy/ask verweigert) und das Panel erzwingt keinen eigenen HTTPS-Redirect — ein Proxy davor terminiert TLS und liefert an den HTTP-Port. Die Host-Prüfung und der Bare-IP-Recovery-Pfad bleiben in beiden Modi aktiv; die Umstellung gilt nach Stack-Neustart. Caddy vertraut dem X-Forwarded-Proto des Proxys über TRUSTED_PROXY_CIDR (Default 127.0.0.1/32 = nichts Externes); Hinweis in der UI, den HTTP-Port per Firewall nur dem Proxy zugänglich zu machen.

Behoben (Sicherheit)

  • Onboarding-Gate auch auf Livewire-Updates. EnsureSecurityOnboarded ist jetzt persistente Livewire-Middleware: das Gate greift auch bei /livewire/update-Aufrufen, nicht nur beim Seiten-GET — eine während des Onboardings gemountete Komponente kann keine destruktiven Aktionen mehr ausführen, nachdem das Konto mitten in der Sitzung auf „Passwort ändern erforderlich" zurückgesetzt wurde.

[0.7.0] - 2026-06-14

Hinzugefügt

  • Optionale, pluggbare 2FA (TOTP und/oder Security-Key — oder aus). 2FA ist nicht mehr erzwungen: Der Betreiber entscheidet selbst, ob er sein Konto absichert, und wählt TOTP, einen Security-Key (YubiKey/WebAuthn) oder beides — oder lässt 2FA ganz aus. Empfohlen, nicht Pflicht.
    • Onboarding erzwingt nur noch die Passwort-Rotation (kein 2FA-Zwang mehr). Nach dem Passwortwechsel landet man direkt im Dashboard; 2FA wird in den Einstellungen angeboten (Hinweis „empfohlen").
    • Ein Security-Key kann der einzige/erste Faktor sein (vorher war TOTP Voraussetzung). TOTP und Keys werden in Einstellungen → Sicherheit unabhängig hinzugefügt/entfernt.
    • Login-Challenge passt sich dem Faktor an: TOTP-Nutzer sehen das Code-Feld, reine Key-Nutzer führen mit dem Security-Key (Backup-Code als Rückfall) — kein irreführendes 6-stelliges Feld mehr.
    • Backup-Codes als Modal statt eigener Seite: werden beim ersten Faktor erzeugt und einmal gezeigt, später jederzeit über „Backup-Codes verwalten" wieder einsehbar / neu erzeugbar / herunterladbar. Die alte /two-factor/recovery-codes-Seite + Route entfällt (Download bleibt).
    • Backup-Codes folgen den Faktoren: beim Entfernen des letzten Faktors werden sie geleert, bei verbleibendem Faktor behalten.
    • Passwort-vergessen ist key-only-sicher: ein reiner Key-Nutzer setzt per Backup-Code zurück; ohne 2FA per E-Mail-Link (falls SMTP) oder clusev:reset-admin.
  • SSH-Key automatisch erstellen & Passwort-Login sicher deaktivieren. Statt der bisherigen Sackgasse („Key zuerst hinterlegen") erzeugt das Panel auf Knopfdruck einen ed25519-Key, installiert ihn, prüft den Key-Login, stellt die eigene gespeicherte Verbindung darauf um — und schaltet Passwort-Login erst danach ab. Scheitert die Prüfung, bleibt Passwort-Login aktiv (kein Aussperren, auch nicht der Control-Plane). Der Private-Key wird einmalig angezeigt (Download) und verschlüsselt im Vault gehalten.
  • Schnellsuche (Strg/⌘ K) findet Server: Tippen von Name oder IP springt direkt zur Server-Detailseite.
  • Journal ist live: journalctl wird per Cursor inkrementell nachgeladen (alle 5 s, lückenlos, ohne Duplikate) statt eines einmaligen Schnappschusses; Anzeige auf 200 Zeilen begrenzt und scrollbar (fixe Höhe), folgt automatisch dem neuesten Eintrag.

Geändert

  • Metriken-Broadcast-Gate: securityOnboarded() heißt jetzt „Passwort rotiert" (nicht mehr „+ 2FA"), da 2FA optional ist — Metriken streamen an eine passwort-rotierte Session.

Behoben

  • Alleinstehende Firewall-/fail2ban-Box dehnte sich über die volle Breite; ist jetzt auf eine sinnvolle Kartenbreite begrenzt, wenn nur ein Tool installiert ist.
  • Einstellungen-Tab in der URL (?tab=…): Reload/Deep-Link bleibt auf dem gewählten Tab statt auf „Profil" zurückzuspringen.

[0.6.1] - 2026-06-14

Behoben

  • Sidebar-Server-Switcher driftete von der Detailseite ab. Es gab zwei „aktueller Server"-Begriffe: den Switcher (Session) und die /servers/{uuid}-Detailseite. Jetzt setzt der Aufruf einer Detailseite den Server aktiv (Sidebar spiegelt immer die Seite), und ein Wechsel im Switcher auf einer Detailseite navigiert zum gewählten Server statt die alte Seite neu zu laden. Geschrieben wird nur bei bewusster Navigation (race-frei). Bekannte Rest-Kante: nach Back/Forward (wire:navigate-Cache, kein erneutes mount()) zeigt die gecachte Seite den richtigen Server, aber die Session kann kurz auf den vorigen zeigen — löst sich bei der nächsten bewussten Navigation; inhärent am Single-Session-Modell (auch Multi-Tab).

[0.6.0] - 2026-06-14

Hinzugefügt

  • WebAuthn / Security-Keys (YubiKey) als optionaler 2. Login-Faktor. Per web-auth/webauthn-lib. TOTP + Backup-Codes bleiben der erforderliche 2FA und die einzige Wiederherstellung; ein Security-Key ist eine zusätzliche Login-Option.
    • Gated auf Domain + HTTPS: Die rpId ist die aktive Domain — auf Bare-IP/HTTP ist die Funktion ausgeblendet (mit Hinweis), da eine IP keine gültige rpId ist.
    • Einstellungen → Sicherheit: Security-Keys hinzufügen (benannt), auflisten, entfernen (mit Bestätigung). Beim Login-2FA-Schritt „Mit Security-Key anmelden" neben TOTP/Backup-Code.
    • 2FA deaktivieren (UI und clusev:reset-admin --disable-2fa) entfernt jetzt auch Security-Keys + Backup-Codes, damit ein Reset den zweiten Faktor vollständig zurücksetzt.

Sicherheit

  • rpId strikt = aktive Domain (nie eine IP). Challenges serverseitig + einmalig.
  • Signatur-Zähler-Prüfung mit Rollback-/Klon-Erkennung; Zähler 0 nur toleriert, wenn der gespeicherte Zähler ebenfalls 0 ist (Plattform-Authenticator/Passkeys).
  • Registrierung erfordert eine aktive 2FA-Anmeldung; Label wird vor der Browser-Ceremony validiert (keine verwaisten Credentials). Alle Aktionen im Audit-Log.

Hinweis: Der echte End-to-End-Test (YubiKey registrieren + anmelden) ist nur unter einer Domain mit HTTPS möglich; die Ceremony-Logik ist auf Options-/Speicher-/Gating-Ebene getestet.

[0.5.0] - 2026-06-14

Hinzugefügt

  • Kontowiederherstellung.
    • 2FA-Backup-Codes: 8 Einmal-Codes werden bei der 2FA-Einrichtung erzeugt, einmalig angezeigt und als .txt herunterladbar; in den Einstellungen (Sicherheit) neu erzeugbar. Beim Login-2FA-Schritt als Alternative zum Authenticator-Code verwendbar.
    • „Passwort vergessen": Reset über einen 2FA-Code oder Backup-Code (ohne E-Mail nötig). Zusätzlich ein E-Mail-Reset-Link, sobald ein echter Mailer konfiguriert ist (mail.defaultlog/array).
    • CLI-Notfall: php artisan clusev:reset-admin setzt das Admin-Passwort (und optional 2FA
      • Backup-Codes) per Server-Shell zurück — der Last-Resort bei kompletter Aussperrung.

Sicherheit

  • Passwort-Resets (Formular, E-Mail-Link, CLI) rotieren das remember_token — eine gestohlene „Angemeldet bleiben"-Cookie überlebt einen Reset nicht.
  • Backup-Codes werden atomar (Row-Lock) verbraucht (kein Replay).
  • Der Notify-Toaster liegt jetzt auch auf den Auth-Seiten; alle Reset-/Backup-Aktionen werden im Audit-Log erfasst.

[0.4.6] - 2026-06-14

Behoben

  • Validierungsmeldungen waren nur Englisch. Es gab keine lang/{de,en}/validation.php, also fiel Laravel bei jedem Eingabefeld-Fehler auf seine eingebauten englischen Meldungen zurück — unabhängig von der gewählten Sprache. Vollständige validation.php in Deutsch (Quelle) und Englisch ergänzt, plus lokalisierte Attribut-Namen (z. B. „SSH-Port", „E-Mail-Adresse"). Leeres Login-Formular zeigt jetzt z. B. „E-Mail-Adresse muss ausgefüllt werden.".

[0.4.5] - 2026-06-14

Hinzugefügt

  • Favicons + PWA-Manifest. Vollständiger Icon-Satz aus dem App-Brand-Mark (oranges Server-Glyph auf Dunkel): favicon.svg (skalierbar), favicon.ico (16/32/48), apple-touch-icon.png, icon-192/512.png und site.webmanifest. In beiden Layouts via gemeinsames partials/head-icons eingebunden (incl. theme-color); nginx liefert das Manifest als application/manifest+json.
  • Eigene Fehlerseiten. Gebrandete, zweisprachige Seiten für 403/404/419/429/500/503 auf einem eigenständigen Fehler-Layout (Design-Tokens, Brand-Mark) — ersetzen Laravels Standard-Fehlerseiten. Es werden nie rohe Status-Texte oder Stacktraces gezeigt; in Prod ist APP_DEBUG fest auf false gepinnt.

Behoben

  • Roher Fehlertext beim Datei-Download. Der Download-Fehler zeigte die unverpackte Exception-Meldung; jetzt eine lokalisierte Meldung (files.download_failed), konsistent mit Upload/Löschen.
  • Fehlerseiten-Sprache. Früh geworfene Fehler (CSRF 419, gematchte Aborts) berücksichtigen jetzt die Nutzersprache (locale-Auflösung im Exception-Hook, absturzsicher gekapselt).

[0.4.4] - 2026-06-14

Behoben

  • Zoom beim Fokus auf Eingabefelder (Handy/Tablet). iOS Safari zoomt die Seite hinein, wenn ein fokussiertes Eingabefeld eine Schriftgröße < 16px hat — durch das dichte Design (text-xs/ text-sm auf Inputs) trat das bei jedem Feld auf. Eine zentrale Regel (@media (pointer: coarse)) erzwingt jetzt 16px für input/textarea/select auf Touch-Geräten (Handy + Tablet jeder Größe, inkl. iPad Pro 1024/1366px), während das Desktop-Layout (Maus) unverändert klein bleibt. Per Touch-Emulation über alle Eingabefelder verifiziert.

[0.4.3] - 2026-06-14

Behoben

  • Mobile Overflow durch lange URLs und IPv6-Bans. Responsive-Audit (R7) über alle Views bei 375/768px: mit den aktuellen Daten lief nichts über, aber lange Werte konnten Zeilen über den Viewport schieben. Behoben: die Panel-Domain (break-all am Link, min-w-0 an der Zugriffsadresse) im System-Bereich und gebannte IPv6-Adressen in der fail2ban-Liste (min-w-0 break-all). Per injizierter 253-Zeichen-Domain und langer IPv6 bei 375px verifiziert.

[0.4.2] - 2026-06-14

Hinzugefügt

  • Server-Status „Initialisierung". Neu angelegte Server starten in einem cyanfarbenen pending-Zustand (nicht mehr sofort rot/Offline), bis der erste Kontakt sie auf online/offline setzt.
  • SSH-Verbindungstest beim Anlegen. Beim Server-Anlegen wird die SSH-Anmeldung in der Anlage-Transaktion geprüft (Login + Befehls-Marker beweisen, dass Befehle ausführbar sind); schlägt sie fehl, wird nichts gespeichert und der SSH-Grund im Formular angezeigt.
  • Hinweis „nur per SSH-Key". Die SSH-Passwort-Login-Zeile der Härtungs-Checkliste weist darauf hin, dass der Zugang nach dem Deaktivieren nur noch per SSH-Key möglich ist.

Geändert

  • Einheitliche Buttons. Alle Buttons haben durchgehend Rahmen + Hintergrund; Löschen-Aktionen nutzen die neue danger-soft-Variante (dezent rot getönt, dauerhaft — nicht erst beim Hover). Die rahmenlosen ghost/ghost-danger-Varianten sind entfallen.
  • Firewall- und fail2ban-Boxen erscheinen nur noch, wenn das jeweilige Tool installiert ist (oder die Statusabfrage fehlschlug), und liegen im responsiven 2-Spalten-Grid (eine Box → volle Breite). Die redundante „nicht installiert"-Box entfällt — installiert wird über die Härtungs-Checkliste.
  • Automatische Updates gelten als Betreiber-Präferenz, nicht als Sicherheitsanforderung: der Zustand wird neutral als aktiv/inaktiv angezeigt und „aus" nie mehr als unsicher (OFFEN) markiert.

Behoben

  • fail2ban/UFW fälschlich „nicht installiert". Die Härtungs-Checkliste erkennt ein Tool jetzt auch dann als installiert, wenn nur die Binary auf dem PATH liegt oder der Dienst läuft (vorher nur dpkg -l … ^ii) — konsistent mit den Status-Panels. Behebt den Widerspruch „Checkliste: nicht installiert" vs. „Panel: aktiv, N gesperrt".

[0.4.1] - 2026-06-14

Behoben

  • Verstümmelter Header auf jeder eingeloggten Seite. Ein Kommentar im @php-Block der topbar enthielt die literalen Tokens @php/@endphp; Blades Raw-Block-Matcher nahm das @endphp im Kommentar als Block-Ende und gab den Rest (inkl. $title ??= …) als Klartext im <header> aus — der Seitentitel blieb zudem leer. Kommentar bereinigt; neue Regel R17 (keine Direktiv-Tokens in Blade-Kommentaren; gerenderte Ausgabe prüfen, nicht nur HTTP 200).

Sicherheit

  • Realtime-Kanäle sind jetzt privat. Der Live-Metrik-Broadcast (MetricsTicked) läuft über einen PrivateChannel, autorisiert über /broadcasting/auth (im web-Group → PanelScheme + Session). Damit kann niemand mehr allein mit dem (öffentlich gebündelten) App-Key Infrastruktur- Metriken abonnieren — auch nicht über einen alten Caddy-Host nach einem Domain-Wechsel. Neue Konvention (CLAUDE.md §3 / routes/channels.php): jeder Broadcast-Kanal ist privat.

[0.4.0] - 2026-06-14

Die Panel-Domain ist wieder aus dem Dashboard änderbar — diesmal sicher: gespeichert wird in der Datenbank, angewendet beim Neustart, das Zertifikat holt Caddy automatisch.

Hinzugefügt

  • Domain aus dem Dashboard ändern. Auf der System-Seite lässt sich die Panel-Domain eintragen oder leeren. Gespeichert wird als Einstellung (panel_domain, übersteuert die Installations-Domain APP_DOMAIN); nach einem Stack-Neustart leiten sich URL, der WebSocket-Endpunkt (Reverb), das TLS-Zertifikat und die Cookie-Sicherheit automatisch davon ab. Kein Terminal-Editieren mehr — nur „speichern und neu starten".
  • Automatisches TLS per On-Demand (Caddy). Caddy stellt das Zertifikat beim ersten HTTPS-Zugriff aus und fragt vorher die App (/_caddy/ask), ob für genau diese Domain ausgestellt werden darf — so kann keine fremde, auf die IP gezeigte Domain Zertifikate erzwingen. Voraussetzung bleibt: die Domain zeigt per DNS auf den Server.

Geändert

  • Der Reverb-Client (Browser) bezieht seinen Endpunkt jetzt zur Laufzeit aus der aktuellen Anfrage und läuft über dieselbe Adresse wie das Panel (Proxy/Reverse-Tunnel /app/*, /apps/* — Caddy in Produktion, nginx in Dev). So folgt Realtime jeder Domain-Änderung ohne Neu-Bauen der Assets und ohne separat geöffneten Port.
  • Server-seitiges Event-Publishing läuft intern direkt zum reverb-Dienst, ist damit unabhängig von der öffentlichen Domain/dem Zertifikat.

Sicherheit

  • Kein Aussperren. Sicherer-Cookie-Schalter und HTTPS-Redirect richten sich nach der tatsächlichen Anfrage (hinter Caddy via X-Forwarded-*, das nur in Produktion vertraut wird). Der Zugriff per IP über HTTP bleibt immer als Rückfallweg erreichbar, falls eine neue Domain (DNS/Zertifikat) noch nicht bereit ist.
  • Nur die aktive Domain liefert das Panel aus. Bei gesetzter Domain wird das Panel per Hostname ausschließlich über genau diese Domain (HTTPS) bedient; eine alte/fremde, auf den Server gezeigte Domain wird abgewiesen (404) — auch wenn Caddy ihr Zertifikat noch hält.

[0.3.0] - 2026-06-13

Zweisprachige Oberfläche: die gesamte UI gibt es jetzt auf Deutsch und Englisch, frei umschaltbar pro Konto.

Hinzugefügt

  • Sprache wählbar (Deutsch / Englisch). Ein DE/EN-Umschalter (oben rechts, sowie auf der Login-Seite) stellt die Sprache um; die Wahl wird pro Konto gespeichert und gilt sofort für die gesamte Oberfläche. Ohne Wahl greift die Standardsprache (Deutsch).
  • Übersetzungen liegen strukturiert unter lang/{de,en}/<gruppe>.php — eine Gruppe je Feature plus geteiltes common, Schlüssel in beiden Sprachen identisch.

Geändert

  • Alle sichtbaren Texte sind lokalisiert. Views, Komponenten und Backend-Services geben Text ausschließlich über __('gruppe.schlüssel') aus, nie mehr fest verdrahtet — neue, verbindliche Regel R16. Native Technik-Begriffe (nginx.service, SSH, 2FA, Ports) bleiben unverändert.

Entfernt

  • Erläuternde „Warum …?"-Texte auf der System-Seite — die Seite zeigt nur noch Funktion und Status.

0.2.1 - 2026-06-13

Verfeinerungen an 0.2.0: vollautomatisches TLS ohne sichtbare Konfiguration und einheitliche Buttons.

Geändert

  • TLS ist vollautomatisch. Caddy stellt das Zertifikat aus, erneuert es und leitet HTTP→HTTPS um — ohne manuelle Schritte. Die System-Seite zeigt nur noch den Status (Domain, Zugriffsadresse) statt der erzeugten Caddy-Konfiguration und des caddy reload-Befehls. Die Panel-Domain ist eine Installations-Entscheidung (APP_DOMAIN) und bleibt damit konsistent mit URL, WebSocket (wss) und Cookie-Sicherheit; zum Ändern install.sh mit der neuen Domain erneut ausführen.
  • Härtungs-Checkliste: alle Aktions-Buttons im selben Stil (einheitlich secondary, mit Rahmen) statt zustandsabhängig mal mit, mal ohne Rahmen.

Entfernt

  • Laufzeit-Override der App-URL aus der Datenbank und das dashboard-seitige Bearbeiten von Domain/E-Mail (konnte URL/WebSocket/Cookies nicht konsistent zur Laufzeit umstellen). Eine Migration entfernt die leeren Alt-Einträge; ein real konfigurierter Wert bleibt als Verlauf erhalten. Upgrade-Hinweis: eine zuvor im Dashboard gesetzte Domain bitte einmalig über install.sh setzen.

0.2.0 - 2026-06-13

Mehr-Betriebssystem-Verwaltung, echte Firewall- und fail2ban-Steuerung aus dem Dashboard sowie eine Tastatur-/Befehls-Bedienung.

Hinzugefügt

  • OS-Abstraktion: Updates, Härtung und Firewall laufen jetzt über apt, dnf und zypper (Debian/Ubuntu, RHEL/Fedora/Rocky/Alma, openSUSE). Das Betriebssystem wird erkannt und auf der Server-Detailseite angezeigt (Paketverwaltung/Firewall). Arch und Alpine werden erkannt; nicht unterstützte Funktionen werden mit deutschem Hinweis sauber deaktiviert statt fehlzuschlagen.
  • Firewall-Regeln (UFW): Regeln ansehen, hinzufügen und löschen samt Standard-Anzeige — mit Aussperr-Schutz (SSH-Regeln/-Port, deny ohne Port). firewalld wird nur angezeigt (Laufzeit aller aktiven Zonen inkl. Rich Rules); Regelverwaltung bleibt vorerst UFW.
  • fail2ban-Status: Jails mit Zählern, gebannte IPs sehen und entsperren, IP manuell sperren (R5-Dialog, Aussperr-Schutz für Loopback + eigenen Zugang) und Whitelist (ignoreip) verwalten.
  • Befehls-Palette (Strg/⌘-K) + Tastenkürzel: Schnell-Navigation (Leader „g“), Seitensuche („/“), Hilfe („?“).

Geändert

  • System-Updates verwenden je Distribution den passenden Paketmanager (Audit-Aktion system.package_upgrade).
  • fail2ban-Tuning und -Whitelist liegen in getrennten Drop-ins, damit Änderungen sich nicht gegenseitig überschreiben; das frühere Einzel-Drop-in wird migriert.
  • Server-Detailseite zeigt erkanntes System, Paketverwaltung und Firewall-Werkzeug.
  • System-Seite erklärt jetzt, warum TLS über Caddy terminiert wird.

Sicherheit

  • Firewall: race-freies Löschen per Regel-Spezifikation (keine Regelnummern), Whitelisting gegen Befehls-Injection, firewalld-Lockout-Schutz (Ports vor dem Start in der permanenten Konfiguration).
  • fail2ban: kanonischer (inet_pton) Schutz gegen das Sperren des eigenen Zugangs, ignoreip wird verbatim erhalten (inkl. Hostnames/Fortsetzungszeilen).

0.1.1 - 2026-06-13

Wartungs-Release: vollständige Code-Auditierung, Entfernung von totem Code und ein behobenes Open-Redirect. Keine funktionalen Änderungen für Nutzer.

Behoben

  • Open-Redirect im Server-Switcher: ein manipulierter Referer konnte die Weiterleitung nach dem Server-Wechsel auf eine fremde Adresse lenken. Die Weiterleitung wird jetzt auf einen gleich-origin relativen Pfad reduziert (fremde Schemata wie javascript:/data:, andere Hosts/Ports sowie protokoll-relative Tricks //host / /\host werden abgewiesen).
  • SSH-Zugang: eine Passphrase wurde unabhängig vom Auth-Typ gespeichert; sie wird jetzt nur noch bei Schlüssel-Authentifizierung hinterlegt.

Geändert

  • Datei-Manager + Editor verwenden ein eigenes file-Symbol statt des Audit-Symbols (semantisch korrekt).
  • Code-Hygiene: toten Code entfernt (ungenutzte FirewallService-Methoden status/allow/deny, Server::auditEvents(), SshCredential::scopeActive(), ungenutztes bell-Icon), veraltete Doc-Blöcke/Kommentare korrigiert, Exceptions sauber importiert, base64-Argument im Sudo-Pfad defensiv gequotet, Null-sichere Nutzerabfrage in den Einstellungen. Pint + Codex-Review sauber.

0.1.0 - 2026-06-13

Erste geschnittene Version — die komplette v1-Basis inklusive Server- und Panel-Härtung als ein Release.

Hinzugefügt

  • Dashboard mit Live-Sparklines und poll-getriebenem Dual-Chart (KPIs + Tabelle).
  • Server-Index + Detailseite, Dienste (systemd: Start/Stop/Restart + Journal), Datei-Manager (SFTP: Up-/Download, Editor), Audit-Log.
  • SSH-Schicht über phpseclib (exec + SFTP) mit Host-Key-Pinning (TOFU) und Multi-Server-Switcher samt Fleet-Kontext.
  • Echte Metriken (CPU/RAM/Disk/Load) inkl. Absolutwerten über Reverb.
  • Authentifizierung + 2FA (TOTP) mit erzwungenem Onboarding.
  • R5-Bestätigungsmodals (wire-elements/modal) für destruktive Aktionen.
  • System-Seite mit Domain & TLS (Caddy Auto-TLS) und Release-Kanal.
  • Echte Versions-Seite, die Version, Build-Hash und dieses Änderungsprotokoll liest.
  • Deploy: install.sh + Caddy (Auto-TLS) + Produktions-Härtung.
  • Selbst-gehostete Fonts (kein CDN), Split-Brand Login/2FA.

Geändert

  • Dienst-Steuerung läuft über das hinterlegte Sudo-Passwort (sudo -S) statt nur passwortlosem sudo — Start/Stop/Restart + Journal funktionieren live.
  • Lazy-Loading der Seiten (Skeletons), normalisierte Buttons + Spinner, Detailseiten- und Einstellungs-Redesign, Schlüsselerzeugung.
  • Vollständige Regel-Auditierung (R1R14), neue Regel R14 (lokale Fonts).

Behoben

  • Datei-Editor: Klick löste einen Alpine-Fehler aus (@js() kompiliert nicht in Komponenten-Attributen) — auf Index-Lookups umgestellt.
  • Adversarial Review der SSH-Schicht (Escaping, Parser, Host-Key-Pinning).
  • 500 durch @disabled auf <x-btn>; Modal über dem Backdrop; poll-getriebene Metriken (Chart + Gauges) statt fiktiver Fleet-Daten; 2FA-Setup-QR als <img>.
  • fail2ban/apt-Härtung schlug fehl, weil der 12s-SSH-Timeout den Paket-Install abbrach — langer Timeout für apt-Aktionen.

Sicherheit

  • Server-Härtung aus dem Dashboard (Root-/Passwort-Login, fail2ban, UFW, Auto-Updates) mit Befehls-Vorschau, Bestätigung und Aussperr-Schutz.
  • Verwaltung hinterlegter SSH-Zugänge (Name, Sperren, Löschen); der Vault verweigert gesperrte Zugänge. Dienst-Steuerung über hinterlegtes Sudo-Passwort.
  • Panel-Selbst-Härtung: Security-Header + umgebungsbewusste CSP, 2FA-Brute-Force- Drossel, Session-Cookie-Härtung.
  • Release-/Versionierungsmodell: echte semantische Versionen + Git-Tags, Changelog pro Version, Kanäle stable/beta (kein nutzer-seitiges dev).