docs(betrieb): Handbuch, Bedrohungsanalyse, geprobte Sicherung (#029)
Handbuch mit Bildern fuer alle Ablaeufe, darunter der Kern- Anwendungsfall und der Deployment-Zugang fuer Claude Code. Betriebsdoku zu Installation, authentik und Reverse Proxy sowie eine Bedrohungsanalyse mit Notfallplan fuer den Fall, dass sshkv selbst kompromittiert wird. Sicherung und Wiederherstellung als Skripte, auf einem Wegwerf-Stapel geprobt. Dabei fielen zwei Fehler auf: das Schema der Warteschlange liess sich nicht zurueckspielen, und ein Abbruch ohne Transaktion loeschte es halb. Jetzt nur Schema public, in einer Transaktion, mit Pruefwert des Master-Keys. Der Worker startet nicht, wenn der Master-Key nicht zur Datenbank passt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Executable
+67
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bash
|
||||
# SSHKeyVault sichern (#029).
|
||||
#
|
||||
# bash deploy/sichern.sh # nach /var/backups/sshkv
|
||||
# bash deploy/sichern.sh /mnt/sicherung/sshkv # anderes Ziel
|
||||
#
|
||||
# Sichert die Datenbank (pg_dump mit --clean, damit sie sich auch in eine nicht leere
|
||||
# Datenbank zurueckspielen laesst) und legt einen Pruefwert des Master-Keys daneben.
|
||||
#
|
||||
# Den MASTER-KEY selbst sichert dieses Skript absichtlich NICHT mit: Datenbank und Master-Key
|
||||
# zusammen geben alle Private Keys preis. Den Master-Key einmalig und GETRENNT aufbewahren
|
||||
# (Passwortmanager, Tresor, zweites Medium) – ohne ihn ist jede Sicherung wertlos.
|
||||
# Siehe docs/betrieb/sicherung.md.
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
VERZEICHNIS=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
cd "$VERZEICHNIS"
|
||||
|
||||
gruen() { printf '\033[1;32m✓ %s\033[0m\n' "$*"; }
|
||||
gelb() { printf '\033[1;33m! %s\033[0m\n' "$*"; }
|
||||
fehler() {
|
||||
printf '\033[1;31m✗ %s\033[0m\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
ZIEL="${1:-/var/backups/sshkv}"
|
||||
BEHALTEN="${SSHKV_SICHERUNGEN_BEHALTEN:-14}"
|
||||
STAND=$(date +%Y%m%d-%H%M%S)
|
||||
|
||||
mkdir -p "$ZIEL"
|
||||
chmod 0700 "$ZIEL"
|
||||
|
||||
# Datenbank: als Klartext-SQL, gzip-komprimiert. Enthaelt nur verschluesselte Private Keys.
|
||||
# Nur das Schema public: Die Warteschlange (Schema pgboss) legt der Worker beim Start neu an,
|
||||
# und ihre partitionierten Tabellen lassen sich mit --clean nicht zurueckspielen (in der
|
||||
# Wiederherstellungsprobe aufgefallen).
|
||||
docker compose exec -T db pg_dump -U sshkv --clean --if-exists --no-owner --schema=public sshkv \
|
||||
| gzip -9 > "${ZIEL}/datenbank-${STAND}.sql.gz" \
|
||||
|| fehler "pg_dump ist fehlgeschlagen. Läuft der Dienst db?"
|
||||
[ -s "${ZIEL}/datenbank-${STAND}.sql.gz" ] || fehler "Die Sicherung ist leer."
|
||||
chmod 0600 "${ZIEL}/datenbank-${STAND}.sql.gz"
|
||||
gruen "Datenbank gesichert: ${ZIEL}/datenbank-${STAND}.sql.gz"
|
||||
|
||||
# Pruefwert des Master-Keys: verraet nichts ueber den Schluessel, zeigt beim Zurueckspielen
|
||||
# aber, ob der richtige Master-Key bereitliegt.
|
||||
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 [ -r "$SCHLUESSEL" ]; then
|
||||
sha256sum "$SCHLUESSEL" | cut -d' ' -f1 > "${ZIEL}/master-key-pruefwert-${STAND}.txt"
|
||||
chmod 0600 "${ZIEL}/master-key-pruefwert-${STAND}.txt"
|
||||
gruen "Prüfwert des Master-Keys abgelegt (nicht der Schlüssel selbst)."
|
||||
else
|
||||
gelb "Master-Key ${SCHLUESSEL} nicht lesbar – kein Prüfwert abgelegt (mit sudo ausführen?)."
|
||||
fi
|
||||
|
||||
# Alte Sicherungen aufraeumen (samt Pruefwert).
|
||||
ls -1t "${ZIEL}"/datenbank-*.sql.gz 2>/dev/null | tail -n +"$((BEHALTEN + 1))" | while read -r alt; do
|
||||
alter_stand=$(basename "$alt" .sql.gz)
|
||||
alter_stand=${alter_stand#datenbank-}
|
||||
rm -f -- "$alt" "${ZIEL}/master-key-pruefwert-${alter_stand}.txt"
|
||||
done
|
||||
|
||||
gelb "Den Master-Key getrennt aufbewahren – er gehört NICHT neben diese Sicherung."
|
||||
Reference in New Issue
Block a user