Files
net-kingdom/sso-mfa/k8s/verify-t03.sh
Bernd Worsch 6d25d088d7 feat(sso-mfa): T02/T03 live apply — age-encrypted secrets, CNPG cluster (NK-WP-0001-T02/T03)
- Add encrypt-secrets.sh / decrypt-secrets.sh: age-based secrets workflow
  replaces KeePassXC dependency; encrypted .env.age files committed to repo
- Add bootstrap/secrets.enc/: all component secrets encrypted to age pubkey
- Fix .gitignore: allow secrets.enc/**/*.age while blocking plaintext
- Fix verify-t02.sh: update netpol names for Authelia+LLDAP+KeyCape stack
- Fix verify-t03.sh: remove keycloak_db/role checks; fix ((PASS++)) set-e bug
- Update postgresql/cluster.yaml: drop keycloak_db, bootstrap privacyidea_db only
- Update postgresql/create-secrets.sh: remove keycloak secret
- Fix netpol-databases.yaml: add port 8000 for CNPG instance manager HTTP API
- T02 COMPLETE: namespaces, network policies, cert-manager issuers applied
- T03 COMPLETE: CNPG operator installed, net-kingdom-pg cluster healthy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 02:57:41 +00:00

171 lines
7.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# verify-t03.sh — verify NK-WP-0001-T03 done-criteria
#
# Checks:
# 1. CloudNativePG operator is installed and running
# 2. Cluster net-kingdom-pg is Ready
# 3. privacyidea_db database exists
# 4. privacyidea role exists
# 5. K8s Secrets are present in the databases namespace
# 6. (Optional) Scheduled backup CR is present when backup is configured
#
# Note: keycloak_db removed — Keycloak replaced by Authelia+LLDAP+KeyCape (T05).
#
# Usage:
# chmod +x verify-t03.sh
# ./verify-t03.sh
set -euo pipefail
PASS=0
FAIL=0
WARN=0
pass() { echo " [PASS] $1"; PASS=$((PASS + 1)); }
fail() { echo " [FAIL] $1"; FAIL=$((FAIL + 1)); }
warn() { echo " [WARN] $1"; WARN=$((WARN + 1)); }
section() { echo ""; echo "── $1 ──────────────────────────────────────"; }
# ── 1. CloudNativePG operator ─────────────────────────────────────────────────
section "1. CloudNativePG operator"
if kubectl get ns cnpg-system &>/dev/null; then
pass "cnpg-system namespace exists"
else
fail "cnpg-system namespace not found — install operator first (see postgresql/README.md)"
fi
if kubectl get crd clusters.postgresql.cnpg.io &>/dev/null; then
pass "clusters.postgresql.cnpg.io CRD registered"
else
fail "CloudNativePG CRD not found — operator not installed"
fi
CNPG_READY=$(kubectl get pods -n cnpg-system -l app.kubernetes.io/name=cloudnative-pg \
--field-selector=status.phase=Running --no-headers 2>/dev/null | wc -l || echo 0)
if [[ "$CNPG_READY" -ge 1 ]]; then
pass "CloudNativePG operator pod running ($CNPG_READY pod(s))"
else
fail "No running CloudNativePG operator pods in cnpg-system"
fi
# ── 2. Cluster readiness ──────────────────────────────────────────────────────
section "2. Cluster net-kingdom-pg"
CLUSTER_READY=$(kubectl get cluster net-kingdom-pg -n databases \
-o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || echo "")
if [[ "$CLUSTER_READY" == "True" ]]; then
pass "Cluster net-kingdom-pg status: Ready"
else
CLUSTER_PHASE=$(kubectl get cluster net-kingdom-pg -n databases \
-o jsonpath='{.status.phase}' 2>/dev/null || echo "not found")
fail "Cluster net-kingdom-pg not Ready (phase: $CLUSTER_PHASE)"
fi
PRIMARY_POD=$(kubectl get pod -n databases \
-l "cnpg.io/cluster=net-kingdom-pg,role=primary" \
--field-selector=status.phase=Running \
-o name 2>/dev/null | head -1 || echo "")
if [[ -n "$PRIMARY_POD" ]]; then
pass "Primary pod running: $PRIMARY_POD"
else
fail "No running primary pod found for net-kingdom-pg"
fi
# ── 3. Databases ──────────────────────────────────────────────────────────────
section "3. Databases"
if [[ -n "$PRIMARY_POD" ]]; then
DB_LIST=$(kubectl exec -n databases "$PRIMARY_POD" -- \
psql -U postgres -tAc "SELECT datname FROM pg_database WHERE datname = 'privacyidea_db';" \
2>/dev/null || echo "")
if echo "$DB_LIST" | grep -q "privacyidea_db"; then
pass "privacyidea_db exists"
else
fail "privacyidea_db not found"
fi
else
warn "Skipping database checks — no primary pod available"
fi
# ── 4. Roles ──────────────────────────────────────────────────────────────────
section "4. Database roles"
if [[ -n "$PRIMARY_POD" ]]; then
ROLE_LIST=$(kubectl exec -n databases "$PRIMARY_POD" -- \
psql -U postgres -tAc "SELECT rolname FROM pg_roles WHERE rolname = 'privacyidea';" \
2>/dev/null || echo "")
if echo "$ROLE_LIST" | grep -q "privacyidea"; then
pass "role privacyidea exists"
else
fail "role privacyidea not found"
fi
else
warn "Skipping role checks — no primary pod available"
fi
# ── 5. K8s Secrets ────────────────────────────────────────────────────────────
section "5. K8s Secrets (databases namespace)"
for secret in net-kingdom-pg-privacyidea-app; do
if kubectl get secret "$secret" -n databases &>/dev/null; then
pass "Secret $secret exists"
else
fail "Secret $secret not found — run create-secrets.sh"
fi
done
# CNPG auto-creates these
for secret in net-kingdom-pg-superuser; do
if kubectl get secret "$secret" -n databases &>/dev/null; then
pass "Secret $secret exists (CNPG-managed)"
else
warn "Secret $secret not found (CNPG creates this; may appear after cluster init)"
fi
done
# ── 6. Backup configuration ───────────────────────────────────────────────────
section "6. Backup (optional until object storage is provisioned)"
if kubectl get scheduledbackup net-kingdom-pg-daily -n databases &>/dev/null; then
BACKUP_SUSPENDED=$(kubectl get scheduledbackup net-kingdom-pg-daily -n databases \
-o jsonpath='{.spec.suspend}' 2>/dev/null || echo "false")
if [[ "$BACKUP_SUSPENDED" == "true" ]]; then
warn "ScheduledBackup net-kingdom-pg-daily is suspended"
else
pass "ScheduledBackup net-kingdom-pg-daily present and active"
fi
LAST_BACKUP=$(kubectl get backup -n databases \
-l "cnpg.io/cluster=net-kingdom-pg" \
--sort-by='.metadata.creationTimestamp' \
-o name 2>/dev/null | tail -1 || echo "")
if [[ -n "$LAST_BACKUP" ]]; then
pass "At least one backup found: $LAST_BACKUP"
else
warn "No backups yet — trigger a manual backup or wait for schedule"
fi
else
warn "ScheduledBackup not deployed — configure object storage, then apply scheduled-backup.yaml"
fi
# ── Summary ───────────────────────────────────────────────────────────────────
echo ""
echo "════════════════════════════════════════════════"
echo " T03 verification: PASS=$PASS WARN=$WARN FAIL=$FAIL"
echo "════════════════════════════════════════════════"
if [[ "$FAIL" -gt 0 ]]; then
echo " Result: INCOMPLETE — resolve FAIL items before proceeding to T04"
exit 1
elif [[ "$WARN" -gt 0 ]]; then
echo " Result: PARTIAL — T03 core done; WARN items should be addressed before production"
exit 0
else
echo " Result: COMPLETE — T03 done-criteria met"
exit 0
fi