Bonnes pratiques¶
Haute disponibilité et reprise d'activité¶
Architecture HA multi-AZ¶
Pour une disponibilité maximale, déployer les instances Keycloak sur plusieurs zones de disponibilité (AZ) ou plusieurs nœuds physiques distincts.
graph TD
LB["Load Balancer<br/>(HAProxy / Cloud LB)"] --> KC1["AZ-1<br/>Keycloak #1<br/>Infinispan"]
LB --> KC2["AZ-2<br/>Keycloak #2<br/>Infinispan"]
LB --> KC3["AZ-3<br/>Keycloak #3<br/>Infinispan"]
KC1 <--> KC2 <--> KC3
KC1 --> PG["PostgreSQL<br/>Primary (AZ-1)<br/>Replica (AZ-2)<br/>Replica (AZ-3)"]
KC2 --> PG
KC3 --> PG Objectifs de disponibilité¶
| Metrique | Cible | Justification |
|---|---|---|
| RTO (Recovery Time Objective) | < 5 minutes | Failover automatique Infinispan |
| RPO (Recovery Point Objective) | < 1 minute | Réplication synchrone PostgreSQL |
| SLA | 99.9% (8.7h d'indisponibilite/an) | Service critique transversal |
PodDisruptionBudget (Kubernetes)¶
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: keycloak-pdb
namespace: keycloak-system
spec:
minAvailable: 2
selector:
matchLabels:
app: keycloak
Backup et restore PostgreSQL¶
Backup continu avec WAL archiving¶
# postgresql.conf
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /backup/wal/%f && cp %p /backup/wal/%f'
max_wal_senders = 5
Backup periodique¶
#!/bin/bash
# Backup complet quotidien
pg_basebackup -h localhost -U replication -D /backup/base/$(date +%Y%m%d) \
--wal-method=stream --checkpoint=fast --compress=gzip
# Retention : 7 jours de backups complets
find /backup/base -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \;
Procedure de restauration¶
# 1. Arreter Keycloak
systemctl stop keycloak
# 2. Restaurer le backup de base
rm -rf /var/lib/postgresql/data/*
tar xzf /backup/base/20260416/base.tar.gz -C /var/lib/postgresql/data/
# 3. Configurer la recovery
cat > /var/lib/postgresql/data/recovery.signal << EOF
EOF
cat >> /var/lib/postgresql/data/postgresql.conf << EOF
restore_command = 'cp /backup/wal/%f %p'
recovery_target_time = '2026-04-16 14:00:00'
EOF
# 4. Demarrer PostgreSQL (replay des WAL)
systemctl start postgresql
# 5. Verifier puis redemarrer Keycloak
systemctl start keycloak
Tester la restauration
Planifier un test de restauration trimestriel dans un environnement isole. Documenter la procedure et mesurer le temps de restauration reel.
Failover Infinispan¶
Le cache distribue Infinispan gère automatiquement le failover :
| Scenario | Comportement | Impact |
|---|---|---|
| Perte d'1 nœud Keycloak | Infinispan redistribue les sessions | Aucun — les sessions sont repliquees |
| Perte de 2 nœuds sur 3 | Le nœud restant detient toutes les sessions | Dégradation — plus de HA |
| Perte totale | Toutes les sessions sont perdues | Les utilisateurs doivent se reconnecter |
Configuration recommandee du cache :
<!-- infinispan.xml (embarque dans Keycloak) -->
<distributed-cache name="sessions" owners="2">
<!-- 2 copies de chaque session = tolere 1 panne -->
<state-transfer timeout="60000"/>
</distributed-cache>
<distributed-cache name="authenticationSessions" owners="2">
<expiration max-idle="1800000"/> <!-- 30 min -->
</distributed-cache>
Nombre d'owners
Le parametre owners définit le nombre de copies de chaque entree de cache. Avec owners=2 et 3 nœuds, chaque session est presente sur 2 nœuds. Augmenter a 3 pour tolérer la perte de 2 nœuds simultanément (au prix de la mémoire).
Migration depuis un LDAP legacy¶
Stratégie de migration progressive¶
Ne pas migrer en big bang. Utiliser la fédération Keycloak pour coexister avec l'annuaire existant pendant la transition.
gantt
title Migration LDAP → Keycloak
dateFormat YYYY-MM
section Phase 1
Federation LDAP read-only :a1, 2026-05, 2026-07
Ajout OIDC service par service :a2, 2026-06, 2026-09
section Phase 2
MFA pour tous les utilisateurs :a3, 2026-08, 2026-10
Migration groupes/roles :a4, 2026-09, 2026-11
section Phase 3
Basculement ecriture Keycloak :a5, 2026-11, 2026-12
Decommissionnement LDAP :a6, 2027-01, 2027-02 Étapes détaillées¶
| Phase | Action | Duree | Risque |
|---|---|---|---|
| 1. Coexistence | Federer le LDAP en read-only dans Keycloak | 2 mois | Faible |
| 1. Migration OIDC | Ajouter OIDC client par client (Gitea, Grafana...) | 3 mois | Moyen (par service) |
| 2. MFA | Activer le MFA pour tous les utilisateurs | 2 mois | Moyen (resistance utilisateurs) |
| 2. Rôles | Migrer les groupes LDAP vers les rôles Keycloak | 2 mois | Moyen |
| 3. Écriture | Basculer Keycloak en mode WRITABLE | 1 mois | Eleve |
| 3. Decommission | Eteindre l'ancien LDAP | 1 mois | Faible (si phase 3 validee) |
Points d'attention¶
Comptes de service
Les comptes de service LDAP (utilisateurs techniques pour les applications) doivent etre migres vers des service accounts Keycloak avec client credentials grant. Ne pas les oublier lors de l'inventaire.
Upgrade Keycloak (blue/green)¶
Stratégie blue/green¶
Ne jamais upgrader Keycloak en place en production. Utiliser un déploiement blue/green.
graph TD
LB["Load Balancer<br/>(poids: blue=100%, green=0%)"]
LB --> Blue["BLUE<br/>KC v25.0<br/>(actif)"]
LB --> Green["GREEN<br/>KC v26.0<br/>(standby)"]
Blue --> PG["PostgreSQL<br/>(partage)"]
Green --> PG Procedure d'upgrade¶
| Étape | Action | Validation |
|---|---|---|
| 1 | Lire les release notes et changelog | Vérifier les breaking changes |
| 2 | Backup complet de la base PostgreSQL | Vérifier l'intégrité du backup |
| 3 | Déployer la nouvelle version (green) | Health check OK |
| 4 | Migrer la base (Keycloak le fait au demarrage) | Logs de migration sans erreur |
| 5 | Tester les flux critiques sur green | Login OIDC, SAML, LDAP sync |
| 6 | Basculer le trafic (blue→green) progressivement | 10% → 50% → 100% |
| 7 | Surveiller les metriques pendant 24h | Pas d'augmentation d'erreurs |
| 8 | Decommissionner blue | Conserver en standby 48h pour rollback |
Migration de base irreversible
Les migrations de schema PostgreSQL effectuees par Keycloak sont irreversibles. Un rollback necessite de restaurer le backup de la base. Toujours sauvegarder avant la migration.
Automatisation avec Helm¶
# Deployer la nouvelle version en parallele
helm upgrade keycloak-green bitnami/keycloak \
--namespace keycloak-green \
--values values-keycloak-v26.yaml \
--set replicaCount=1 # 1 seul replica pour tester
# Tester
curl -s https://keycloak-green.internal/health/ready
# Basculer le trafic
kubectl patch ingress keycloak -n keycloak-system \
--type merge \
-p '{"spec":{"rules":[{"host":"auth.example.com","http":{"paths":[{"path":"/","pathType":"Prefix","backend":{"service":{"name":"keycloak-green","port":{"number":8080}}}}]}}]}}'
Troubleshooting¶
Token expiry (erreurs 401)¶
Symptome : les utilisateurs reçoivent des erreurs 401 de manière intermittente.
Causes possibles :
| Cause | Diagnostic | Solution |
|---|---|---|
| Access token expire | Vérifier accessTokenLifespan (defaut 5 min) | Augmenter ou vérifier le refresh token flow |
| Horloge desynchronisee | date sur les nœuds Keycloak et les services | Configurer NTP/chrony |
| Cle de signature changee | Vérifier le JWKS endpoint | Redemarrer les services pour forcer le rechargement des cles |
| Token blackliste | Vérifier les revocation events dans les logs | Vérifier la raison de la revocation |
# Decoder un JWT pour diagnostiquer
echo "eyJhbGciOiJS..." | cut -d. -f2 | base64 -d | jq .
# Verifier les cles publiques
curl -s https://auth.example.com/realms/organization/protocol/openid-connect/certs | jq .
Debug des flux OIDC¶
Symptome : la redirection OIDC échoue ou boucle.
Diagnostic :
# 1. Verifier l'endpoint de discovery
curl -s https://auth.example.com/realms/organization/.well-known/openid-configuration | jq .
# 2. Verifier le client existe
kcadm.sh get clients -r organization -q clientId=svc-gitea
# 3. Verifier les redirect URIs
kcadm.sh get clients/$CLIENT_ID -r organization | jq '.redirectUris'
# 4. Activer le logging debug temporairement
# Dans la console admin : Realm Settings > Events > Config
# Enabled Event Types : ajouter CODE_TO_TOKEN_ERROR
Erreurs courantes :
| Erreur | Cause | Solution |
|---|---|---|
invalid_redirect_uri | L'URI de callback ne correspond pas | Vérifier les redirectUris du client |
invalid_client | Client ID incorrect ou client desactive | Vérifier la configuration du client |
invalid_grant | Code d'autorisation expire ou déjà utilise | Vérifier les timeouts et la connectivité réseau |
access_denied | Utilisateur n'a pas les rôles requis | Vérifier les rôle mappings et scopes |
Problèmes de synchronisation LDAP¶
Symptome : les utilisateurs LDAP n'apparaissent pas dans Keycloak, ou les modifications ne sont pas refletees.
# Verifier la connectivite LDAP
ldapsearch -H ldaps://ldap.example.com:636 \
-D "cn=keycloak,ou=services,dc=example,dc=com" \
-W -b "ou=users,dc=example,dc=com" \
"(uid=fabien)"
# Lancer une synchronisation manuelle
kcadm.sh create user-storage/$COMPONENT_ID/sync \
-r organization \
-s action=triggerFullSync
# Verifier les logs de synchronisation
podman logs keycloak 2>&1 | grep -i "ldap\|sync\|federation"
| Problème | Cause probable | Solution |
|---|---|---|
| Aucun utilisateur importe | usersDn incorrect | Vérifier le DN de base avec ldapsearch |
| Utilisateurs presents mais login échoue | usernameLDAPAttribute incorrect | Vérifier l'attribut utilise (uid, sAMAccountName) |
| Groupes non synchronises | Mapper de groupes absent | Ajouter un group-ldap-mapper |
| Sync lente (> 5 min pour 1000 users) | Filtre LDAP trop large | Ajouter un filtre (objectClass=inetOrgPerson) |
| Erreur SSL | Certificat CA non reconnu | Importer le CA dans le truststore Java de Keycloak |
Import du certificat CA LDAP¶
# Copier le certificat CA dans le conteneur
podman cp ca-ldap.crt keycloak:/tmp/ca-ldap.crt
# Importer dans le truststore Java
podman exec keycloak keytool -import -alias ldap-ca \
-file /tmp/ca-ldap.crt \
-keystore /opt/keycloak/conf/truststores/ldap-ca.jks \
-storepass changeit -noprompt
# Redemarrer Keycloak
podman restart keycloak
Metriques a surveiller¶
| Metrique | Seuil d'alerte | Signification |
|---|---|---|
keycloak_logins (rate) | Chute > 50% | Problème d'accessibilite |
keycloak_login_errors (rate) | > 10/min | Brute force ou problème de config |
keycloak_request_duration_seconds | p99 > 2s | Performance degradee |
jvm_memory_used_bytes | > 85% du max | Risque d'OOM |
pg_stat_activity (connections) | > 80% du max | Pool de connexions sature |
infinispan_cache_size | Croissance constante | Fuite de sessions, vérifier les expirations |
Dashboard Grafana pre-configure
Keycloak expose des metriques Prometheus sur le port 9000. Importer le dashboard officiel (ID: 19659) dans Grafana pour une visibilité immédiate.