#!/usr/bin/env bash # Setup Keycloak for Sankofa on r630-01 # VMID: 7802, IP: 10.160.0.12 set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/.env.r630-01" 2>/dev/null || true # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[✓]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } # Configuration PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.11}" VMID="${VMID_SANKOFA_KEYCLOAK:-7802}" CONTAINER_IP="${SANKOFA_KEYCLOAK_IP:-10.160.0.12}" KEYCLOAK_ADMIN_USERNAME="${KEYCLOAK_ADMIN_USERNAME:-admin}" KEYCLOAK_ADMIN_PASSWORD="${KEYCLOAK_ADMIN_PASSWORD:-$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-24)}" KEYCLOAK_REALM="${KEYCLOAK_REALM:-master}" KEYCLOAK_CLIENT_ID_API="${KEYCLOAK_CLIENT_ID_API:-sankofa-api}" KEYCLOAK_CLIENT_ID_PORTAL="${KEYCLOAK_CLIENT_ID_PORTAL:-portal-client}" KEYCLOAK_CLIENT_SECRET_API="${KEYCLOAK_CLIENT_SECRET_API:-$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32)}" KEYCLOAK_CLIENT_SECRET_PORTAL="${KEYCLOAK_CLIENT_SECRET_PORTAL:-$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32)}" DB_HOST="${SANKOFA_POSTGRES_IP:-10.160.0.13}" DB_NAME="${DB_NAME:-keycloak}" DB_USER="${DB_USER:-keycloak}" DB_PASSWORD="${DB_PASSWORD:-$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-24)}" # SSH function ssh_r630_01() { ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 root@"$PROXMOX_HOST" "$@" } # Execute command in container exec_container() { ssh_r630_01 "pct exec $VMID -- $*" } main() { echo "" log_info "=========================================" log_info "Keycloak Setup for Sankofa" log_info "=========================================" echo "" log_info "Container VMID: $VMID" log_info "Container IP: $CONTAINER_IP" log_info "Realm: $KEYCLOAK_REALM" echo "" # Check if container exists and is running log_info "Checking container status..." if ! ssh_r630_01 "pct status $VMID >/dev/null 2>&1"; then log_error "Container $VMID does not exist or is not running" exit 1 fi local status=$(ssh_r630_01 "pct status $VMID" 2>/dev/null | awk '{print $2}' || echo "stopped") if [[ "$status" != "running" ]]; then log_info "Starting container $VMID..." ssh_r630_01 "pct start $VMID" sleep 5 fi log_success "Container is running" echo "" # Install Java and PostgreSQL client log_info "Installing Java and dependencies..." exec_container bash -c "export DEBIAN_FRONTEND=noninteractive && \ apt-get update -qq && \ apt-get install -y -qq openjdk-21-jdk wget curl unzip" # Set JAVA_HOME exec_container bash -c "echo 'export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64' >> /etc/profile" log_success "Java installed" echo "" # Create Keycloak database on PostgreSQL log_info "Creating Keycloak database on PostgreSQL..." ssh_r630_01 "pct exec ${VMID_SANKOFA_POSTGRES:-7803} -- bash -c \"sudo -u postgres psql << 'EOF' CREATE USER $DB_USER WITH PASSWORD '$DB_PASSWORD'; CREATE DATABASE $DB_NAME OWNER $DB_USER ENCODING 'UTF8'; GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER; EOF\"" log_success "Keycloak database created" echo "" # Download and install Keycloak log_info "Downloading Keycloak..." exec_container bash -c "cd /opt && \ wget -q https://github.com/keycloak/keycloak/releases/download/24.0.0/keycloak-24.0.0.tar.gz && \ tar -xzf keycloak-24.0.0.tar.gz && \ mv keycloak-24.0.0 keycloak && \ rm keycloak-24.0.0.tar.gz && \ chmod +x keycloak/bin/kc.sh" log_success "Keycloak downloaded" echo "" # Build Keycloak log_info "Building Keycloak (this may take a few minutes)..." exec_container bash -c "cd /opt/keycloak && \ export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 && \ ./bin/kc.sh build --db postgres" log_success "Keycloak built" echo "" # Create systemd service log_info "Creating Keycloak systemd service..." exec_container bash -c "cat > /etc/systemd/system/keycloak.service << 'EOF' [Unit] Description=Keycloak Authorization Server After=network.target [Service] Type=idle User=root WorkingDirectory=/opt/keycloak Environment=\"JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64\" Environment=\"KC_DB=postgres\" Environment=\"KC_DB_URL_HOST=$DB_HOST\" Environment=\"KC_DB_URL_DATABASE=$DB_NAME\" Environment=\"KC_DB_USERNAME=$DB_USER\" Environment=\"KC_DB_PASSWORD=$DB_PASSWORD\" Environment=\"KC_HTTP_ENABLED=true\" Environment=\"KC_HOSTNAME_STRICT=false\" Environment=\"KC_HOSTNAME_PORT=8080\" ExecStart=/opt/keycloak/bin/kc.sh start --optimized ExecStop=/bin/kill -TERM \$MAINPID Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF" # Start Keycloak log_info "Starting Keycloak service..." exec_container bash -c "systemctl daemon-reload && \ systemctl enable keycloak && \ systemctl start keycloak" log_info "Waiting for Keycloak to start (this may take 1-2 minutes)..." sleep 30 # Wait for Keycloak to be ready local max_attempts=30 local attempt=0 while [ $attempt -lt $max_attempts ]; do if exec_container bash -c "curl -s -f http://localhost:8080/health/ready >/dev/null 2>&1"; then log_success "Keycloak is ready" break fi attempt=$((attempt + 1)) log_info "Waiting for Keycloak... ($attempt/$max_attempts)" sleep 5 done if [ $attempt -eq $max_attempts ]; then log_error "Keycloak failed to start" exit 1 fi echo "" # Create admin user log_info "Creating admin user..." exec_container bash -c "cd /opt/keycloak && \ export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 && \ ./bin/kc.sh config credentials --server http://localhost:8080 --realm master --user admin --password admin 2>/dev/null || \ ./bin/kc.sh config credentials --server http://localhost:8080 --realm master --user $KEYCLOAK_ADMIN_USERNAME --password $KEYCLOAK_ADMIN_PASSWORD" # Get admin token and create clients log_info "Creating API and Portal clients..." # Note: This requires Keycloak Admin REST API # We'll create a script that can be run after Keycloak is fully started exec_container bash -c "cat > /root/create-clients.sh << 'SCRIPT' #!/bin/bash export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 cd /opt/keycloak # 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=$KEYCLOAK_ADMIN_USERNAME\" \\ -d \"password=$KEYCLOAK_ADMIN_PASSWORD\" \\ -d \"grant_type=password\" \\ -d \"client_id=admin-cli\" | jq -r '.access_token') # Create sankofa-api client curl -s -X POST \"http://localhost:8080/admin/realms/master/clients\" \\ -H \"Authorization: Bearer \$TOKEN\" \\ -H \"Content-Type: application/json\" \\ -d '{ \"clientId\": \"$KEYCLOAK_CLIENT_ID_API\", \"enabled\": true, \"clientAuthenticatorType\": \"client-secret\", \"secret\": \"$KEYCLOAK_CLIENT_SECRET_API\", \"protocol\": \"openid-connect\", \"publicClient\": false, \"standardFlowEnabled\": true, \"directAccessGrantsEnabled\": true, \"serviceAccountsEnabled\": true }' > /dev/null # Create portal-client curl -s -X POST \"http://localhost:8080/admin/realms/master/clients\" \\ -H \"Authorization: Bearer \$TOKEN\" \\ -H \"Content-Type: application/json\" \\ -d '{ \"clientId\": \"$KEYCLOAK_CLIENT_ID_PORTAL\", \"enabled\": true, \"clientAuthenticatorType\": \"client-secret\", \"secret\": \"$KEYCLOAK_CLIENT_SECRET_PORTAL\", \"protocol\": \"openid-connect\", \"publicClient\": false, \"standardFlowEnabled\": true, \"directAccessGrantsEnabled\": true }' > /dev/null echo \"Clients created successfully\" SCRIPT chmod +x /root/create-clients.sh" # Wait a bit more and create clients sleep 10 exec_container bash -c "/root/create-clients.sh" || log_warn "Client creation may need to be done manually via web UI" log_success "Keycloak setup complete" echo "" # Summary log_success "=========================================" log_success "Keycloak Setup Complete" log_success "=========================================" echo "" log_info "Keycloak Configuration:" echo " URL: http://$CONTAINER_IP:8080" echo " Admin URL: http://$CONTAINER_IP:8080/admin" echo " Admin Username: $KEYCLOAK_ADMIN_USERNAME" echo " Admin Password: $KEYCLOAK_ADMIN_PASSWORD" echo " Realm: $KEYCLOAK_REALM" echo "" log_info "Client Secrets:" echo " API Client Secret: $KEYCLOAK_CLIENT_SECRET_API" echo " Portal Client Secret: $KEYCLOAK_CLIENT_SECRET_PORTAL" echo "" log_info "Next steps:" echo " 1. Update .env.r630-01 with Keycloak credentials" echo " 2. Verify clients in Keycloak admin console" echo " 3. Run: ./scripts/deploy-api-r630-01.sh" echo "" } main "$@"