Task 5: Schlüsselbund-Zweck voice (VOICE_ENCRYPTION_KEY), AES-256-GCM- Hilfen für Text und Bytes mit AAD capture:<id>:<art>, voice-config (Modell, Tageslimit, Aufnahme, Whisper-Grösse) und anthropic-config (nie geloggt, nur worker/all), Startprüfung verlangt den Schlüssel in Produktion in jeder Rolle, rekey versiegelt offene Sprachnotizen neu (Audio einzeln gelesen, CAS mit Status), Secret-Vorlagen und ensure-secrets.sh ergänzt. Task 6: Text-Normalisierung und Bereinigung (Steuer- und Formatzeichen, Codepoint-Kürzung), Zitatprüfung mit Dezimalzahlen als ein Wort (Ruling 7), Felderliste passend zu CreateInspectionDto, versiegeltes Transkript mit Grenzen, Fachwörter und Skalen mit Paritätstests. Rollout: Ein Image mit diesem Stand startet in Produktion nur mit VOICE_ENCRYPTION_KEY in apicore-backend-secret (ARCHITECTURE Annahme 22). Gates: Backend tsc, eslint, 922 Unit-Tests, e2e 100 Suites / 1004 Tests. Refs #27 #28 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
100 lines
3.8 KiB
Bash
100 lines
3.8 KiB
Bash
#!/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 <secret> <key> <random|empty|dummy> [<key> <mode> ...]
|
|
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."
|