From 46991a081568e0a67ece5920e59e359f690ff3c9 Mon Sep 17 00:00:00 2001 From: Nick Szaunig Date: Thu, 15 Oct 2026 16:44:55 +0200 Subject: [PATCH] fix(k8s): ensure-secrets keeps secret values off command lines 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) --- deploy/k8s/scripts/ensure-secrets.sh | 38 +++++++++++++++++++--------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/deploy/k8s/scripts/ensure-secrets.sh b/deploy/k8s/scripts/ensure-secrets.sh index 82a5c6f..cb9f9ae 100644 --- a/deploy/k8s/scripts/ensure-secrets.sh +++ b/deploy/k8s/scripts/ensure-secrets.sh @@ -21,9 +21,14 @@ NS="${NAMESPACE:-apicore}" DRY_RUN=false [[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true -random_key() { - node -e "process.stdout.write(require('crypto').randomBytes(48).toString('base64url'))" -} +# 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 \ @@ -35,19 +40,13 @@ ensure_keys() { local secret="$1"; shift local have have="$(existing_keys "$secret")" - local patch="{}" added=() + local spec=() added=() while (($#)); do local key="$1" mode="$2"; shift 2 if grep -qx "$key" <<<"$have"; then continue fi - local value - case "$mode" in - random) value="$(random_key)" ;; - empty) value="" ;; - dummy) value="dummy" ;; - esac - patch="$(KEY="$key" VALUE="$value" PATCH="$patch" node -e "const p=JSON.parse(process.env.PATCH);p.stringData=p.stringData||{};p.stringData[process.env.KEY]=process.env.VALUE;process.stdout.write(JSON.stringify(p))")" + spec+=("$key=$mode") added+=("$key") done if ((${#added[@]} == 0)); then @@ -58,10 +57,25 @@ ensure_keys() { 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 -p "$patch" >/dev/null + kubectl patch secret "$secret" -n "$NS" --type merge --patch-file "$PATCH_FILE_NATIVE" >/dev/null + : >"$PATCH_FILE" echo "$secret: ergaenzt: ${added[*]}" }