#!/bin/bash set -euo pipefail # Keycloak Deployment Script # This script deploys and configures Keycloak for Sankofa Phoenix SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color log_info() { echo -e "${GREEN}[INFO]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } # Check prerequisites check_prerequisites() { log_info "Checking prerequisites..." if ! command -v kubectl &> /dev/null; then log_error "kubectl is not installed" exit 1 fi if ! command -v helm &> /dev/null; then log_error "helm is not installed" exit 1 fi if ! kubectl cluster-info &> /dev/null; then log_error "Cannot connect to Kubernetes cluster" exit 1 fi log_info "Prerequisites check passed" } # Generate random password generate_password() { openssl rand -base64 32 | tr -d "=+/" | cut -c1-25 } # Deploy PostgreSQL for Keycloak deploy_postgres() { log_info "Deploying PostgreSQL for Keycloak..." POSTGRES_PASSWORD="${KEYCLOAK_DB_PASSWORD:-$(generate_password)}" kubectl create namespace keycloak --dry-run=client -o yaml | kubectl apply -f - kubectl create secret generic keycloak-db-credentials \ --from-literal=username=keycloak \ --from-literal=password="$POSTGRES_PASSWORD" \ --namespace=keycloak \ --dry-run=client -o yaml | kubectl apply -f - log_info "PostgreSQL secret created" log_warn "PostgreSQL password saved in secret: keycloak-db-credentials" } # Deploy Keycloak deploy_keycloak() { log_info "Deploying Keycloak..." ADMIN_PASSWORD="${KEYCLOAK_ADMIN_PASSWORD:-$(generate_password)}" kubectl create secret generic keycloak-credentials \ --from-literal=username=admin \ --from-literal=password="$ADMIN_PASSWORD" \ --namespace=keycloak \ --dry-run=client -o yaml | kubectl apply -f - log_info "Keycloak admin credentials created" log_warn "Admin password saved in secret: keycloak-credentials" # Apply Keycloak manifests kubectl apply -f "$PROJECT_ROOT/gitops/apps/keycloak/namespace.yaml" kubectl apply -f "$PROJECT_ROOT/gitops/apps/keycloak/postgres.yaml" kubectl apply -f "$PROJECT_ROOT/gitops/apps/keycloak/deployment.yaml" log_info "Waiting for Keycloak to be ready..." kubectl wait --for=condition=available --timeout=300s \ deployment/keycloak -n keycloak || { log_error "Keycloak deployment failed" kubectl logs -n keycloak deployment/keycloak --tail=50 exit 1 } log_info "Keycloak deployed successfully" } # Configure Keycloak clients configure_clients() { log_info "Configuring Keycloak clients..." # Wait for Keycloak to be fully ready log_info "Waiting for Keycloak API to be ready..." for i in {1..30}; do if kubectl exec -n keycloak deployment/keycloak -- \ curl -s http://localhost:8080/health/ready &>/dev/null; then break fi sleep 2 done # Get admin credentials ADMIN_USER=$(kubectl get secret keycloak-credentials -n keycloak -o jsonpath='{.data.username}' | base64 -d) ADMIN_PASS=$(kubectl get secret keycloak-credentials -n keycloak -o jsonpath='{.data.password}' | base64 -d) # Port-forward for client configuration log_info "Configuring clients via API..." kubectl port-forward -n keycloak svc/keycloak 8080:8080 & PF_PID=$! sleep 3 # Get admin token TOKEN=$(curl -s -X POST "http://localhost:8080/realms/master/protocol/openid-connect/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=$ADMIN_USER" \ -d "password=$ADMIN_PASS" \ -d "grant_type=password" \ -d "client_id=admin-cli" | jq -r '.access_token') if [ "$TOKEN" == "null" ] || [ -z "$TOKEN" ]; then log_error "Failed to get admin token" kill $PF_PID 2>/dev/null || true exit 1 fi # Generate client secrets API_SECRET="${SANKOFA_API_CLIENT_SECRET:-$(generate_password)}" PORTAL_SECRET="${PORTAL_CLIENT_SECRET:-$(generate_password)}" # Create API client log_info "Creating API client..." curl -s -X POST "http://localhost:8080/admin/realms/master/clients" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"clientId\": \"sankofa-api\", \"name\": \"Sankofa API Client\", \"enabled\": true, \"clientAuthenticatorType\": \"client-secret\", \"secret\": \"$API_SECRET\", \"standardFlowEnabled\": false, \"serviceAccountsEnabled\": true, \"publicClient\": false, \"protocol\": \"openid-connect\" }" > /dev/null # Create Portal client log_info "Creating Portal client..." curl -s -X POST "http://localhost:8080/admin/realms/master/clients" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"clientId\": \"portal-client\", \"name\": \"Sankofa Portal Client\", \"enabled\": true, \"clientAuthenticatorType\": \"client-secret\", \"secret\": \"$PORTAL_SECRET\", \"standardFlowEnabled\": true, \"directAccessGrantsEnabled\": true, \"publicClient\": false, \"protocol\": \"openid-connect\", \"redirectUris\": [ \"http://localhost:3000/*\", \"https://portal.sankofa.nexus/*\" ], \"webOrigins\": [\"+\"] }" > /dev/null # Save secrets kubectl create secret generic keycloak-client-secrets \ --from-literal=api-client-secret="$API_SECRET" \ --from-literal=portal-client-secret="$PORTAL_SECRET" \ --namespace=keycloak \ --dry-run=client -o yaml | kubectl apply -f - kill $PF_PID 2>/dev/null || true log_info "Keycloak clients configured successfully" log_warn "Client secrets saved in secret: keycloak-client-secrets" } # Main deployment main() { log_info "Starting Keycloak deployment..." check_prerequisites deploy_postgres deploy_keycloak configure_clients log_info "Keycloak deployment completed!" log_info "Access Keycloak at: https://keycloak.sankofa.nexus" log_warn "Admin credentials are in secret: keycloak-credentials" log_warn "Client secrets are in secret: keycloak-client-secrets" } # Run main function main "$@"