215 lines
6.7 KiB
Bash
215 lines
6.7 KiB
Bash
|
|
#!/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 "$@"
|