Values were passed as kubectl -p argument and via environment variables, both visible to other processes. Node now generates the values and writes the patch into a private temp file (umask 077, removed on exit); kubectl reads it with --patch-file. Only key names and modes are on the command line. Also fixes the first key being dropped (node -e argv offset). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
92 lines
3.4 KiB
Bash
92 lines
3.4 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_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 und Backups nicht mehr lesbar). Sichern!
|
|
# - 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 \
|
|
OPEN_METEO_API_KEY empty
|
|
|
|
ensure_keys apicore-telegram \
|
|
TELEGRAM_BOT_TOKEN dummy
|
|
|
|
echo "Fertig. Laufende Pods lesen neue Werte erst nach einem Neustart."
|