#!/usr/bin/env bash # Verify that the active identity stack has no runtime dependency on the # retired/source host. Secret values are decoded only in process memory and # never printed; failures report resource and key locators only. set -euo pipefail KUBECTL="${KUBECTL:-kubectl}" SOURCE_HOST_MARKER="${SOURCE_HOST_MARKER:-coulombcore}" SOURCE_IP="${SOURCE_IP:-92.205.130.254}" NAMESPACES="${NAMESPACES:-sso mfa user-engine databases}" tmp="$(mktemp -d)" trap 'rm -rf "$tmp"' EXIT for namespace in $NAMESPACES; do for kind in configmap secret deployment statefulset; do "$KUBECTL" -n "$namespace" get "$kind" -o json \ >"$tmp/${namespace}-${kind}.json" done done python3 - "$tmp" "$SOURCE_HOST_MARKER" "$SOURCE_IP" <<'PY' import base64 import json import pathlib import sys root = pathlib.Path(sys.argv[1]) patterns = { "source-host": sys.argv[2].lower(), "source-ip": sys.argv[3].lower(), "public-lldap": "lldap.coulomb.social", "public-privacyidea": "pink.coulomb.social", "public-privacyidea-account": "pink-account.coulomb.social", } failures = [] for path in sorted(root.glob("*.json")): namespace, kind = path.stem.rsplit("-", 1) for item in json.loads(path.read_text()).get("items", []): name = item["metadata"]["name"] if kind in {"configmap", "secret"}: for key, raw in (item.get("data") or {}).items(): try: value = ( base64.b64decode(raw).decode("utf-8", "replace") if kind == "secret" else raw ) except Exception: continue lowered = value.lower() hits = [label for label, needle in patterns.items() if needle in lowered] if hits: failures.append( f"{kind}/{namespace}/{name}:{key} matches={','.join(hits)}" ) else: lowered = json.dumps(item.get("spec", {}), sort_keys=True).lower() hits = [label for label, needle in patterns.items() if needle in lowered] if hits: failures.append( f"{kind}/{namespace}/{name} matches={','.join(hits)}" ) if failures: print("stale_dependencies=found") for failure in failures: print(failure) raise SystemExit(1) print("stale_dependencies=absent") PY keycape_config="$( "$KUBECTL" -n sso get secret keycape-config \ -o jsonpath='{.data.config\.yaml}' | base64 -d )" for expected in \ lldap.sso.svc.cluster.local \ authelia.sso.svc.cluster.local \ privacyidea.mfa.svc.cluster.local \ user-engine \ rapp-qonto do if [[ "$keycape_config" != *"$expected"* ]]; then echo "keycape_expected_dependency_missing=$expected" >&2 exit 1 fi done unset keycape_config echo "keycape_dependencies=cluster-local-and-complete" pg_pod="$( "$KUBECTL" -n databases get pod \ -l 'cnpg.io/cluster=net-kingdom-pg,role=primary' \ -o jsonpath='{.items[0].metadata.name}' )" resolver_class="$( "$KUBECTL" -n databases exec "$pg_pod" -- \ psql -X -A -t -U postgres -d privacyidea_db -c \ "select case when lower(\"Value\") like '%lldap.sso.svc.cluster.local%' then 'cluster-local' else 'other' end from resolverconfig where \"Key\"='LDAPURI';" 2>/dev/null )" if [[ "$resolver_class" != "cluster-local" ]]; then echo "privacyidea_lldap_resolver=$resolver_class" >&2 exit 1 fi echo "privacyidea_lldap_resolver=cluster-local" for target in \ sso/lldap \ sso/authelia \ sso/keycape \ mfa/privacyidea \ user-engine/user-engine do namespace="${target%/*}" name="${target#*/}" ready="$( "$KUBECTL" -n "$namespace" get deployment "$name" \ -o jsonpath='{.status.readyReplicas}' )" if [[ "${ready:-0}" -lt 1 ]]; then echo "deployment_not_ready=$target" >&2 exit 1 fi done echo "identity_deployments=ready" echo "identity_cutover_dependencies=PASS"