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>
76 lines
3.2 KiB
Bash
Executable File
76 lines
3.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Renew the gitea.famfi.home leaf cert (Kopie von renew-openbao-cert.sh, 2026-09-20) from OpenBAO's pki_int engine and
|
|
# install it where Traefik serves it. Idempotent: only renews when the current
|
|
# cert expires within $RENEW_WINDOW_DAYS. Run as root (writes Traefik's tls dir).
|
|
#
|
|
# Installed as a systemd timer (gitea-cert-renew.timer). Manual run:
|
|
# sudo /home/lutz/Projects/OpenBAO/scripts/renew-gitea-cert.sh [--force]
|
|
set -euo pipefail
|
|
|
|
ADDR="${BAO_ADDR:-http://127.0.0.1:8200}"
|
|
TOKEN_FILE="/etc/openbao-cert-renew.token"
|
|
ROLE="pki_int/issue/famfi-home"
|
|
CN="gitea.famfi.home"
|
|
TTL="2160h" # 90 days
|
|
DEST="/srv/TRAEFIK/etc/traefik/tls/gitea"
|
|
DYN="/srv/TRAEFIK/etc/traefik/traefik.d/tls-gitea.yml"
|
|
RENEW_WINDOW_DAYS="${RENEW_WINDOW_DAYS:-21}" # renew when <= this many days left
|
|
FORCE="${1:-}"
|
|
|
|
log() { printf '%s [renew-cert] %s\n' "$(date '+%F %T')" "$*"; }
|
|
die() { log "ERROR: $*"; exit 1; }
|
|
|
|
[ -r "$TOKEN_FILE" ] || die "token file $TOKEN_FILE not readable (run as root?)"
|
|
TOKEN="$(cat "$TOKEN_FILE")"
|
|
|
|
# Skip if the current cert is still good (unless --force)
|
|
if [ "$FORCE" != "--force" ] && [ -f "$DEST/fullchain.pem" ]; then
|
|
end="$(openssl x509 -in "$DEST/fullchain.pem" -noout -enddate | cut -d= -f2)"
|
|
days_left=$(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 ))
|
|
if [ "$days_left" -gt "$RENEW_WINDOW_DAYS" ]; then
|
|
log "cert valid ${days_left}d (> ${RENEW_WINDOW_DAYS}d) — nothing to do"
|
|
exit 0
|
|
fi
|
|
log "cert has ${days_left}d left (<= ${RENEW_WINDOW_DAYS}d) — renewing"
|
|
fi
|
|
|
|
# Issue a fresh cert
|
|
resp="$(curl -sS --fail-with-body --max-time 15 \
|
|
-H "X-Vault-Token: ${TOKEN}" \
|
|
--data "{\"common_name\":\"${CN}\",\"ttl\":\"${TTL}\"}" \
|
|
"${ADDR}/v1/${ROLE}")" || die "issue request failed"
|
|
|
|
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT
|
|
export RESP="$resp"
|
|
python3 - "$tmp" <<PY
|
|
import json, sys, os
|
|
d = json.loads(os.environ["RESP"])["data"]
|
|
t = sys.argv[1]
|
|
open(f"{t}/fullchain.pem", "w").write(d["certificate"] + "\n" + "\n".join(d.get("ca_chain", [])) + "\n")
|
|
open(f"{t}/privkey.pem", "w").write(d["private_key"] + "\n")
|
|
PY
|
|
|
|
# Validate cert/key match before installing
|
|
cmod="$(openssl x509 -in "$tmp/fullchain.pem" -noout -modulus | openssl md5)"
|
|
kmod="$(openssl rsa -in "$tmp/privkey.pem" -noout -modulus 2>/dev/null | openssl md5)"
|
|
[ "$cmod" = "$kmod" ] || die "cert/key modulus mismatch — refusing to install"
|
|
|
|
install -d -o root -g root -m 0755 "$DEST"
|
|
install -o root -g root -m 0644 "$tmp/fullchain.pem" "$DEST/fullchain.pem"
|
|
install -o root -g root -m 0600 "$tmp/privkey.pem" "$DEST/privkey.pem"
|
|
|
|
# Rewrite the watched dynamic config (with a fresh timestamp) so Traefik's file
|
|
# provider reparses and reloads the cert from disk — changing the cert file
|
|
# alone does NOT trigger a reload.
|
|
cat > "$DYN" <<YML
|
|
# AUTO-MANAGED by renew-gitea-cert.sh — last renewed $(date -Iseconds).
|
|
# Loads the internal cert for gitea.famfi.home; the gitea router serves it by SNI
|
|
# (oeffentlich laeuft gitea.famfi.dyndns.org weiter ueber Lets Encrypt).
|
|
tls:
|
|
certificates:
|
|
- certFile: /etc/traefik/tls/gitea/fullchain.pem
|
|
keyFile: /etc/traefik/tls/gitea/privkey.pem
|
|
YML
|
|
chmod 0644 "$DYN"
|
|
log "installed new cert ($(openssl x509 -in "$DEST/fullchain.pem" -noout -enddate | cut -d= -f2)); Traefik reload triggered"
|