generated from coulomb/repo-seed
Deploy privacyIDEA (MFA core) in the mfa namespace: - pvc.yaml: privacyidea-data (5Gi) and privacyidea-logs (2Gi) - configmap.yaml: pi.cfg reading secrets from env vars - deployment.yaml: Deployment + ClusterIP Service (port 8080) - middleware.yaml: Traefik RateLimit + admin IP AllowList - ingress.yaml: pink.coulomb.social (portal + admin), pink-account.coulomb.social (self-service) - create-secrets.sh: creates privacyidea-config Secret - enckey-bootstrap.sh: post-deploy key extraction + DR Secrets - bootstrap-admin.sh: pi-admin, trigger-admin, privacyidea-trigger-admin Secret - verify-t04.sh: 8-section done-criteria checker Config points CP-NK-002 (pink.coulomb.social) and CP-NK-003 (pink-account.coulomb.social) registered in CONFIG.md. pink = PrivacyIDEA Net Knights (project mnemonic). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
163 lines
7.2 KiB
Bash
Executable File
163 lines
7.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# bootstrap-admin.sh — create pi-admin and trigger-admin in privacyIDEA
|
|
#
|
|
# Run AFTER enckey-bootstrap.sh (key material must exist before admin setup).
|
|
#
|
|
# What it does:
|
|
# 1. Creates pi-admin — full admin (single-credential bootstrap moment).
|
|
# 2. Creates trigger-admin — limited admin for Keycloak's triggerchallenge calls.
|
|
# 3. Creates the trigger-admin policy (triggerchallenge right only, via REST API).
|
|
# 4. Prints instructions to immediately enroll MFA for pi-admin.
|
|
#
|
|
# Usage:
|
|
# ./bootstrap-admin.sh [secrets-dir] [pi-url]
|
|
#
|
|
# <secrets-dir> default: ../../bootstrap/secrets
|
|
# <pi-url> default: https://pink.coulomb.social
|
|
#
|
|
# The script uses the in-cluster kubectl exec path for creating admin users
|
|
# (avoids the need for a network route to pink.coulomb.social during bootstrap).
|
|
# The trigger-admin policy is created via REST API, so the URL must be reachable.
|
|
|
|
set -euo pipefail
|
|
|
|
NAMESPACE="mfa"
|
|
SECRETS_DIR="${1:-../../bootstrap/secrets}"
|
|
PI_URL="${2:-https://pink.coulomb.social}"
|
|
PI_ENV="$SECRETS_DIR/privacyidea/secrets.env"
|
|
|
|
if [[ ! -f "$PI_ENV" ]]; then
|
|
echo "ERROR: $PI_ENV not found — run gen-secrets.sh first." >&2
|
|
exit 1
|
|
fi
|
|
|
|
PI_ADMIN_PASS=$(bash -c "source '$PI_ENV' 2>/dev/null; echo \$PI_ADMIN_PASSWORD")
|
|
if [[ -z "$PI_ADMIN_PASS" ]]; then
|
|
echo "ERROR: PI_ADMIN_PASSWORD not found in $PI_ENV" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# trigger-admin gets its own random password (generated here, stored in KeePassXC).
|
|
TRIGGER_PASS=$(openssl rand -base64 32 | tr -d '\n/+=' | head -c 40)
|
|
|
|
# ── 1. Find running pod ───────────────────────────────────────────────────────
|
|
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 [[ -z "$PI_POD" ]]; then
|
|
echo "ERROR: no Running privacyIDEA pod found in namespace '$NAMESPACE'." >&2
|
|
exit 1
|
|
fi
|
|
echo "Using pod: $PI_POD"
|
|
|
|
# ── 2. Create pi-admin (full admin) ──────────────────────────────────────────
|
|
echo ""
|
|
echo "Creating admin: pi-admin"
|
|
if kubectl exec -n "$NAMESPACE" "$PI_POD" -- \
|
|
pi-manage admin add pi-admin --password "$PI_ADMIN_PASS" 2>&1 | grep -q "already exist"; then
|
|
echo " pi-admin already exists — skipping."
|
|
else
|
|
echo " pi-admin created."
|
|
fi
|
|
|
|
# ── 3. Create trigger-admin (limited admin) ───────────────────────────────────
|
|
echo ""
|
|
echo "Creating admin: trigger-admin"
|
|
if kubectl exec -n "$NAMESPACE" "$PI_POD" -- \
|
|
pi-manage admin add trigger-admin --password "$TRIGGER_PASS" 2>&1 | grep -q "already exist"; then
|
|
echo " trigger-admin already exists — skipping password update."
|
|
echo " WARNING: if you need to rotate trigger-admin's password, do so manually."
|
|
else
|
|
echo " trigger-admin created."
|
|
fi
|
|
|
|
# ── 4. Create K8s Secret for trigger-admin credentials ───────────────────────
|
|
# Keycloak's privacyIDEA Provider reads the trigger-admin credentials from
|
|
# a K8s Secret that is referenced in the Keycloak realm configuration (T05/T06).
|
|
echo ""
|
|
echo "Creating K8s Secret: privacyidea-trigger-admin (namespace: mfa)"
|
|
kubectl create secret generic privacyidea-trigger-admin \
|
|
--namespace=mfa \
|
|
--from-literal=username=trigger-admin \
|
|
--from-literal=password="$TRIGGER_PASS" \
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
|
|
echo " Done."
|
|
|
|
# ── 5. Create trigger-admin policy via REST API ───────────────────────────────
|
|
# This restricts trigger-admin to the triggerchallenge action only.
|
|
# Requires pink.coulomb.social to be reachable.
|
|
echo ""
|
|
echo "Creating trigger-admin policy via REST API ($PI_URL)..."
|
|
echo " (Skip this step if $PI_URL is not yet reachable — do it via the WebUI instead.)"
|
|
echo ""
|
|
|
|
# Authenticate as pi-admin to get a bearer token.
|
|
AUTH_RESPONSE=$(curl -sf -X POST "$PI_URL/auth" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"username\":\"pi-admin\",\"password\":\"$PI_ADMIN_PASS\"}" || echo "CURL_FAILED")
|
|
|
|
if [[ "$AUTH_RESPONSE" == "CURL_FAILED" ]]; then
|
|
echo " Could not reach $PI_URL — skipping REST policy creation."
|
|
echo " Create the trigger-admin policy manually (see README.md — Post-deploy steps)."
|
|
else
|
|
PI_TOKEN=$(echo "$AUTH_RESPONSE" | python3 -c \
|
|
"import sys,json; print(json.load(sys.stdin)['result']['value']['token'])" 2>/dev/null || echo "")
|
|
|
|
if [[ -z "$PI_TOKEN" ]]; then
|
|
echo " ERROR: could not extract auth token from response." >&2
|
|
echo " Create the trigger-admin policy manually (see README.md)." >&2
|
|
else
|
|
# Create policy: trigger-admin can only call triggerchallenge.
|
|
POLICY_RESP=$(curl -sf -X POST "$PI_URL/policy/trigger-admin-rights" \
|
|
-H "Authorization: $PI_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"scope": "admin",
|
|
"action": "triggerchallenge",
|
|
"adminrealm": "*",
|
|
"adminuser": "trigger-admin",
|
|
"realm": "*",
|
|
"priority": 1
|
|
}' || echo "CURL_FAILED")
|
|
|
|
if [[ "$POLICY_RESP" == "CURL_FAILED" ]]; then
|
|
echo " WARNING: policy creation request failed." >&2
|
|
echo " Create the trigger-admin policy manually (see README.md)." >&2
|
|
else
|
|
echo " Policy 'trigger-admin-rights' created."
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# ── 6. Summary ────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "════════════════════════════════════════════════════════════"
|
|
echo " Admin bootstrap complete."
|
|
echo "════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
echo "CRITICAL — Do these steps NOW:"
|
|
echo ""
|
|
echo " 1. Store trigger-admin password in KeePassXC:"
|
|
echo " KeePassXC group: net-kingdom/privacyIDEA"
|
|
echo " Entry: trigger-admin → username=trigger-admin password=<below>"
|
|
echo " trigger-admin password: $TRIGGER_PASS"
|
|
echo " Then shred this terminal history."
|
|
echo ""
|
|
echo " 2. Log in to the privacyIDEA WebUI as pi-admin:"
|
|
echo " $PI_URL"
|
|
echo " Enroll MFA for pi-admin IMMEDIATELY (TOTP or hardware token)."
|
|
echo " Until MFA is enrolled, pi-admin has only password authentication."
|
|
echo ""
|
|
echo " 3. Verify the trigger-admin policy:"
|
|
echo " WebUI → Config → Policies → trigger-admin-rights"
|
|
echo " Scope: admin Action: triggerchallenge AdminUser: trigger-admin"
|
|
echo " If it was not created automatically, create it here."
|
|
echo ""
|
|
echo " 4. Test admin MFA:"
|
|
echo " Log out, log back in as pi-admin — MFA challenge must appear."
|
|
echo ""
|
|
echo "Next step: run ../verify-t04.sh"
|