Keycloak Rebuild Runbook
Complete rebuild of keycloak-01 from scratch. Use when VM is corrupted or Keycloak needs fresh deployment.
Executive Summary
| Item | Value |
|---|---|
VM |
keycloak-01 |
Hypervisor |
kvm-01 |
IP |
10.50.1.80 |
OS |
Fedora Cloud 43 |
Service |
Keycloak (Docker Compose) |
Purpose |
SAML/OIDC IdP for ISE admin, dashboards |
Prerequisites
-
SSH access to kvm-01
-
Vault SSH cert valid (
vault-ssh-sign) -
dsec credentials loaded (
dsource d000 dev/identity) -
Fedora Cloud image available on kvm-01
Phase 0: Discovery
Determine current state before proceeding.
0.1 Check VM State
ssh kvm-01 "sudo virsh list --all | grep -i keycloak"
| Output | Action |
|---|---|
|
VM running - check container: 0.2 Check Container (if VM running) |
|
VM stopped - try start: 0.3 Start VM (if stopped) |
|
VM paused - resume: |
(no output) |
VM missing - proceed to 1.1 Export Realm (if possible) |
0.2 Check Container (if VM running)
ssh keycloak-01 "sudo docker ps -a"
If container unhealthy, try restart:
ssh keycloak-01 "cd /opt/keycloak && sudo docker compose restart"
0.3 Start VM (if stopped)
ssh kvm-01 "sudo virsh start keycloak-01"
Wait 30 seconds, then test SSH:
ssh keycloak-01 "hostnamectl"
If SSH works, skip to 4.1 Install Docker.
Phase 1: Destroy Corrupted VM
| This destroys all data on the VM. Ensure you have realm export if needed. |
1.1 Export Realm (if possible)
If Keycloak is still accessible:
# Export realm config before destruction
curl -ks -X POST "https://keycloak-01.inside.domusdigitalis.dev:8443/admin/realms/domusdigitalis/partial-export?exportClients=true&exportGroupsAndRoles=true" \
-H "Authorization: Bearer $(cat /tmp/kc-token)" \
-o /tmp/domusdigitalis-realm-export.json
Phase 2: Create VM
2.1 Verify Fedora Cloud Image
ssh kvm-01 "ls -lh /mnt/onboard-ssd/vms/Fedora-Cloud-Base-Generic-43*.qcow2"
If missing, download:
ssh kvm-01 << 'EOF'
cd /mnt/onboard-ssd/vms
sudo curl -LO https://download.fedoraproject.org/pub/fedora/linux/releases/43/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-43-1.6.x86_64.qcow2
EOF
2.2 Create Cloud-Init ISO
ssh kvm-01 << 'EOF'
cd /mnt/onboard-ssd/isos
cat > meta-data <<METADATA
instance-id: keycloak-01
local-hostname: keycloak-01
METADATA
cat > user-data <<'USERDATA'
#cloud-config
hostname: keycloak-01
fqdn: keycloak-01.inside.domusdigitalis.dev
users:
- name: evanusmodestus
sudo: ALL=(ALL) NOPASSWD:ALL
groups: wheel
shell: /bin/bash
lock_passwd: false
plain_text_passwd: changeme
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFKcJlxTF8z3O3Y3F6lMdZMXfA8R0hbLs2KfQJYuJ+Yk evanusmodestus@modestus-razer
USERDATA
genisoimage -output keycloak-01-cloud-init.iso -volid cidata -joliet -rock user-data meta-data
rm user-data meta-data
ls -lh keycloak-01-cloud-init.iso
EOF
2.3 Create VM Disk
ssh kvm-01 << 'EOF'
cd /mnt/onboard-ssd/vms
# Copy base image
sudo cp Fedora-Cloud-Base-Generic-43-1.6.x86_64.qcow2 keycloak-01.qcow2
# Resize to 20GB
sudo qemu-img resize keycloak-01.qcow2 20G
# Verify
ls -lh keycloak-01.qcow2
EOF
2.4 Create VM
ssh kvm-01 << 'EOF'
sudo virt-install \
--name keycloak-01 \
--vcpus 2 \
--memory 4096 \
--disk /mnt/onboard-ssd/vms/keycloak-01.qcow2,bus=virtio \
--disk /mnt/onboard-ssd/isos/keycloak-01-cloud-init.iso,device=cdrom \
--os-variant fedora-unknown \
--network bridge=virbr0,model=virtio \
--graphics none \
--import \
--noautoconsole
# Verify running
sudo virsh list | grep keycloak
EOF
Phase 3: Configure VM
3.1 Connect to Console
ssh kvm-01 "sudo virsh console keycloak-01"
Login: evanusmodestus / changeme
Immediately change password:
passwd
3.2 Configure Static IP
sudo tee /etc/NetworkManager/system-connections/enp1s0.nmconnection <<'EOF'
[connection]
id=enp1s0
type=ethernet
interface-name=enp1s0
autoconnect=true
[ipv4]
method=manual
addresses=10.50.1.80/24
gateway=10.50.1.1
dns=10.50.1.90
[ipv6]
method=disabled
EOF
sudo chmod 600 /etc/NetworkManager/system-connections/enp1s0.nmconnection
sudo nmcli con reload
sudo nmcli con up enp1s0
Phase 4: Install Keycloak
4.1 Install Docker
ssh keycloak-01 << 'EOF'
# Install Docker
sudo dnf install -y docker docker-compose
# Enable and start
sudo systemctl enable --now docker
# Verify
sudo docker --version
sudo docker compose version
EOF
4.2 Create Directory Structure
ssh keycloak-01 << 'EOF'
sudo mkdir -p /opt/keycloak/certs
sudo chown -R $(id -u):$(id -g) /opt/keycloak
EOF
4.3 Create Docker Compose File
ssh keycloak-01 << 'ENDSSH'
cat > /opt/keycloak/docker-compose.yml <<'EOF'
services:
postgres:
image: postgres:16
container_name: keycloak-db
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: ${KC_DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
keycloak:
image: quay.io/keycloak/keycloak:latest
container_name: keycloak
command: start
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: ${KC_DB_PASSWORD}
KC_HOSTNAME: keycloak-01.inside.domusdigitalis.dev
KC_HTTPS_CERTIFICATE_FILE: /opt/keycloak/conf/cert.pem
KC_HTTPS_CERTIFICATE_KEY_FILE: /opt/keycloak/conf/key.pem
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: ${KC_ADMIN_PASSWORD}
ports:
- "8443:8443"
volumes:
- ./certs:/opt/keycloak/conf:ro
depends_on:
- postgres
restart: unless-stopped
volumes:
postgres_data:
EOF
ENDSSH
4.4 Get TLS Certificate from Vault PKI
Issue certificate from Vault (not AD CS):
# On workstation - issue cert
vault write -format=json pki_int/issue/domus-server \
common_name="keycloak-01.inside.domusdigitalis.dev" \
alt_names="keycloak-01" \
ttl="8760h" > /tmp/keycloak-cert.json
# Extract cert and key
jq -r '.data.certificate' /tmp/keycloak-cert.json > /tmp/cert.pem
jq -r '.data.private_key' /tmp/keycloak-cert.json > /tmp/key.pem
jq -r '.data.ca_chain[]' /tmp/keycloak-cert.json >> /tmp/cert.pem
# Verify
openssl x509 -in /tmp/cert.pem -noout -subject -dates
# Transfer to keycloak-01
scp /tmp/cert.pem /tmp/key.pem keycloak-01:/tmp/
4.5 Install Certificate
ssh keycloak-01 << 'EOF'
# Move certs to keycloak directory
sudo mv /tmp/cert.pem /tmp/key.pem /opt/keycloak/certs/
# Set ownership for Keycloak container (UID 1000)
sudo chown 1000:1000 /opt/keycloak/certs/*.pem
# Set permissions
sudo chmod 400 /opt/keycloak/certs/key.pem
sudo chmod 444 /opt/keycloak/certs/cert.pem
# SELinux context (Fedora)
sudo chcon -Rt svirt_sandbox_file_t /opt/keycloak/certs/
# Verify
ls -laZ /opt/keycloak/certs/
EOF
4.6 Start Keycloak
# Load secrets on workstation
eval "$(dsource d000 dev/identity)"
# Start containers with secrets
ssh keycloak-01 "cd /opt/keycloak && sudo env KC_DB_PASSWORD='$KC_DB_PASSWORD' KC_ADMIN_PASSWORD='$KC_ADMIN_PASSWORD' docker compose up -d"
4.7 Verify Deployment
# Check containers
ssh keycloak-01 "sudo docker ps"
# Check logs (wait for startup)
ssh keycloak-01 "sudo docker logs keycloak 2>&1 | tail -30"
# Test HTTPS
curl -sk https://keycloak-01.inside.domusdigitalis.dev:8443/ -w "%{http_code}\n" -o /dev/null
# Expected: 302
# Verify certificate
echo | openssl s_client -connect keycloak-01.inside.domusdigitalis.dev:8443 2>/dev/null | openssl x509 -noout -subject -issuer
Phase 5: Configure Realm
5.1 Create Realm
Access Keycloak admin console:
https://keycloak-01.inside.domusdigitalis.dev:8443/admin/
Login: admin / (from dsec KC_ADMIN_PASSWORD)
-
Click Create realm
-
Name:
domusdigitalis -
Click Create
5.2 Create ISE SAML Client
-
Clients → Create client
-
Client type: SAML
-
Client ID: (get from ISE SP metadata - format
CiscoISE/{UUID}) -
Configure:
-
Root URL:
ise-02.inside.domusdigitalis.dev:8443 -
Valid redirect URIs:
ise-02.inside.domusdigitalis.dev:8443/* -
IDP-Initiated SSO URL:
ise-admin
-
5.3 Create Groups
Create groups that map to ISE admin roles:
| Keycloak Group | ISE Admin Group |
|---|---|
|
Super Admin |
|
Read Only Admin |
|
Help Desk Admin |
Phase 6: Validation
6.1 Test SAML Login
-
Open ISE admin portal:
ise-02.inside.domusdigitalis.dev/admin/ -
Click Log in with SAML
-
Authenticate with Keycloak credentials
-
Verify ISE admin access