# Keycloak — migrat pe SSO cluster (2026-04-30) > **TL;DR**: containerul local `keycloak` (Keycloak 22) nu mai rulează. DIDI folosește acum **SSO cluster** la `https://` (Keycloak 26 HA, 3 replicas pe Dev Docker Swarm). Realm `didi-clients` migrat cu toate datele (users, clients, groups). Theme custom `didi-clients-theme` deployed pe SSO via bind mount pe nodurile Swarm. --- ## Ce era aici (înainte de 2026-04-30) Container `keycloak` (Keycloak 22, image `quay.io/keycloak/keycloak:22.0`) definit în `backend/production/docker-compose.yml`. Single-instance pe didi12 (10.11.10.12:28000). DB pe Patroni cluster (`keycloak_db`). Hostname fix `KC_HOSTNAME_URL=https://didi365.eu/auth`. Folosit doar de DIDI. Theme custom `didi-clients-theme` (purple gradient). ## De ce migrare 1. **Single-tenant lock-in**: Keycloak local servea doar didi365.eu. Pentru alte produse (lege365, rafai, etc.) ar fi trebuit instanțe separate sau hostname dinamic complex. 2. **Single-point-of-failure**: 1 container, 1 host. Down când didi12 down. 3. **DB password issue**: 2026-04-29 cineva a rotat parola `keycloak` user în Patroni → connection pool fail → service degraded. 4. **SSO cluster live**: 2026-04-29 Lucian a deploy-uit Keycloak 26 HA pe Dev Swarm, cu hostname public ``. ## Ce e acum ### SSO Cluster | Componentă | Detaliu | |---|---| | Hostname public | `` (DNS public, cert Let's Encrypt valid) | | Hostname intern | `` (admin URL via `KC_HOSTNAME_ADMIN`) | | IP public | `82.79.147.181` (port-forward la Traefik intern) | | Edge router | Traefik central (`10.11.10.171:443`) | | Keycloak version | 26.0 (`quay.io/keycloak/keycloak:26.0`) | | HA | 3 replicas, max 1 per node | | Cluster | Dev Docker Swarm (`10.11.50.151-154`) | | DB | Patroni cluster (`10.11.50.166:5000/keycloak_db`) | | Cache | ispn (Infinispan, dns.query=tasks.keycloak) | | Stack name | `keycloak-cluster` (`docker service ls`) | ### Hostname configuration ```yaml KC_HOSTNAME: https:// # public URL (used in tokens, redirects) KC_HOSTNAME_ADMIN: https:// # admin endpoints (internal-only via 307 redirect) KC_HOSTNAME_STRICT_BACKCHANNEL: false KC_PROXY_HEADERS: xforwarded ``` Issuer in tokens: `https:///realms/didi-clients`. Endpoints (no `/auth/` prefix in K26): - `/realms/didi-clients/.well-known/openid-configuration` - `/realms/didi-clients/protocol/openid-connect/auth` - `/realms/didi-clients/protocol/openid-connect/token` - `/realms/didi-clients/protocol/openid-connect/certs` (JWKS) ## Realm-uri pe SSO - `master` — admin Keycloak (NU folosi pentru apps) - **`didi-clients`** — DIDI customer-facing app (migrat 1:1 din local) - `didi-admins` — DIDI admin panel (creat de Lucian, neutilizat încă) ## Theme deployment (didi-clients-theme) Themes sunt mounted ca **bind mount** pe fiecare nod Swarm: ```yaml mount: type: bind source: /var/keycloak-themes/didi-clients-theme target: /opt/keycloak/themes/didi-clients-theme readonly: true ``` Adăugat via `docker service update --mount-add` (nu via stack file). Pentru ca toate 3 replicas să găsească tema, fișierele trebuie pe **toate 4 nodurile** Swarm (10.11.50.151-154). ### Procedură deploy theme update 1. Pack theme local pe didi12: ```bash cd backend/services/gateway-auth-layer/didiKeycloak/themes tar -czf /home/admin365/didi-clients-theme.tar.gz didi-clients-theme/ ``` 2. Pe `dev-docker-mgr` (Swarm manager — 10.11.50.151): ```bash scp admin365@10.11.10.12:/home/admin365/didi-clients-theme.tar.gz /tmp/ sudo tar -xzf /tmp/didi-clients-theme.tar.gz -C /var/keycloak-themes/ for n in 152 153 154; do scp /tmp/didi-clients-theme.tar.gz admin365@10.11.50.$n:/tmp/ ssh -t admin365@10.11.50.$n 'sudo tar -xzf /tmp/didi-clients-theme.tar.gz -C /var/keycloak-themes/' done sudo docker service update --force keycloak-cluster_keycloak ``` 3. Verify: ```bash curl -ksm 5 https:///resources//login/didi-clients-theme/css/login.css | head ``` ### Theme structure (PatternFly v4 specific) Keycloak 26 default theme (`keycloak`) folosește PatternFly v4 markup. Custom theme cu `parent=keycloak` moștenește template-urile, dar PF4 are reguli CSS specifice care necesită overrides în login.css: - **Password input wrap** — în `
` cu eye-icon button. Necesită CSS specific pentru `.pf-c-input-group .pf-c-form-control` - **Pseudo-element `::after` pe button** — PF4 button-uri au `