Reconcile development provider secrets

This commit is contained in:
SimpleTest 2026-08-03 23:15:28 +03:00
parent 9e0d41eadf
commit 67747813cb
4 changed files with 211 additions and 0 deletions

View File

@ -669,6 +669,14 @@ that dump. After a successful rollout it also removes the obsolete chart Secret
and only the local Helm history revisions that stored the former inline
credential fields.
When the ignored mode-`0600` `.env` exists, every `kind-up.sh` run also
reconciles a fixed allowlist of development provider settings into that same
Secret: Google/GitHub sign-in, browser push, FCM delivery, Android App Links,
sender identity, and support routing. It never prints their values and does not
replace the independently generated database or application secrets. Empty
allowlisted values remove stale provider settings so `.env` remains the single
development source of truth.
Exercise the verified local rolling-update path without recreating PostGIS or
the Secret:

View File

@ -144,6 +144,16 @@ elif [ -z "$(kube --namespace "$NAMESPACE" get secret "$SECRET_NAME" -o jsonpath
unset metrics_token
fi
if [ -f "$ROOT/.env" ]; then
KUBECTL_BIN="$ROOT/.tools/bin/kubectl" \
KUBE_CONTEXT="kind-${CLUSTER}" \
KUBE_NAMESPACE="$NAMESPACE" \
KUBE_SECRET_NAME="$SECRET_NAME" \
"$ROOT/scripts/sync-kind-runtime-secret.sh" "$ROOT/.env"
else
echo "No .env found; retained the existing optional kind runtime keys."
fi
if [ -n "$legacy_backup" ]; then
kube --namespace "$NAMESPACE" delete deployment postgis --wait=true
fi

View File

@ -83,6 +83,39 @@ scan_image() {
scan_image_sequence=0
echo "Checking the development kind runtime Secret allowlist"
kind_runtime_env="$scan_dir/kind-runtime.env"
kind_runtime_rendered="$scan_dir/kind-runtime.rendered"
cat >"$kind_runtime_env" <<'EOF'
DATABASE_URL=must-not-be-copied
GOOGLE_OAUTH_CLIENT_ID=quality-google-id
GOOGLE_OAUTH_CLIENT_SECRET="quality-google-secret"
GOOGLE_OAUTH_AUTHORIZED_PARTY_IDS=quality-android-id
WEB_PUSH_VAPID_PUBLIC_KEY=quality-vapid-public
FCM_PROJECT_ID=
EOF
chmod 600 "$kind_runtime_env"
./scripts/sync-kind-runtime-secret.sh \
"$kind_runtime_env" --render-only "$kind_runtime_rendered" >/dev/null
grep -Fx 'GOOGLE_OAUTH_CLIENT_ID=quality-google-id' \
"$kind_runtime_rendered" >/dev/null
grep -Fx 'GOOGLE_OAUTH_CLIENT_SECRET=quality-google-secret' \
"$kind_runtime_rendered" >/dev/null
grep -Fx 'GOOGLE_OAUTH_AUTHORIZED_PARTY_IDS=quality-android-id' \
"$kind_runtime_rendered" >/dev/null
grep -Fx 'WEB_PUSH_VAPID_PUBLIC_KEY=quality-vapid-public' \
"$kind_runtime_rendered" >/dev/null
if grep -Eq '^(DATABASE_URL|FCM_PROJECT_ID)=' "$kind_runtime_rendered"; then
echo "Kind runtime Secret renderer copied an unmanaged or empty value." >&2
exit 1
fi
printf '%s\n' 'GOOGLE_OAUTH_CLIENT_ID=duplicate' >>"$kind_runtime_env"
if ./scripts/sync-kind-runtime-secret.sh \
"$kind_runtime_env" --render-only "$kind_runtime_rendered" >/dev/null 2>&1; then
echo "Kind runtime Secret renderer accepted a duplicate managed key." >&2
exit 1
fi
echo "Checking shell scripts with ShellCheck 0.11.0"
# Word splitting is intentional: find emits repository-controlled paths and
# ShellCheck expects each file as a separate argument.

View File

@ -0,0 +1,160 @@
#!/usr/bin/env bash
set -euo pipefail
umask 077
ROOT=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
env_file=${1:-"$ROOT/.env"}
mode=${2:-}
render_target=${3:-}
if [[ "$env_file" != /* ]]; then
env_file="$ROOT/$env_file"
fi
if [[ -n "$mode" && "$mode" != "--render-only" ]]; then
echo "Usage: $0 [ENV_FILE] [--render-only OUTPUT_FILE]" >&2
exit 2
fi
if [[ "$mode" == "--render-only" && -z "$render_target" ]]; then
echo "Usage: $0 [ENV_FILE] [--render-only OUTPUT_FILE]" >&2
exit 2
fi
[[ -f "$env_file" ]] || {
echo "Development environment does not exist: $env_file" >&2
exit 2
}
[[ "$(stat -c '%a' "$env_file")" == 600 ]] || {
echo "Development environment must have mode 0600: $env_file" >&2
exit 2
}
[[ "$(stat -c '%u' "$env_file")" == "$(id -u)" ]] || {
echo "Development environment must be owned by the current user: $env_file" >&2
exit 2
}
managed_keys=(
ANDROID_APP_LINKS_PACKAGE_NAME
ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS
EMAIL_FROM_ADDRESS
EMAIL_FROM_NAME
FCM_PROJECT_ID
FCM_SERVICE_ACCOUNT_JSON_BASE64
GITHUB_OAUTH_CLIENT_ID
GITHUB_OAUTH_CLIENT_SECRET
GOOGLE_OAUTH_AUTHORIZED_PARTY_IDS
GOOGLE_OAUTH_CLIENT_ID
GOOGLE_OAUTH_CLIENT_SECRET
SUPPORT_INBOX_ADDRESS
SUPPORT_OPERATOR_EMAIL_MODE
WEB_PUSH_VAPID_PRIVATE_KEY
WEB_PUSH_VAPID_PUBLIC_KEY
WEB_PUSH_VAPID_SUBJECT
)
runtime_dir=$(mktemp -d "${TMPDIR:-/tmp}/wnh-kind-runtime-secret.XXXXXX")
cleanup() {
status=$?
trap - EXIT HUP INT TERM
find "$runtime_dir" -xdev -depth -delete 2>/dev/null || true
exit "$status"
}
trap cleanup EXIT HUP INT TERM
keys_file="$runtime_dir/managed-keys"
printf '%s\n' "${managed_keys[@]}" >"$keys_file"
filtered_env="$runtime_dir/runtime.env"
awk -F= '
NR == FNR {
managed[$1] = 1
next
}
{
key = $1
if (!(key in managed)) next
seen[key]++
if (seen[key] > 1) {
printf "Managed development key occurs more than once: %s\n", key > "/dev/stderr"
invalid = 1
next
}
value = substr($0, length(key) + 2)
if ((value ~ /^".*"$/) || (value ~ /^\047.*\047$/)) {
value = substr(value, 2, length(value) - 2)
}
if (length(value) > 0) print key "=" value
}
END { if (invalid) exit 1 }
' "$keys_file" "$env_file" >"$filtered_env"
chmod 600 "$filtered_env"
if [[ "$mode" == "--render-only" ]]; then
case "$render_target" in
/*) ;;
*) render_target="$ROOT/$render_target" ;;
esac
install -m 600 "$filtered_env" "$render_target"
echo "Rendered managed development runtime keys without printing their values."
exit 0
fi
KUBECTL_BIN=${KUBECTL_BIN:-kubectl}
KUBE_CONTEXT=${KUBE_CONTEXT:-kind-who-need-help}
KUBE_NAMESPACE=${KUBE_NAMESPACE:-who-need-help}
KUBE_SECRET_NAME=${KUBE_SECRET_NAME:-who-need-help-local}
existing_json="$runtime_dir/existing.json"
extra_json="$runtime_dir/extra.json"
desired_json="$runtime_dir/desired.json"
observed_json="$runtime_dir/observed.json"
"$KUBECTL_BIN" --context "$KUBE_CONTEXT" --namespace "$KUBE_NAMESPACE" \
get secret "$KUBE_SECRET_NAME" --output json >"$existing_json"
if [[ -s "$filtered_env" ]]; then
"$KUBECTL_BIN" create secret generic "$KUBE_SECRET_NAME" \
--namespace "$KUBE_NAMESPACE" \
--from-env-file="$filtered_env" \
--dry-run=client \
--output json >"$extra_json"
else
printf '%s\n' '{"data":{}}' >"$extra_json"
fi
jq --slurpfile extra "$extra_json" --rawfile managed "$keys_file" '
($managed | split("\n") | map(select(length > 0))) as $managed_keys
| .data = (
((.data // {})
| with_entries(select(.key as $key | ($managed_keys | index($key) | not))))
+ ($extra[0].data // {})
)
| {
apiVersion: "v1",
kind: "Secret",
metadata: {
name: .metadata.name,
namespace: .metadata.namespace,
resourceVersion: .metadata.resourceVersion
},
type: (.type // "Opaque"),
data: .data
}
' "$existing_json" >"$desired_json"
"$KUBECTL_BIN" --context "$KUBE_CONTEXT" --namespace "$KUBE_NAMESPACE" \
replace --filename "$desired_json" >/dev/null
"$KUBECTL_BIN" --context "$KUBE_CONTEXT" --namespace "$KUBE_NAMESPACE" \
get secret "$KUBE_SECRET_NAME" --output json >"$observed_json"
jq --exit-status --slurpfile desired "$desired_json" \
'.data == $desired[0].data' "$observed_json" >/dev/null || {
echo "Kubernetes Secret verification did not match the desired key set." >&2
exit 1
}
synced_count=$(wc -l <"$filtered_env" | tr -d '[:space:]')
echo "Synchronized $synced_count managed development runtime keys without printing their values."