Files
net-kingdom/sso-mfa/k8s/verify-t04.sh
Bernd Worsch bee0936d5d docs(sso-mfa): fix stale Keycloak refs and add T04 apply section to WORKPLAN
- README.md: ipAllowList → ipWhiteList (match Traefik v2 fix)
- verify-t04.sh: update success message (Keycloak → LLDAP+Authelia+KeyCape)
- WORKPLAN.md: add full T04 section with deliverables, pending steps, done-criteria

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 07:33:47 +00:00

218 lines
9.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# verify-t04.sh — verify NK-WP-0001-T04 done-criteria
#
# Checks:
# 1. privacyIDEA pod is Running in the mfa namespace
# 2. privacyidea Service exists
# 3. Traefik Middlewares exist
# 4. Ingress resources exist with correct hostnames
# 5. TLS certificates issued by cert-manager
# 6. Required K8s Secrets are present
# 7. PVCs are Bound
# 8. Enckey and auditkey Secrets present (from enckey-bootstrap.sh)
#
# Usage:
# chmod +x verify-t04.sh
# ./verify-t04.sh
set -euo pipefail
NAMESPACE="mfa"
PASS=0
FAIL=0
WARN=0
pass() { echo " [PASS] $1"; ((PASS++)); }
fail() { echo " [FAIL] $1"; ((FAIL++)); }
warn() { echo " [WARN] $1"; ((WARN++)); }
section() { echo ""; echo "── $1 ──────────────────────────────────────"; }
# ── 1. privacyIDEA pod ────────────────────────────────────────────────────────
section "1. privacyIDEA pod (namespace: $NAMESPACE)"
PI_POD=$(kubectl get pod -n "$NAMESPACE" \
-l app.kubernetes.io/name=privacyidea \
--field-selector=status.phase=Running \
-o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
if [[ -n "$PI_POD" ]]; then
pass "Pod Running: $PI_POD"
READY=$(kubectl get pod -n "$NAMESPACE" "$PI_POD" \
-o jsonpath='{.status.containerStatuses[0].ready}' 2>/dev/null || echo "false")
if [[ "$READY" == "true" ]]; then
pass "Pod readiness probe passing"
else
fail "Pod is Running but not Ready (probe failing — check logs)"
fi
else
PENDING=$(kubectl get pod -n "$NAMESPACE" \
-l app.kubernetes.io/name=privacyidea \
-o name 2>/dev/null | wc -l || echo 0)
if [[ "$PENDING" -gt 0 ]]; then
fail "privacyIDEA pod(s) exist but none are Running (check kubectl describe pod -n $NAMESPACE)"
else
fail "No privacyIDEA pods found in namespace $NAMESPACE — apply deployment.yaml"
fi
fi
# ── 2. Service ────────────────────────────────────────────────────────────────
section "2. Service"
if kubectl get service privacyidea -n "$NAMESPACE" &>/dev/null; then
pass "Service privacyidea exists"
PORT=$(kubectl get service privacyidea -n "$NAMESPACE" \
-o jsonpath='{.spec.ports[0].port}' 2>/dev/null || echo "")
if [[ "$PORT" == "8080" ]]; then
pass "Service port: 8080"
else
warn "Service port is $PORT (expected 8080 — check container port and netpol)"
fi
else
fail "Service privacyidea not found in namespace $NAMESPACE"
fi
# ── 3. Traefik Middlewares ────────────────────────────────────────────────────
section "3. Traefik Middlewares"
for mw in privacyidea-rate-limit privacyidea-admin-allowlist; do
if kubectl get middleware "$mw" -n "$NAMESPACE" &>/dev/null; then
pass "Middleware $mw exists"
else
fail "Middleware $mw not found — apply middleware.yaml"
fi
done
# ── 4. Ingress resources ──────────────────────────────────────────────────────
section "4. Ingress resources"
for ing in privacyidea privacyidea-admin privacyidea-account; do
if kubectl get ingress "$ing" -n "$NAMESPACE" &>/dev/null; then
pass "Ingress $ing exists"
else
fail "Ingress $ing not found — apply ingress.yaml"
fi
done
# Verify hostnames in the main Ingress
PI_HOST=$(kubectl get ingress privacyidea -n "$NAMESPACE" \
-o jsonpath='{.spec.rules[0].host}' 2>/dev/null || echo "")
if [[ "$PI_HOST" == "pink.coulomb.social" ]]; then
pass "Ingress host: pink.coulomb.social"
else
fail "Ingress host is '$PI_HOST' (expected pink.coulomb.social)"
fi
ACCOUNT_HOST=$(kubectl get ingress privacyidea-account -n "$NAMESPACE" \
-o jsonpath='{.spec.rules[0].host}' 2>/dev/null || echo "")
if [[ "$ACCOUNT_HOST" == "pink-account.coulomb.social" ]]; then
pass "Self-service host: pink-account.coulomb.social"
else
fail "Self-service host is '$ACCOUNT_HOST' (expected pink-account.coulomb.social)"
fi
# ── 5. TLS certificates ───────────────────────────────────────────────────────
section "5. TLS certificates"
for cert_secret in pink-tls pink-account-tls; do
if kubectl get secret "$cert_secret" -n "$NAMESPACE" &>/dev/null; then
# Check cert-manager Ready condition
CERT_NAME="${cert_secret%-tls}"
CERT_READY=$(kubectl get certificate "$CERT_NAME" -n "$NAMESPACE" \
-o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || echo "")
if [[ "$CERT_READY" == "True" ]]; then
pass "Certificate $cert_name is Ready (TLS secret $cert_secret exists)"
else
warn "TLS secret $cert_secret exists but certificate status is not Ready (DNS propagation pending?)"
fi
else
warn "TLS secret $cert_secret not yet issued (cert-manager pending — check DNS and ACME)"
fi
done
# ── 6. K8s Secrets ────────────────────────────────────────────────────────────
section "6. K8s Secrets (namespace: $NAMESPACE)"
for secret in privacyidea-config; do
if kubectl get secret "$secret" -n "$NAMESPACE" &>/dev/null; then
pass "Secret $secret exists"
else
fail "Secret $secret not found — run create-secrets.sh"
fi
done
# DR secrets — created by enckey-bootstrap.sh (may not exist yet on first run)
for secret in privacyidea-enckey privacyidea-auditkeys privacyidea-trigger-admin; do
if kubectl get secret "$secret" -n "$NAMESPACE" &>/dev/null; then
pass "Secret $secret exists"
else
warn "Secret $secret not found — run enckey-bootstrap.sh / bootstrap-admin.sh"
fi
done
# ── 7. PVCs ───────────────────────────────────────────────────────────────────
section "7. PersistentVolumeClaims"
for pvc in privacyidea-data privacyidea-logs; do
STATUS=$(kubectl get pvc "$pvc" -n "$NAMESPACE" \
-o jsonpath='{.status.phase}' 2>/dev/null || echo "not found")
if [[ "$STATUS" == "Bound" ]]; then
pass "PVC $pvc: Bound"
elif [[ "$STATUS" == "not found" ]]; then
fail "PVC $pvc not found — apply pvc.yaml"
else
fail "PVC $pvc status: $STATUS (expected Bound)"
fi
done
# ── 8. Key material check ─────────────────────────────────────────────────────
section "8. Key material (inside pod)"
if [[ -n "$PI_POD" ]]; then
if kubectl exec -n "$NAMESPACE" "$PI_POD" -- test -f /etc/privacyidea/enckey 2>/dev/null; then
pass "enckey present in pod"
else
warn "enckey not found in pod — run enckey-bootstrap.sh (or wait for PI to generate on first DB init)"
fi
if kubectl exec -n "$NAMESPACE" "$PI_POD" -- test -f /etc/privacyidea/private.pem 2>/dev/null; then
pass "audit private.pem present in pod"
else
warn "audit private.pem not found — run enckey-bootstrap.sh"
fi
if kubectl exec -n "$NAMESPACE" "$PI_POD" -- \
pi-manage admin list 2>/dev/null | grep -q "pi-admin"; then
pass "Admin user pi-admin exists"
else
warn "pi-admin not found — run bootstrap-admin.sh"
fi
if kubectl exec -n "$NAMESPACE" "$PI_POD" -- \
pi-manage admin list 2>/dev/null | grep -q "trigger-admin"; then
pass "Admin user trigger-admin exists"
else
warn "trigger-admin not found — run bootstrap-admin.sh"
fi
else
warn "Skipping key material checks — no running pod"
fi
# ── Summary ───────────────────────────────────────────────────────────────────
echo ""
echo "════════════════════════════════════════════════════════════"
echo " T04 verification: PASS=$PASS WARN=$WARN FAIL=$FAIL"
echo "════════════════════════════════════════════════════════════"
if [[ "$FAIL" -gt 0 ]]; then
echo " Result: INCOMPLETE — resolve FAIL items before proceeding to T05"
exit 1
elif [[ "$WARN" -gt 0 ]]; then
echo " Result: PARTIAL — T04 core is up; WARN items should be resolved before T05"
exit 0
else
echo " Result: COMPLETE — T04 done-criteria met; proceed to T05 (SSO core: LLDAP+Authelia+KeyCape)"
exit 0
fi