#!/usr/bin/env bash # Ergaenzt fehlende Schluessel in den ApiCore-Secrets, ohne vorhandene Werte # zu aendern. Gibt nur Schluesselnamen aus, nie Werte. Mehrfach ausfuehrbar. # # bash deploy/k8s/scripts/ensure-secrets.sh # Namespace apicore # bash deploy/k8s/scripts/ensure-secrets.sh --dry-run # nur anzeigen # # Werte: # - MFA_/WEBHOOK_/BACKUP_/VOICE_ENCRYPTION_KEY: zufaellig (48 Byte, base64url). Die # App verlangt mindestens 32 Zeichen; "dummy" wuerde den Start abbrechen. # Diese Schluessel nie wieder aendern ohne *_PREVIOUS (sonst sind MFA- # Geheimnisse, Webhook-Secrets, Backups und offene Sprachnotizen nicht mehr # lesbar). Sichern! # VOICE_ENCRYPTION_KEY muss vor dem Rollout des T7-Backend-Images existieren: # die App verlangt ihn in Produktion in jeder Rolle und startet sonst nicht. # - OPEN_METEO_API_KEY: leer = freie Open-Meteo-API (ein Fantasiewert wuerde # die kostenpflichtige API mit falschem Schluessel ansprechen). # - TELEGRAM_BOT_TOKEN (Secret apicore-telegram, nur Worker): "dummy". Die App # erkennt ein ungueltiges Format und schaltet Telegram ab, bis das echte # Token von BotFather eingetragen ist. set -euo pipefail NS="${NAMESPACE:-apicore}" DRY_RUN=false [[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true # Secret values never appear on a command line or in the environment (both # are visible to other processes): node writes the patch straight into a # private temp file, kubectl reads it with --patch-file. umask 077 PATCH_FILE="$(mktemp)" trap 'rm -f "$PATCH_FILE"' EXIT # Windows tools (node, kubectl) in Git Bash need the native path. PATCH_FILE_NATIVE="$(cygpath -w "$PATCH_FILE" 2>/dev/null || echo "$PATCH_FILE")" existing_keys() { kubectl get secret "$1" -n "$NS" -o jsonpath='{.data}' 2>/dev/null \ | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{try{console.log(Object.keys(JSON.parse(s||'{}')).join('\n'))}catch{}})" } # ensure_keys [ ...] ensure_keys() { local secret="$1"; shift local have have="$(existing_keys "$secret")" local spec=() added=() while (($#)); do local key="$1" mode="$2"; shift 2 if grep -qx "$key" <<<"$have"; then continue fi spec+=("$key=$mode") added+=("$key") done if ((${#added[@]} == 0)); then echo "$secret: alle Schluessel vorhanden" return fi if $DRY_RUN; then echo "$secret: wuerde ergaenzen: ${added[*]}" return fi # Only key names and modes go on the command line; values are created here. PATCH_FILE_PATH="$PATCH_FILE_NATIVE" node -e " const crypto = require('crypto'); const value = (mode) => mode === 'random' ? crypto.randomBytes(48).toString('base64url') : mode === 'dummy' ? 'dummy' : ''; const stringData = {}; for (const entry of process.argv.slice(1)) { const [key, mode] = entry.split('='); stringData[key] = value(mode); } require('fs').writeFileSync(process.env.PATCH_FILE_PATH, JSON.stringify({ stringData }), { mode: 0o600 }); " "${spec[@]}" if ! kubectl get secret "$secret" -n "$NS" >/dev/null 2>&1; then kubectl create secret generic "$secret" -n "$NS" >/dev/null fi kubectl patch secret "$secret" -n "$NS" --type merge --patch-file "$PATCH_FILE_NATIVE" >/dev/null : >"$PATCH_FILE" echo "$secret: ergaenzt: ${added[*]}" } ensure_keys apicore-backend-secret \ MFA_ENCRYPTION_KEY random \ WEBHOOK_ENCRYPTION_KEY random \ BACKUP_ENCRYPTION_KEY random \ VOICE_ENCRYPTION_KEY random \ OPEN_METEO_API_KEY empty ensure_keys apicore-telegram \ TELEGRAM_BOT_TOKEN dummy # apicore-anthropic (ANTHROPIC_API_KEY, nur Worker, optional) legt der Inhaber # selbst an; dieses Skript erzeugt es nicht. Ohne Schluessel bleibt die # Auswertung der Sprachnotizen aus (EXTRACT_DISABLED), alles andere laeuft. echo "Fertig. Laufende Pods lesen neue Werte erst nach einem Neustart."