#!/usr/bin/env bash # SSHKeyVault aus einer Sicherung wiederherstellen (#029). # # bash deploy/wiederherstellen.sh /var/backups/sshkv # jüngste Sicherung # bash deploy/wiederherstellen.sh /var/backups/sshkv 20261012-170000 # bash deploy/wiederherstellen.sh … --ja # ohne Rückfrage # # Voraussetzung: Der zur Sicherung gehoerende Master-Key liegt an der in .env genannten Stelle # (SSHKV_MASTER_KEY_HOST_FILE). Das Skript vergleicht ihn mit dem Pruefwert der Sicherung und # bricht ab, wenn er nicht passt – sonst laeuft sshkv ohne lesbare Private Keys an. set -o errexit set -o nounset set -o pipefail VERZEICHNIS=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) cd "$VERZEICHNIS" blau() { printf '\033[1;36m› %s\033[0m\n' "$*"; } gruen() { printf '\033[1;32m✓ %s\033[0m\n' "$*"; } fehler() { printf '\033[1;31m✗ %s\033[0m\n' "$*" >&2 exit 1 } QUELLE="${1:-/var/backups/sshkv}" STAND="" OHNE_RUECKFRAGE="nein" for argument in "${@:2}"; do if [ "$argument" = "--ja" ]; then OHNE_RUECKFRAGE="ja"; else STAND="$argument"; fi done [ -d "$QUELLE" ] || fehler "Kein Sicherungsordner unter ${QUELLE}." if [ -z "$STAND" ]; then DATENBANK=$(ls -1t "${QUELLE}"/datenbank-*.sql.gz 2>/dev/null | head -n1 || true) else DATENBANK="${QUELLE}/datenbank-${STAND}.sql.gz" fi [ -n "${DATENBANK:-}" ] && [ -f "$DATENBANK" ] || fehler "Keine Datenbanksicherung in ${QUELLE} gefunden." PRUEFWERT="${DATENBANK/datenbank-/master-key-pruefwert-}" PRUEFWERT="${PRUEFWERT/.sql.gz/.txt}" ENV_DATEI="${SSHKV_ENV_DATEI:-.env}" SCHLUESSEL="$(grep -E '^SSHKV_MASTER_KEY_HOST_FILE=' "$ENV_DATEI" 2>/dev/null | cut -d= -f2- || true)" SCHLUESSEL="${SCHLUESSEL:-./geheim/master.key}" if [ -f "$PRUEFWERT" ]; then [ -r "$SCHLUESSEL" ] || fehler "Master-Key ${SCHLUESSEL} nicht lesbar (mit sudo ausführen?)." [ "$(sha256sum "$SCHLUESSEL" | cut -d' ' -f1)" = "$(cat "$PRUEFWERT")" ] \ || fehler "Der Master-Key unter ${SCHLUESSEL} gehört NICHT zu dieser Sicherung. Abbruch." gruen "Master-Key passt zur Sicherung." else printf '\033[1;33m! Kein Prüfwert zur Sicherung – der Worker prüft den Master-Key beim Start.\033[0m\n' fi printf '\033[1;33m! Achtung: Der aktuelle Datenbestand wird durch die Sicherung ersetzt.\033[0m\n' echo " Sicherung: $(basename "$DATENBANK")" if [ "$OHNE_RUECKFRAGE" != "ja" ]; then read -r -p "Wirklich zurückspielen? [tippen Sie ja] " antwort [ "$antwort" = "ja" ] || fehler "Abgebrochen." fi blau "Weboberfläche und Worker anhalten …" docker compose stop web worker blau "Datenbank zurückspielen …" # Eine Transaktion: scheitert etwas, bleibt der alte Stand vollstaendig erhalten (in der # Wiederherstellungsprobe hatte ein Abbruch ohne Transaktion das Schema pgboss halb geloescht). gunzip -c "$DATENBANK" | docker compose exec -T db psql -U sshkv -d sshkv -v ON_ERROR_STOP=1 --single-transaction -q >/dev/null \ || fehler "Zurückspielen fehlgeschlagen. Web und Worker sind angehalten – nach Klärung: docker compose up -d" # Die Warteschlange gehoert nicht zur Sicherung: frisch anlegen lassen (macht der Worker beim # Start), offene Vorgaenge abbrechen – ihre Jobs sind damit weg. docker compose exec -T db psql -U sshkv -d sshkv -q -c "DROP SCHEMA IF EXISTS pgboss CASCADE" >/dev/null docker compose exec -T db psql -U sshkv -d sshkv -q -c \ "UPDATE \"Vorgang\" SET status = 'ABGEBROCHEN', fehler = 'Durch Wiederherstellung abgebrochen.', \"beendetAm\" = now() WHERE status IN ('WARTEND','LAEUFT')" >/dev/null blau "Dienste starten …" docker compose up -d --wait web worker sleep 3 if docker compose logs --tail 20 worker 2>&1 | grep -q "passt nicht zur Datenbank"; then fehler "Der Worker meldet: Master-Key passt nicht zur Datenbank. Siehe docker compose logs worker." fi gruen "Wiederhergestellt aus $(basename "$DATENBANK")."