Beim ersten Neustart seit 106 Tagen (2026-09-20) zeigte sich: der Unsealer kommt versiegelt hoch, die Hauptinstanz startet daraufhin endlos neu und nichts kann mehr Secrets lesen - Backups, Traefik-Zertifikate, Skripte. Bisher musste das jemand von Hand entsiegeln. - scripts/unseal-unsealer.sh + systemd/openbao-auto-unseal.service: entsiegelt die Sidecar nach docker.service, startet die Hauptinstanz nach und schreibt die Metrik openbao_unsealer_unsealed (cp1 alarmiert darauf). Idempotent. Zwei Fallstricke, die das Skript umgeht: `bao operator unseal -` (Schluessel ueber stdin) wird abgelehnt, und `bao status` liefert Exit 2 bei versiegelt - was trotzdem heisst, dass der Container antwortet. Der Schluessel liegt in /etc/openbao-unsealer.key (root, 0400), nicht im Repo. - scripts/renew-gitea-cert.sh + systemd/gitea-cert-renew.*: gitea.famfi.home bekommt ein Zertifikat aus der eigenen PKI (90 Tage, Erneuerung ab 21 Tagen Restlaufzeit), Aufbau wie bei renew-openbao-cert.sh. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
81 lines
3.4 KiB
Bash
Executable File
81 lines
3.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Entsiegelt die openbao-unsealer-Sidecar nach einem Host-Neustart.
|
|
#
|
|
# Warum: der Unsealer ist Shamir-versiegelt (1 Schluessel). Bis er offen ist, kann die
|
|
# Hauptinstanz ihren Transit-Seal nicht aufmachen und startet endlos neu (2026-09-20 beobachtet,
|
|
# erster Reboot seit 106 Tagen). Danach entsiegelt sich die Hauptinstanz von selbst.
|
|
#
|
|
# Der Schluessel steht in /etc/openbao-unsealer.key (root, 0400). Das ist bewusst ein Kompromiss:
|
|
# wer root auf diesem Host hat, kann entsiegeln — er liegt aber ohnehin schon in
|
|
# ~/Projects/OpenBAO/unsealer-init.json. Echte Trennung braeuchte den Unsealer auf einem
|
|
# anderen Host (siehe README, Abschnitt Auto-unseal).
|
|
set -uo pipefail
|
|
|
|
KEY_FILE="${KEY_FILE:-/etc/openbao-unsealer.key}"
|
|
CONTAINER="${CONTAINER:-openbao-unsealer}"
|
|
MAIN="${MAIN:-openbao}"
|
|
TEXTFILE_DIR="${TEXTFILE_DIR:-/var/lib/prometheus/node-exporter}"
|
|
PROM="${TEXTFILE_DIR}/openbao_unseal.prom"
|
|
TIMEOUT="${TIMEOUT:-300}" # Sekunden warten, bis Docker und der Container da sind
|
|
|
|
log() { printf '%s [unseal] %s\n' "$(date '+%F %T')" "$*"; }
|
|
|
|
metric() { # $1 = 1 (offen) | 0 (zu)
|
|
[ -d "$TEXTFILE_DIR" ] || return 0
|
|
{
|
|
echo "# HELP openbao_unsealer_unsealed 1 = openbao-unsealer ist entsiegelt"
|
|
echo "# TYPE openbao_unsealer_unsealed gauge"
|
|
echo "openbao_unsealer_unsealed $1"
|
|
echo "# HELP openbao_unsealer_last_attempt_timestamp_seconds Letzter Entsiegelungsversuch"
|
|
echo "# TYPE openbao_unsealer_last_attempt_timestamp_seconds gauge"
|
|
echo "openbao_unsealer_last_attempt_timestamp_seconds $(date +%s)"
|
|
} > "${PROM}.tmp" && mv "${PROM}.tmp" "$PROM" && chmod 644 "$PROM"
|
|
}
|
|
|
|
[ -r "$KEY_FILE" ] || { log "ABBRUCH: $KEY_FILE fehlt oder ist nicht lesbar"; metric 0; exit 1; }
|
|
|
|
# 1. Warten, bis der Container laeuft und seine API antwortet.
|
|
# 'bao status' liefert 0 = offen, 2 = versiegelt, 1 = nicht erreichbar. 0 und 2 heissen
|
|
# beide "der Container antwortet" — genau darauf wird gewartet.
|
|
deadline=$(( $(date +%s) + TIMEOUT ))
|
|
while :; do
|
|
docker exec "$CONTAINER" bao status >/dev/null 2>&1
|
|
rc=$?
|
|
case "$rc" in 0|2) break;; esac
|
|
[ "$(date +%s)" -lt "$deadline" ] || { log "ABBRUCH: $CONTAINER antwortet nicht binnen ${TIMEOUT}s (rc=$rc)"; metric 0; exit 1; }
|
|
sleep 5
|
|
done
|
|
|
|
# 2. Schon offen? Dann nichts tun (idempotent — die Unit darf jederzeit laufen).
|
|
if docker exec "$CONTAINER" bao status 2>/dev/null | grep -qE '^Sealed[[:space:]]+false'; then
|
|
log "$CONTAINER ist bereits entsiegelt"
|
|
metric 1
|
|
exit 0
|
|
fi
|
|
|
|
# 3. Entsiegeln. Der Schluessel geht als Umgebungsvariable in den Container;
|
|
# 'bao operator unseal -' (stdin) funktioniert nicht, es lehnt den Schluessel ab.
|
|
UK="$(tr -d '\r\n' < "$KEY_FILE")"
|
|
docker exec -e UK="$UK" "$CONTAINER" sh -c 'bao operator unseal "$UK"' >/dev/null 2>&1
|
|
unset UK
|
|
|
|
if docker exec "$CONTAINER" bao status 2>/dev/null | grep -qE '^Sealed[[:space:]]+false'; then
|
|
log "$CONTAINER entsiegelt"
|
|
metric 1
|
|
else
|
|
log "FEHLER: $CONTAINER ist weiterhin versiegelt"
|
|
metric 0
|
|
exit 1
|
|
fi
|
|
|
|
# 4. Die Hauptinstanz anstupsen, damit sie nicht erst den naechsten Restart-Zyklus abwartet.
|
|
if docker inspect "$MAIN" >/dev/null 2>&1; then
|
|
docker restart "$MAIN" >/dev/null 2>&1 && log "$MAIN neu gestartet"
|
|
for i in $(seq 1 24); do
|
|
docker exec "$MAIN" bao status 2>/dev/null | grep -qE '^Sealed[[:space:]]+false' && { log "$MAIN ist entsiegelt"; exit 0; }
|
|
sleep 5
|
|
done
|
|
log "HINWEIS: $MAIN ist nach 120s noch nicht entsiegelt — Logs pruefen"
|
|
fi
|
|
exit 0
|