#!/usr/bin/env bash # bootstrap-realm.sh — Phase 1 Keycloak bootstrap for T05 # # Runs AFTER the Keycloak pod is Running and Ready (verify with verify-t05.sh). # # Actions (T05 scope): # 1. Authenticate to Keycloak admin REST API via kcadm.sh in the pod # 2. Harden master realm: brute-force protection, token lifetimes, SSL required # 3. Create the net-kingdom application realm # 4. Enable realm-level brute-force protection and SSL # # Out of scope for T05 (handled in T06): # - privacyIDEA authentication flow configuration # - User federation / LDAP resolver setup # - Per-client OIDC settings # - Break-glass admin account (T07) # # Usage: # chmod +x bootstrap-realm.sh # ./bootstrap-realm.sh [--realm-name NAME] # # Options: # --realm-name NAME Name for the application realm (default: net-kingdom) # # Prerequisites: # - kubectl configured with cluster access # - Keycloak pod Running+Ready in the sso namespace # - KC_ADMIN_PASSWORD available (prompted interactively if not set as env var) set -euo pipefail NAMESPACE="sso" REALM_NAME="net-kingdom" while [[ $# -gt 0 ]]; do case "$1" in --realm-name) REALM_NAME="$2"; shift 2 ;; *) echo "Unknown option: $1" >&2; exit 1 ;; esac done # ── Find the running Keycloak pod ───────────────────────────────────────────── KC_POD=$(kubectl get pod -n "$NAMESPACE" \ -l app.kubernetes.io/name=keycloak \ --field-selector=status.phase=Running \ -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "") if [[ -z "$KC_POD" ]]; then echo "ERROR: No running Keycloak pod found in namespace $NAMESPACE." >&2 echo "Run verify-t05.sh to diagnose." >&2 exit 1 fi echo "Using pod: $KC_POD" # ── Admin password ──────────────────────────────────────────────────────────── if [[ -z "${KC_ADMIN_PASSWORD:-}" ]]; then echo -n "Keycloak admin password (KC_ADMIN_PASSWORD): " read -rs KC_ADMIN_PASSWORD echo "" fi if [[ -z "$KC_ADMIN_PASSWORD" ]]; then echo "ERROR: KC_ADMIN_PASSWORD is required." >&2 exit 1 fi # ── Helper: run kcadm.sh inside the pod ────────────────────────────────────── kcadm() { kubectl exec -n "$NAMESPACE" "$KC_POD" -- \ /opt/keycloak/bin/kcadm.sh "$@" } # ── 1. Authenticate ─────────────────────────────────────────────────────────── echo "" echo "── Authenticating ───────────────────────────────────────────────────────" kcadm config credentials \ --server http://localhost:8080 \ --realm master \ --user admin \ --password "$KC_ADMIN_PASSWORD" echo " Authenticated as admin@master" # ── 2. Harden master realm ──────────────────────────────────────────────────── echo "" echo "── Hardening master realm ───────────────────────────────────────────────" # Require SSL for all connections to master realm (external access requires HTTPS). kcadm update realms/master \ --set 'sslRequired=external' echo " sslRequired=external (master)" # Brute-force protection: lock after 5 failures for 5 minutes. kcadm update realms/master \ --set 'bruteForceProtected=true' \ --set 'failureFactor=5' \ --set 'waitIncrementSeconds=60' \ --set 'maxFailureWaitSeconds=900' \ --set 'minimumQuickLoginWaitSeconds=60' echo " Brute-force protection enabled (master)" # Shorten access token lifetime for security. kcadm update realms/master \ --set 'accessTokenLifespan=300' # 5 min echo " Access token lifetime: 300s (master)" # ── 3. Create the net-kingdom application realm ─────────────────────────────── echo "" echo "── Creating realm: $REALM_NAME ─────────────────────────────────────────" if kcadm get realms/"$REALM_NAME" &>/dev/null; then echo " Realm $REALM_NAME already exists — skipping creation." else kcadm create realms \ --set "realm=$REALM_NAME" \ --set 'enabled=true' \ --set 'displayName=net-kingdom' \ --set 'sslRequired=external' \ --set 'registrationAllowed=false' \ --set 'bruteForceProtected=true' \ --set 'failureFactor=5' \ --set 'waitIncrementSeconds=60' \ --set 'maxFailureWaitSeconds=900' \ --set 'minimumQuickLoginWaitSeconds=60' \ --set 'accessTokenLifespan=300' \ --set 'refreshTokenMaxReuse=0' \ --set 'revokeRefreshToken=true' echo " Realm $REALM_NAME created." fi # ── 4. Summary ──────────────────────────────────────────────────────────────── echo "" echo "════════════════════════════════════════════════════════════" echo " T05 realm bootstrap complete." echo "════════════════════════════════════════════════════════════" echo "" echo "What was done:" echo " - master realm: SSL required, brute-force protection, short token lifetimes" echo " - realm '$REALM_NAME' created with equivalent hardening" echo "" echo "Next steps (T06):" echo " 1. Log in to https://kc.coulomb.social/admin with the bootstrap admin." echo " Immediately create a permanent admin account, then delete/disable" echo " the bootstrap 'admin' account (or rotate KC_BOOTSTRAP_ADMIN_PASSWORD)." echo " 2. In realm '$REALM_NAME':" echo " - Configure the privacyIDEA authentication flow (see T06 README)." echo " - Set privacyIDEA base URL: https://pink.coulomb.social" echo " - Set trigger-admin credentials from the privacyidea-trigger-admin Secret." echo " 3. Run verify-t05.sh to confirm all T05 done-criteria."