Stand vor Einführung des Nacht-Agenten

This commit is contained in:
Kundencenter 2026-09-27 00:51:32 +02:00
commit 4763548bfb
168 changed files with 12726 additions and 0 deletions

View file

@ -0,0 +1,77 @@
# Betrieb: Backup und Wiederherstellung
## Neu: Passwort und Ziele in der Oberfläche
Unter **Einstellungen → Backup** (nur Superadministratoren dürfen ändern):
- **Backup-Passwort**: selbst wählbar, mindestens 11 Zeichen (länger als 10), mit Wiederholung und Bestätigung durch das Anmeldepasswort. Neue Sicherungen werden damit per **gpg (AES-256, Integritätsschutz)** verschlüsselt (`*.tar.gz.gpg`). Das Passwort liegt mit dem Master-Schlüssel verschlüsselt in der Datenbank, wird nie angezeigt oder protokolliert und lässt sich **nicht wiederherstellen**. Nach einer Änderung bleiben ältere Sicherungen mit dem alten Passwort verschlüsselt. Ohne Passwort und ohne age-Schlüssel wird nichts gesichert (nie unverschlüsselt).
- **Externe Ziele**: SFTP (Passwort oder Schlüssel; Server-Schlüssel wird beim ersten Test gemerkt und angezeigt, ein anderer Server wird danach abgelehnt), FTP/FTPS, Google Drive (Token aus `rclone authorize`), Ordner/Netzlaufwerk (eingehängt, z. B. `/mnt/nas/...`). Zugangsdaten werden verschlüsselt gespeichert und nie wieder angezeigt. „Testen“ legt den Ordner an, schreibt und löscht eine Testdatei. Ziele lassen sich deaktivieren oder entfernen (bereits abgelegte Sicherungen bleiben).
- Alte Sicherungen mit `.age` bleiben mit der Schlüsseldatei lesbar; der Wiederherstellungstest erkennt die Art an der Dateiendung.
**Entschlüsseln im Notfall (nur mit Standardwerkzeug):**
```bash
gpg -d -o archive.tar.gz kundencenter-DATUM-....tar.gz.gpg # fragt nach dem Backup-Passwort
```
## Überblick
- **Was wird gesichert:** Datenbank `kundencenter` (Dump) + Konfiguration mit dem Master-Schlüssel (`app.env`, `db.env`, `discord.env`). Ohne `KC_SECRET_KEY` wären 2FA- und Verbindungs-Geheimnisse in einem Backup unlesbar, deshalb ist er enthalten. **Nicht** enthalten: der private Backup-Schlüssel, Plane-Zugang, das Lizenzsystem (eigenes System, eigenes Backup).
- **Verschlüsselung:** `age` (öffentlicher Schlüssel `BACKUP_AGE_RECIPIENT`). Ziele (NAS, FTP, Google Drive) sehen nur verschlüsselte Dateien.
- **Zeitplan:** täglich ca. 02:30 (`kc-backup.timer`), Wiederherstellungstest sonntags ca. 04:30 (`kc-restore-test.timer`). Verpasste Läufe werden nachgeholt (`Persistent`).
- **Aufbewahrung** (lokal und auf jedem Ziel): 14 tägliche, 8 wöchentliche, 12 monatliche Sicherungen; immer mindestens 3 bleiben erhalten. Fremde Dateien werden nie gelöscht.
- **Ablage lokal:** `/var/backups/kundencenter/` (Rechte 700). Das ersetzt kein externes Ziel: bei Verlust des Servers ist es weg.
- **Kontrolle:** Übersicht (Personal) zeigt letzten Lauf, Ziele und Wiederherstellungstest. Bei fehlgeschlagenem Lauf oder mehr als 26 h ohne Erfolg geht eine Meldung an Discord (ohne Nutzdaten, sobald der Bot eingerichtet ist). Status: `/var/lib/kundencenter/backup-status.json`, Logs: `journalctl -u kc-backup`.
- **Wiederherstellungstest** (automatisch): entschlüsselt die neueste Sicherung, spielt sie in die Wegwerf-Datenbank `kundencenter_restoretest` ein und prüft Prüfsummen, Zeilenzahlen aller Kerntabellen, Migrationen, Audit-Hash-Kette und ob gesicherte Geheimnisse mit dem gesicherten Schlüssel lesbar sind. Produktivdaten bleiben unberührt.
## WICHTIG: privaten Schlüssel extern sichern
Der private Schlüssel liegt in `/etc/kundencenter/backup-age-key.txt` (Rechte 600). **Ohne ihn sind alle Backups wertlos.** Bitte einmalig eine Kopie an einem sicheren Ort ablegen (Passwortmanager, Tresor), **nicht** im selben Cloud-Ziel wie die Backups:
```bash
cat /etc/kundencenter/backup-age-key.txt # Inhalt (zwei Kommentarzeilen + Schlüsselzeile AGE-SECRET-KEY-...) sicher kopieren
```
Der Schlüssel liegt auf dem Server, damit der automatische Wiederherstellungstest laufen kann. Wer den Server kontrolliert, hat ohnehin Zugriff auf die Datenbank.
## Backup-Ziele per Kommandozeile (Alternative zur Oberfläche)
Zugangsdaten der Ziele stehen in `/var/lib/kundencenter/rclone.conf` (nur root). Alle Befehle mit `--config /var/lib/kundencenter/rclone.conf`.
**SFTP (empfohlen: Schlüssel statt Passwort)**
```bash
ssh-keygen -t ed25519 -N "" -f /var/lib/kundencenter/backup_ed25519
cat /var/lib/kundencenter/backup_ed25519.pub # auf dem Ziel in ~/.ssh/authorized_keys eintragen
ssh-keyscan -H ZIELHOST > /var/lib/kundencenter/known_hosts
rclone config create nas sftp host=ZIELHOST user=BENUTZER key_file=/var/lib/kundencenter/backup_ed25519 known_hosts_file=/var/lib/kundencenter/known_hosts --config /var/lib/kundencenter/rclone.conf
rclone mkdir nas:kundencenter --config /var/lib/kundencenter/rclone.conf && rclone lsd nas: --config /var/lib/kundencenter/rclone.conf
```
**FTP/FTPS**
```bash
rclone config create ftpziel ftp host=ZIELHOST user=BENUTZER pass="$(rclone obscure 'PASSWORT')" explicit_tls=true --config /var/lib/kundencenter/rclone.conf
```
(Nur unverschlüsseltes FTP möglich? Dann `explicit_tls=false`; die Backups sind trotzdem verschlüsselt, Zugangsdaten gehen aber im Klartext über das Netz.)
**Google Drive** (OAuth, einmalig auf einem Rechner mit Browser)
```bash
# auf dem Rechner mit Browser (rclone dort installieren):
rclone authorize "drive" "eyJzY29wZSI6ImRyaXZlLmZpbGUifQ" # Scope drive.file: nur von rclone angelegte Dateien
# ausgegebenen Token kopieren, dann auf dem Server:
rclone config create gdrive drive scope=drive.file token='{"access_token":...}' --config /var/lib/kundencenter/rclone.conf
```
Empfehlung: eigene OAuth-Client-ID (Google Cloud) verwenden (`client_id=`/`client_secret=`), sonst gelten geteilte Limits.
**Aktivieren und testen**
```bash
# in /etc/kundencenter/backup.env: BACKUP_REMOTES=nas:kundencenter,gdrive:Backups/kundencenter
systemctl start kc-backup && journalctl -u kc-backup -n 30 --no-pager -o cat # Ziele müssen "ok": true zeigen
```
Ein ausgefallenes Ziel lässt den Lauf als Fehler zählen (Meldung), das lokale Backup bleibt erhalten.
## Wiederherstellung (Notfall)
1. Neuer/repariertes Server mit MariaDB, Node und `age`; Kundencenter-Code nach `/srv/kundencenter` (Repo), `pnpm install && pnpm build`.
2. Sicherungsdatei holen (lokal, Ziel) und den **privaten Schlüssel** bereitlegen.
3. Entschlüsseln und entpacken:
```bash
age -d -i backup-age-key.txt -o archive.tar.gz kundencenter-DATUM-....tar.gz.age
mkdir restore && tar -xzf archive.tar.gz -C restore # enthält db.sql, config/*.env, manifest.json
```
4. Konfiguration zurück: `cp restore/config/*.env /etc/kundencenter/` (chmod 600), Datenbank und Benutzer anlegen (nur Rechte auf `kundencenter`).
5. Datenbank einspielen: `mysql -u… -p kundencenter < restore/db.sql`
6. Dienste starten (`kc-api`, `kc-worker`, `kc-web`), prüfen: `/v1/ready`, Anmeldung, Übersicht, **Audit-Protokoll → „Integrität prüfen“**.
7. Erwartung: Datenverlust höchstens bis zum letzten Backup (bis zu 24 h), Wiederherstellung ca. 30 Minuten.
## Vor größeren Änderungen
Vor Migrationen oder Updates manuell sichern: `systemctl start kc-backup`. Rollback einer fehlerhaften Migration = Wiederherstellung (Abschnitt oben) in eine Wegwerf-Datenbank prüfen, dann produktiv einspielen.