#!/bin/sh # ============================================================================= # Sicherung zurueckspielen # ============================================================================= # # docker compose exec sicherung /bin/sh /wiederherstellung.sh /backups/cockpit_2026-08-22_0315.dump.enc # # Das Skript spielt eine verschluesselte Sicherung zurueck. Es fragt # ausdruecklich nach, weil dabei der aktuelle Datenbestand ueberschrieben # wird. # # DREI DINGE, DIE MAN VORHER WISSEN MUSS: # # 1. Die Sicherung enthaelt die anwendungsseitig verschluesselten Felder, # aber NICHT den Schluessel. Ohne den passenden ENC_KEY_Vn aus der .env # bleiben Diagnosen, IBANs, Versicherungsnummern und Notizen unlesbar - # die Datenbank ist dann zwar da, aber halb blind. # # 2. Ein Backup, das nie zurueckgespielt wurde, ist kein Backup. Probiere # das hier einmal im Jahr auf einer Testdatenbank aus, bevor du es im # Ernstfall zum ersten Mal brauchst. Siehe README, Abschnitt # "Wiederherstellung ueben". # # 3. Die Dokumente werden NICHT selbsttaetig an ihren Platz geschrieben. # Dieses Skript packt sie nach ./backups aus und sagt, wie es # weitergeht. Grund: Der Sicherungsdienst hat auf das Ablageverzeichnis # bewusst nur Lesezugriff (siehe docker-compose.yml), und in ein # laufendes MinIO hineinzuschreiben wuerde dessen Datenbestand # beschaedigen. Der letzte Handgriff bleibt beim Menschen - er ist ein # Einzeiler und im Klartext beschrieben. # ============================================================================= set -eu DATEI="${1:-}" if [ -z "$DATEI" ]; then echo "Aufruf: $0 " >&2 echo "" >&2 echo "Vorhandene Sicherungen:" >&2 ls -lh "${SICHERUNG_ZIEL:-/backups}"/cockpit_*.dump.enc 2>/dev/null >&2 || echo " keine gefunden" >&2 exit 1 fi if [ ! -f "$DATEI" ]; then echo "FEHLER: $DATEI gibt es nicht." >&2 exit 1 fi if [ -z "${SICHERUNG_PASSWORT:-}" ]; then echo "FEHLER: SICHERUNG_PASSWORT ist nicht gesetzt." >&2 exit 1 fi # --- Pruefsumme ------------------------------------------------------------- if [ -f "$DATEI.sha256" ]; then echo "Pruefsumme wird geprueft ..." if sha256sum -c "$DATEI.sha256" >/dev/null 2>&1; then echo " in Ordnung." else echo "FEHLER: Die Pruefsumme stimmt nicht. Die Datei ist beschaedigt." >&2 exit 1 fi else echo "WARNUNG: Keine Pruefsummendatei gefunden - es wird ungeprueft fortgefahren." fi # --- Sicherheitsabfrage ------------------------------------------------------ echo "" echo "Es wird zurueckgespielt nach:" echo " Datenbank: ${PGDATABASE:-?} auf ${PGHOST:-?}" echo " Aus Datei: $DATEI" echo "" echo "DER AKTUELLE DATENBESTAND WIRD DABEI UEBERSCHRIEBEN." echo "" if [ "${SICHERUNG_OHNE_RUECKFRAGE:-}" != "ja" ]; then printf "Zum Fortfahren bitte JA eingeben: " read -r antwort if [ "$antwort" != "JA" ]; then echo "Abgebrochen." exit 1 fi fi # --- Zurueckspielen ---------------------------------------------------------- echo "" echo "$(date -Iseconds) Wiederherstellung beginnt ..." # --clean --if-exists raeumt vorhandene Objekte ab, bevor sie neu angelegt # werden. --no-owner, weil der Eigentuemer auf einem anderen Server anders # heissen kann. openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 \ -pass env:SICHERUNG_PASSWORT -in "$DATEI" \ | pg_restore --clean --if-exists --no-owner --no-privileges \ --dbname="${PGDATABASE:-postgres}" --exit-on-error echo "$(date -Iseconds) Datenbank wiederhergestellt." # --- Dokumente --------------------------------------------------------------- # Zu "cockpit_2026-09-01_0315.dump.enc" gehoert # "dokumente_2026-09-01_0315.tar.gz.enc" - gleicher Zeitstempel, anderer # Name. Fehlt sie, stammt die Sicherung aus einer Fassung vor v1.12.0. ZEITSTEMPEL="$(basename "$DATEI" | sed -n 's/^cockpit_\(.*\)\.dump\.enc$/\1/p')" DOKUMENTE_DATEI="$(dirname "$DATEI")/dokumente_${ZEITSTEMPEL}.tar.gz.enc" AUSPACKZIEL="$(dirname "$DATEI")/dokumente-wiederhergestellt_${ZEITSTEMPEL}" if [ ! -f "$DOKUMENTE_DATEI" ]; then echo "" echo "WARNUNG: Zu dieser Sicherung gibt es kein Dokumentenarchiv." echo " Erwartet wurde: $DOKUMENTE_DATEI" echo " Sicherungen aus Fassungen vor v1.12.0 enthalten nur die" echo " Datenbank. Die Akte ist dann vollstaendig, die Dateien" echo " dahinter fehlen." else if [ -f "$DOKUMENTE_DATEI.sha256" ]; then echo "Pruefsumme des Dokumentenarchivs wird geprueft ..." if sha256sum -c "$DOKUMENTE_DATEI.sha256" >/dev/null 2>&1; then echo " in Ordnung." else echo "FEHLER: Die Pruefsumme des Dokumentenarchivs stimmt nicht." >&2 exit 1 fi fi mkdir -p "$AUSPACKZIEL" openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 \ -pass env:SICHERUNG_PASSWORT -in "$DOKUMENTE_DATEI" \ | tar -xzf - -C "$AUSPACKZIEL" ANZAHL="$(find "$AUSPACKZIEL" -type f | wc -l)" echo "$(date -Iseconds) Dokumente ausgepackt: $ANZAHL Dateien nach" echo " $AUSPACKZIEL" fi echo "" echo "Naechste Schritte:" echo " 1. Die SQL-Skripte erneut ausfuehren (Volltextsuche und Audit-Schutz):" echo " docker compose exec app npm run db:sql" echo " 2. Pruefen, ob ENC_KEY_V1 in der .env zu diesem Bestand passt -" echo " ohne den richtigen Schluessel bleiben die verschluesselten" echo " Felder unlesbar." echo " 3. Die Dokumente an ihren Platz bringen. Dazu MinIO anhalten, das" echo " Datenverzeichnis ersetzen und wieder starten - auf dem Host," echo " nicht in diesem Behaelter:" echo "" echo " docker compose stop minio app" echo " docker run --rm \\" echo " -v betreuungs-cockpit_minio-daten:/ziel \\" echo " -v \"\$PWD/backups:/quelle:ro\" alpine \\" echo " sh -c 'rm -rf /ziel/* /ziel/..?* && cp -a /quelle/dokumente-wiederhergestellt_*/. /ziel/'" echo " docker compose up -d" echo "" echo " Steht mehr als ein ausgepacktes Verzeichnis unter ./backups," echo " vorher das richtige auswaehlen statt des Sternchens." echo "" echo " 4. In der Anwendung eine betreute Person oeffnen und nachsehen, ob" echo " die Diagnosen im Klartext erscheinen. Dann ein Dokument" echo " herunterladen. Erst wenn beides klappt, ist die" echo " Wiederherstellung wirklich gelungen."