Zum Inhalt

Authentifizierung & Sicherheit

Anmeldung und Sessions

  • Teammitglieder melden sich mit E-Mail und Passwort gegen die Tabelle users an (/admin/login).
  • Passwörter werden mit scrypt gehasht (lib/auth/password.ts). Es wird keine externe Auth-Bibliothek verwendet.
  • Die Session ist ein HMAC-signiertes httpOnly-Cookie (12 Stunden), signiert mit SESSION_SECRET. Es enthält die User-ID und bei Datenbank-Usern zusätzlich eine session_version.
  • Die Rolle wird bei jedem Request neu aus der Datenbank gelesen (getCurrentUser). Rollenänderungen wirken daher sofort.
  • updateUserAction erhöht users.session_version, wenn sich Passwort oder Rolle ändern. Alle bestehenden Cookies dieses Nutzers werden ungültig, auch das des auslösenden Admins.

Startprüfung für SESSION_SECRET

Beim Serverstart prüft instrumentation.ts (register(), nur in der Node-Runtime) das Secret über checkSessionSecret (lib/env/session-secret.ts):

  • Fehlt SESSION_SECRET oder ist es leer, wirft register(), und der Server startet nicht.
  • Ist es kürzer als 32 Zeichen (SESSION_SECRET_MIN_LENGTH), wird nur eine Warnung geloggt.
  • next build führt die Prüfung nicht aus. Der Worker braucht das Secret nicht.

SESSION_SECRET ist außerdem der Schlüssel für die Adress-Zähler des Formular-Rate-Limits (siehe unten). Wird es geändert, sind alle Sessions ungültig und die Adress-Zähler zurückgesetzt.

Env-Bootstrap-Admin

ADMIN_PASSWORD_HASH ermöglicht eine Bootstrap-Anmeldung mit der E-Mail aus ADMIN_EMAIL (Standard admin@status.local). Sie wird nur akzeptiert, solange kein Datenbank-Admin existiert. Solange sie genutzt wird, zeigt jede Admin-Seite einen dauerhaften Hinweis. Mit ADMIN_BOOTSTRAP_DISABLED_AFTER (ISO-Datum) wird diese Anmeldung ab einem Zeitpunkt abgeschaltet, sowohl für neue Anmeldungen als auch für laufende Sessions. Ein Datum in der Vergangenheit wirkt sofort. Ein leerer oder nicht lesbarer Wert schaltet nichts ab.

Warning

Legen Sie direkt nach der Installation einen Datenbank-Admin an und entfernen Sie danach ADMIN_PASSWORD_HASH aus der Umgebung.

Wiederherstellung bei Aussperrung

Ist ADMIN_BOOTSTRAP_DISABLED_AFTER gesetzt und das Datum vorbei, während kein Datenbank-Admin (oder keiner mit funktionierendem Zugang) existiert, sind Sie ausgesperrt. Zwei Wege zurück:

Regulärer Weg (Zugriff auf die Env vorhanden):

  1. ADMIN_BOOTSTRAP_DISABLED_AFTER entfernen oder auf ein zukünftiges Datum setzen.
  2. App neu starten und mit dem Bootstrap-Login (ADMIN_PASSWORD_HASH) anmelden.
  3. Unter Users einen Datenbank-Admin anlegen.
  4. ADMIN_BOOTSTRAP_DISABLED_AFTER wieder setzen (und ADMIN_PASSWORD_HASH entfernen) und die App neu starten.

Notweg (kein Zugriff auf die Env): Passwort-Hash mit node scripts/hash-admin-password.mjs erzeugen und einen Admin direkt per SQL anlegen. Spaltennamen und Admin-Rollenwert stammen aus users in lib/db/schema.ts:

INSERT INTO users (email, password_hash, name, role)
VALUES ('admin@example.com', '<erzeugter scrypt-Hash>', 'Admin', 'admin');

Warning

pnpm db:seed niemals in Produktion verwenden — es löscht bestehende Daten.

Autorisierung

Die Autorisierung besteht aus zwei Ebenen.

1. Rollenmatrix. Die Rollen und ihre Berechtigungen sind in lib/domain/users.ts definiert (ROLES_CAN_*). Die Tabelle beschreibt, was die Admin-Oberfläche jeder Rolle anbietet:

Rolle Zusammenfassung
admin Alles, inklusive Benutzerverwaltung, Einstellungen, Benachrichtigungen und Audit-Log
service_manager Betreibt alle Komponenten: Service-Status, Ausfälle, Incidents, Wartungen, interne Notizen und Komponentenkonfiguration. Bestätigt Ausfälle in SLA-Reports.
service_owner Setzt den Status seiner eigenen Services, legt für sie Incidents und Wartungen an, pflegt deren Ausfälle, Incident-Updates, Reviews und Wartungen, schließt sie ab, trägt die exakten Ausfallzeiten ein, bestätigt deren Ausfälle in SLA-Reports und sieht SLA-Reports
sla_manager Verwaltet SLA-Pläne, Feiertagslisten, Ziele und Reports (starten, generieren). Pflegt und bestätigt Ausfälle aller Services und pflegt Incident-Updates, -Reviews und Wartungen. Sonst nur Lesezugriff.

2. Komponentenverantwortung. Operative Server Actions (Status setzen, Incident melden, Wartung planen, Incidents und Wartungen abschließen) sind erlaubt für admin, service_manager oder einen Nutzer, der für alle betroffenen Komponenten verantwortlich ist (components.responsible_user_id). Für Ausfälle, Incident-Updates, Reviews und Wartungs-Updates zählt zusätzlich sla_manager als voll berechtigt. Die Helfer userMayOperateOnComponents und userMayManageOutageComponents in lib/auth/components.ts setzen das um. Eine leere Komponentenmenge gilt nie als „verantwortlich“.

Info

Die Admin-Oberfläche spiegelt die Server-Actions wider (seit Issue #361): Die Anlege-Formulare für Incidents und Wartungen sind über ROLES_CAN_START_INCIDENTS_AND_MAINTENANCES für admin, service_manager und service_owner zugänglich; Serviceowner sehen in der Service-Auswahl nur ihre eigenen Services (filterGroupsToOwnedComponents). Interne Notizen bleiben admin und service_manager vorbehalten (ROLES_CAN_WRITE_INCIDENTS).

Info

Die Prüfungen laufen in jeder Seite und jeder Server Action, nicht nur in Layouts. Layouts werden parallel zur Seite gerendert und sind allein keine verlässliche Sicherheitsgrenze.

Öffentliche Endpunkte ohne Anmeldung

  • Statusseite, Historie, Service-Detail, Badge, RSS, Rechtstexte.
  • Abonnements laufen über Double Opt-in (Bestätigung per E-Mail).
  • Abmeldung und Selbstauskunft verlangen einen Token-Link und danach eine POST-Bestätigung. Das Öffnen des Links allein ändert nichts. So sind Prüfdienste, die Links vorab abrufen, unschädlich.
  • Die drei Anfrageformulare (Abonnieren, Abmelde-Link, Datenauskunft) sind gedrosselt und antworten neutral, siehe unten.

API-Tokens

Bearer-Tokens (Authorization: Bearer …) werden für /api/outage/*, /api/incidents und /api/maintenances verwendet. Tokens werden als SHA-256-Hash gespeichert. Jeder Token kann auf bestimmte Services beschränkt, mit einem Ablaufdatum versehen und widerrufen werden. Siehe Öffentliche API.

Rate-Limiting

Anmeldung und API-Tokens

  • Fehlgeschlagene Anmeldungen und ungültige API-Tokens werden je Client-IP gezählt (Tabelle auth_throttle).
  • Standardwerte: Anmeldung 5 Versuche in 15 Minuten, API-Tokens 20 in 5 Minuten, Sperre 15 Minuten. Alle Werte sind über AUTH_RATELIMIT_* konfigurierbar, AUTH_RATELIMIT_ENABLED=false schaltet die Drosselung ab.
  • Ein erfolgreicher Login oder eine gültige Token-Anfrage setzt den Zähler der IP zurück. Ein gültiger Token ohne Berechtigung für den Service (403) zählt nicht als Fehlversuch.

Öffentliche Formulare

  • Abonnieren, Abmelde-Link anfordern und Datenauskunft anfordern sind gedrosselt (lib/auth/public-form-throttle.ts). Hier zählt jede Anfrage, nicht nur fehlerhafte.
  • Zwei Zähler in auth_throttle: je Client-IP über alle drei Formulare (publicform:ip:<ip>, Standard 10 in 60 Minuten, Sperre 60 Minuten) und je Adresse und Formular (publicform:<art>:email:<hmac>, Standard 1 in 10 Minuten, Sperre 10 Minuten).
  • Der Adress-Schlüssel ist ein HMAC-SHA-256 der getrimmten, kleingeschriebenen Adresse mit SESSION_SECRET. Die Adresse selbst wird nicht gespeichert.
  • Ohne erkennbare Client-IP greift der Sammel-Zähler unknown (Standard 100 in 60 Minuten, Sperre 60 Minuten). Er wird einmal pro Prozess als Warnung geloggt.
  • Eine gedrosselte Anfrage antwortet { ok: true } ohne Mail, genau wie bei einer unbekannten Adresse. Geloggt wird nur eine Warnung ohne Adresse.
  • Konfiguration über PUBLIC_FORM_RATELIMIT_ENABLED und PUBLIC_FORM_RATELIMIT_{IP,EMAIL,UNKNOWN_IP}_{MAX,WINDOW_MIN,LOCK_MIN}.

Client-IP und Reverse-Proxy

Beide Rate-Limits ermitteln die Client-IP über clientIp (lib/auth/rate-limit.ts), gesteuert durch TRUSTED_PROXY_IP_HEADER:

Wert Verhalten
auto (Standard) x-real-ip, sonst der letzte Eintrag von x-forwarded-for
x-real-ip Nur x-real-ip. x-forwarded-for wird ignoriert.
x-forwarded-for Nur x-forwarded-for. Gezählt wird von rechts: Bei TRUSTED_PROXY_COUNT=N (Standard 1) gilt der N-te Eintrag von hinten. x-real-ip wird ignoriert.
  • Unbekannte Werte fallen auf auto zurück. Hinter einem Proxy, der nur x-forwarded-for setzt, kann ein Client im Modus auto einen eigenen X-Real-IP-Header einschleusen. Setzen Sie dann x-forwarded-for.
  • Ein Port-Suffix wird entfernt. IPv6-Adressen werden nach RFC 5952 kanonisiert (Zone-Index entfernt, IPv4-mapped als ::ffff:a.b.c.d), damit verschiedene Schreibweisen im selben Zähler landen.
  • Ist die erkannte IP in Produktion privat oder Loopback (IPv4 127/8, 10/8, 172.16/12, 192.168/16, 100.64/10, 169.254/16; IPv6 ::1, fc00::/7, fe80::/10, auch IPv4-mapped), loggt die App einmal pro Prozess eine Warnung. Meist setzt dann der Proxy keinen IP-Header, und alle Besucher teilen sich die Proxy-IP.

Ausfall des Speichers

Fällt der Speicher des Throttles aus, werden Anfragen durchgelassen (Fail-open) und der Fehler protokolliert (tolerateThrottleFailure). Das Rate-Limiting ist eine Schutzmaßnahme, kein hartes Gate.

Geheimnisse und sensible Daten

  • smtp_password, resend_api_key, twilio_auth_token, vapid_private_key und Webhook-Secrets werden mit AES-256-GCM verschlüsselt, Schlüssel SETTINGS_ENCRYPTION_KEY. Verbraucher erhalten über getSettings() immer Klartext.
  • Geheimnisse und Passwort-Hashes werden nie im Audit-Log gespeichert.
  • Der Rechtstext zum Datenschutz wird vor der Ausgabe mit einer strikten Whitelist (h1, h2, h3, b, i) bereinigt.

Audit-Log

Alle mutierenden Admin-Aktionen werden in audit_log festgehalten, mit Akteur, Aktion (<entity>.<verb>), Entität und Before/After-Snapshots. Dazu gehören Einstellungen, Benutzer, Komponenten und Gruppen, Statusänderungen, Ausfälle, Incidents, Wartungen, SLA-Pläne, Feiertagslisten, SLA-Report-Schritte, API-Tokens, Webhooks, SMS-Empfänger, Abonnenten, Templates, Resend und das Neuerzeugen der VAPID-Schlüssel.

Nicht protokolliert werden öffentliche Aktionen ohne Admin (Abonnieren, Bestätigen, Abmelden, Datenauskunft, Push-Abo), reine Test-Aktionen (Test-Mail, Test-SMS, Check-Test, Test-Webhook), Login und Logout sowie interne Incident-Notizen. Die Audit-Seite ist auf die Rolle admin beschränkt. Siehe Einstellungen, Benutzer, API-Tokens & Audit-Log.