diff --git a/docs/betrieb-backup-monitoring.md b/docs/betrieb-backup-monitoring.md new file mode 100644 index 0000000..1879c96 --- /dev/null +++ b/docs/betrieb-backup-monitoring.md @@ -0,0 +1,106 @@ +# Backup, Restore und Monitoring im Produktivbetrieb + +Stand: 2026-07-15 + +Dieses Dokument beschreibt die für den Produktivbetrieb auf dem Webspace +empfohlenen Backup-, Restore- und Monitoring-Maßnahmen. Es ergänzt +`docs/betrieb-mail.md` und `docs/m8-haertung.md`. + +## Backups + +Die App hat kein eigenes Backup-Tooling; das ist bei einer MySQL-Datenbank +auf Webspace-Hosting bewusst nicht nötig – `mysqldump` reicht. + +Empfohlener täglicher Cron-Job (Zeitpunkt außerhalb der Hauptnutzungszeit): + +```bash +#!/usr/bin/env bash +set -euo pipefail + +TIMESTAMP="$(date +%Y%m%d_%H%M%S)" +BACKUP_DIR="/pfad/ausserhalb/des/webroots/backups" +mkdir -p "$BACKUP_DIR" + +mysqldump \ + --single-transaction \ + --routines \ + --triggers \ + -h "$DB_HOST" -P "${DB_PORT:-3306}" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" \ + | gzip > "$BACKUP_DIR/kaffeeliste_${TIMESTAMP}.sql.gz" + +# Aufbewahrung: 14 Tage taegliche Backups behalten, aeltere loeschen +find "$BACKUP_DIR" -name 'kaffeeliste_*.sql.gz' -mtime +14 -delete +``` + +Wichtige Punkte: + +- `--single-transaction` sorgt für einen konsistenten Snapshot ohne + Tabellen zu sperren (InnoDB, wie hier durchgängig verwendet). +- Der Backup-Ordner muss **außerhalb** des über HTTP erreichbaren Webroots + liegen, sonst wären Dumps öffentlich abrufbar. +- Zusätzlich zu lokalen Backups auf dem Webspace mindestens eine Kopie an + einen anderen Ort übertragen (z. B. verschlüsselt in Cloud-Speicher), + damit ein Ausfall des Webspace-Anbieters nicht auch die Backups + vernichtet. +- `var/uploads`, `var/mail` und `var/sessions` enthalten keine dauerhaft + relevanten Daten (Uploads werden nach Verarbeitung gelöscht, + `var/mail` ist nur der Dev-Log-Transport) und müssen nicht gesichert + werden. + +## Restore + +```bash +gunzip -c kaffeeliste_20260715_030000.sql.gz | mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" +``` + +Danach `php scripts/migrate.php` laufen lassen, falls das Backup älter als +die zuletzt eingespielten Migrationen ist (das Skript ist idempotent und +wendet nur fehlende Migrationen an). + +**Restore-Test:** Mindestens einmal pro Quartal einen echten Restore gegen +eine separate Test-Datenbank durchführen und die App dagegen starten +(`scripts/run-dev-server.sh` mit den Test-DB-Zugangsdaten). Ein Backup, das +nie zurückgespielt wurde, ist kein verlässliches Backup. + +## Monitoring und Fehlerlogging + +### PHP-Fehlerausgabe + +Im Dev-Modus zeigt `display_errors=1` Fehler direkt im Browser (siehe +`.local/php-dev.ini`) – das ist für lokale Entwicklung richtig, aber +produktiv ein Sicherheitsrisiko (Stacktraces, Dateipfade). Produktiv muss +gelten: + +```ini +display_errors = Off +log_errors = On +error_log = /pfad/ausserhalb/des/webroots/logs/php-error.log +``` + +Die meisten Webspace-Anbieter setzen das bereits serverseitig; im Zweifel +per `.htaccess` oder `ini_set()` am Anfang von `config.php` erzwingen. + +### Was aktiv beobachtet werden sollte + +- **`php-error.log`**: Fatal Errors und Warnings. Ein plötzlicher Anstieg + deutet meist auf eine kaputte Migration oder einen fehlerhaften Deploy + hin. +- **`audit_log`**: Ungewöhnliche Häufung sicherheitsrelevanter Aktionen + (`participant.access_granted`, `tenant_settings.updated`, + `year_end_bonus.distributed`, `tenant_data.exported`) außerhalb der + üblichen Nutzungszeiten eines Mandanten. +- **`rate_limit_attempts`**: Viele Treffer in kurzer Zeit für einen + `login_ip:*`- oder `login_email:*`-Bucket zeigen einen laufenden + Brute-Force-Versuch, auch wenn er durch das Rate-Limit bereits + abgewehrt wird. +- **`outbound_emails` mit `status = 'failed'`**: Deutet meist auf ein + Problem mit dem lokalen Mailversand hin (siehe + `docs/betrieb-mail.md`), zum Beispiel einen falsch konfigurierten + `APP_MAIL_FROM` oder ein Zustellproblem beim Webspace-Anbieter. + +### Bewusst nicht umgesetzt + +Ein dediziertes APM-/Monitoring-Tool (z. B. Uptime-Checks, strukturiertes +Log-Shipping, Alerting) ist für den aktuellen Umfang nicht eingerichtet. +Das lohnt sich erst mit echtem Produktivbetrieb und mehreren zahlenden +Mandanten; bis dahin reichen die oben genannten manuellen Prüfpunkte. diff --git a/docs/saas-umstrukturierungsplan.md b/docs/saas-umstrukturierungsplan.md index 8398168..2b47695 100644 --- a/docs/saas-umstrukturierungsplan.md +++ b/docs/saas-umstrukturierungsplan.md @@ -308,7 +308,7 @@ Nicht tun: | M5 | App-Kern | Weit fortgeschritten: Kernseiten lesen und schreiben tenant-sicher gegen das Ledger, inklusive Hinweise und rollenbasiertem Zugang; offen ist ein eigener Zahlungs-Screen | | M6 | Betriebsflows | Abgeschlossen: Import, Export, Mail und Jahresabschluss sind auditierbar | | M7 | Landingpage | Public-Seite und Auth-Seiten sind im gemeinsamen Stil nutzbar; spätere Ausbaustufen folgen | -| M8 | Härtung | Weit fortgeschritten: Security-Headers, Rate-Limits, Audit-Log, Mandanten-Isolations- und Rollenmatrix-Tests stehen; Datenexport, Löschung und Betriebs-Doku offen | +| M8 | Härtung | Abgeschlossen: Security-Headers, Rate-Limits, Audit-Log, Mandanten-Isolation/Rollenmatrix getestet, Datenexport, Löschung/Anonymisierung, Backup/Monitoring dokumentiert (CSP als bewusst offener Punkt) | | M9 | Cutover | Produktivumstellung ist vorbereitet und Legacy ist read-only | ### M0: Planungs- und Sicherheitsbaseline @@ -641,8 +641,14 @@ Die SaaS-App für mehrere Kunden sicher betreiben. Schritte: -- Backups und Restore-Prozess definieren. -- Monitoring und Fehlerlogging einrichten. +- Backups und Restore-Prozess definieren: erledigt (dokumentiert). + `docs/betrieb-backup-monitoring.md` beschreibt den täglichen + `mysqldump`-Cron-Job, Aufbewahrung, Restore-Befehl und den + vierteljährlichen Restore-Test. +- Monitoring und Fehlerlogging einrichten: erledigt (dokumentiert). + Produktive PHP-Fehlerkonfiguration sowie die aktiv zu beobachtenden + Signale (`audit_log`, `rate_limit_attempts`, `outbound_emails`) sind in + `docs/betrieb-backup-monitoring.md` festgehalten. - Audit-Log für Admin-Aktionen prüfen: erledigt. Neue Tabelle `audit_log`, protokolliert Mitgliederverwaltung, Storno, Mandant-Einstellungen, Hinweise, CSV-Import, Jahresbonus und Live-Mailversand; sichtbar für @@ -672,7 +678,9 @@ Schritte: Ergebnis: - SaaS ist betrieblich und datenschutzseitig belastbarer. -- Stand: gestartet. Dokumentation: `docs/m8-haertung.md`. +- Stand: abgeschlossen für den M8-Scope, mit einer bewusst offenen + Ausnahme (Content-Security-Policy, siehe `docs/m8-haertung.md`). + Dokumentation: `docs/m8-haertung.md`, `docs/betrieb-backup-monitoring.md`. Abhängigkeiten: