Installation et configuration¶
Prerequis¶
| Composant | Version minimale | Objectif |
|---|---|---|
| Podman | 4.0+ | Runtime conteneurs (ou Kubernetes 1.26+) |
| Podman Compose | 1.0+ | Orchestration multi-conteneurs |
| PostgreSQL | 15+ | Backend de persistance |
| Certificat TLS | Let's Encrypt ou PKI interne | Chiffrement HTTPS |
| DNS | Enregistrement auth.example.com | Accès au service |
Option A : Déploiement Podman Compose¶
Préparation du système¶
# Creer l'utilisateur dedie
sudo useradd -r -m -s /sbin/nologin keycloak
# Creer les repertoires
sudo -u keycloak mkdir -p /home/keycloak/{certs,data}
# Copier les certificats TLS
sudo cp /etc/pki/tls/certs/auth.example.com.{crt,key} /home/keycloak/certs/
sudo chown -R keycloak:keycloak /home/keycloak/certs/
sudo chmod 600 /home/keycloak/certs/*.key
Fichier Podman Compose¶
# /home/keycloak/podman-compose.yml
version: "3.9"
services:
postgres:
image: docker.io/library/postgres:15-alpine
container_name: keycloak-db
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- keycloak-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak"]
interval: 10s
timeout: 5s
retries: 5
keycloak:
image: quay.io/keycloak/keycloak:25.0
container_name: keycloak
command: start
environment:
# Base de donnees
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD_FILE: /run/secrets/db_password
# Proxy et hostname
KC_HOSTNAME: auth.example.com
KC_PROXY_HEADERS: xforwarded
KC_HTTP_ENABLED: "true"
KC_HTTP_PORT: "8080"
KC_HEALTH_ENABLED: "true"
KC_METRICS_ENABLED: "true"
# Clustering (JDBC_PING)
KC_CACHE: ispn
KC_CACHE_STACK: jdbc-ping
# JVM
JAVA_OPTS_KC_HEAP: "-Xms512m -Xmx768m"
secrets:
- db_password
- admin_password
ports:
- "8080:8080"
- "9000:9000"
networks:
- keycloak-net
depends_on:
postgres:
condition: service_healthy
traefik:
image: docker.io/library/traefik:v3.1
container_name: keycloak-proxy
command:
- "--entrypoints.websecure.address=:443"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
ports:
- "443:443"
volumes:
- /run/user/1001/podman/podman.sock:/var/run/docker.sock:ro
- /home/keycloak/certs:/certs:ro
networks:
- keycloak-net
labels:
- "traefik.enable=true"
volumes:
pgdata:
networks:
keycloak-net:
driver: bridge
secrets:
db_password:
file: /home/keycloak/.secrets/db_password
admin_password:
file: /home/keycloak/.secrets/admin_password
Initialisation des secrets¶
# Creer le repertoire de secrets
sudo -u keycloak mkdir -p /home/keycloak/.secrets
sudo chmod 700 /home/keycloak/.secrets
# Generer les mots de passe
openssl rand -base64 32 | sudo -u keycloak tee /home/keycloak/.secrets/db_password > /dev/null
openssl rand -base64 32 | sudo -u keycloak tee /home/keycloak/.secrets/admin_password > /dev/null
sudo chmod 600 /home/keycloak/.secrets/*
Demarrage¶
# Demarrer la stack
sudo -u keycloak podman-compose -f /home/keycloak/podman-compose.yml up -d
# Verifier les logs
sudo -u keycloak podman logs -f keycloak
# Attendre le message "Keycloak started in Xs"
Creation de l'administrateur initial¶
# Creer l'admin via la CLI Keycloak
sudo -u keycloak podman exec keycloak \
/opt/keycloak/bin/kcadm.sh config credentials \
--server http://localhost:8080 \
--realm master \
--user temp-admin \
--password "$(cat /home/keycloak/.secrets/admin_password)"
# Creer l'utilisateur admin permanent
sudo -u keycloak podman exec keycloak \
/opt/keycloak/bin/kcadm.sh create users \
-r master \
-s username=admin \
-s enabled=true \
-s emailVerified=true \
-s "email=admin@example.com"
# Attribuer le role admin
sudo -u keycloak podman exec keycloak \
/opt/keycloak/bin/kcadm.sh add-roles \
-r master \
--uusername admin \
--rolename admin
Supprimer le compte temporaire
Apres avoir cree l'administrateur permanent, supprimez le compte temp-admin utilise lors du premier demarrage.
Option B : Déploiement Kubernetes (Helm)¶
Ajout du repository Helm¶
Fichier de valeurs¶
# values-keycloak.yaml
replicaCount: 3
image:
registry: quay.io
repository: keycloak/keycloak
tag: "25.0"
auth:
adminUser: admin
existingSecret: keycloak-admin-secret
passwordSecretKey: admin-password
postgresql:
enabled: true
auth:
existingSecret: keycloak-db-secret
secretKeys:
adminPasswordKey: postgres-password
userPasswordKey: password
primary:
persistence:
size: 10Gi
ingress:
enabled: true
ingressClassName: nginx
hostname: auth.example.com
tls: true
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
metrics:
enabled: true
serviceMonitor:
enabled: true
podDisruptionBudget:
create: true
minAvailable: 2
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "2"
memory: 1Gi
Installation¶
# Creer le namespace
kubectl create namespace keycloak-system
# Creer les secrets
kubectl create secret generic keycloak-admin-secret \
--namespace keycloak-system \
--from-literal=admin-password="$(openssl rand -base64 32)"
kubectl create secret generic keycloak-db-secret \
--namespace keycloak-system \
--from-literal=postgres-password="$(openssl rand -base64 32)" \
--from-literal=password="$(openssl rand -base64 32)"
# Deployer
helm install keycloak bitnami/keycloak \
--namespace keycloak-system \
--values values-keycloak.yaml \
--wait --timeout 10m
Configuration initiale¶
Créer le realm principal¶
Un realm isole un ensemble d'utilisateurs, de clients et de rôles. Ne jamais utiliser le realm master pour les applications.
# Via kcadm.sh
kcadm.sh create realms \
-s realm=organization \
-s enabled=true \
-s displayName="Organisation" \
-s loginWithEmailAllowed=true \
-s registrationAllowed=false \
-s resetPasswordAllowed=true \
-s sslRequired=external \
-s bruteForceProtected=true \
-s failureFactor=5 \
-s maxDeltaTimeSeconds=3600 \
-s minimumQuickLoginWaitSeconds=60
Ne jamais utiliser le realm master
Le realm master est reserve a l'administration de Keycloak lui-même. Créer un realm dedie pour les utilisateurs et les applications.
Configurer la politique de mot de passe¶
# Politique de mot de passe robuste
kcadm.sh update realms/organization \
-s 'passwordPolicy="length(12) and upperCase(1) and lowerCase(1) and digits(1) and specialChars(1) and notUsername and passwordHistory(5)"'
| Regle | Valeur | Objectif |
|---|---|---|
length | 12 | Longueur minimale |
upperCase | 1 | Au moins une majuscule |
lowerCase | 1 | Au moins une minuscule |
digits | 1 | Au moins un chiffre |
specialChars | 1 | Au moins un caractère special |
notUsername | - | Le mot de passe ne doit pas contenir le login |
passwordHistory | 5 | Interdire les 5 derniers mots de passe |
Activer MFA obligatoire¶
# Rendre TOTP obligatoire pour tous les utilisateurs
kcadm.sh update realms/organization \
-s 'otpPolicyType=totp' \
-s 'otpPolicyAlgorithm=HmacSHA256' \
-s 'otpPolicyDigits=6' \
-s 'otpPolicyPeriod=30'
Pour imposer le MFA dans le flux de login :
- Aller dans Authentication > Flows > Browser
- Ajouter l'execution OTP Form apres Username Password Form
- Définir l'execution comme Required
Configurer les sessions¶
# Durees de session
kcadm.sh update realms/organization \
-s 'ssoSessionIdleTimeout=1800' \
-s 'ssoSessionMaxLifespan=36000' \
-s 'accessTokenLifespan=300' \
-s 'accessTokenLifespanForImplicitFlow=900'
| Parametre | Valeur | Description |
|---|---|---|
ssoSessionIdleTimeout | 1800 (30 min) | Timeout d'inactivite SSO |
ssoSessionMaxLifespan | 36000 (10h) | Duree max d'une session SSO |
accessTokenLifespan | 300 (5 min) | Duree de vie de l'access token |
Integration LDAP backend¶
Pour les organisations qui ont déjà un annuaire LDAP (Active Directory, OpenLDAP), Keycloak peut federer les identités.
Ajouter une fédération LDAP¶
kcadm.sh create components \
-r organization \
-s name="Corporate LDAP" \
-s providerId=ldap \
-s providerType=org.keycloak.storage.UserStorageProvider \
-s 'config.vendor=["other"]' \
-s 'config.connectionUrl=["ldaps://ldap.example.com:636"]' \
-s 'config.bindDn=["cn=keycloak,ou=services,dc=example,dc=com"]' \
-s 'config.bindCredential=["VAULT_REF"]' \
-s 'config.usersDn=["ou=users,dc=example,dc=com"]' \
-s 'config.usernameLDAPAttribute=["uid"]' \
-s 'config.uuidLDAPAttribute=["entryUUID"]' \
-s 'config.userObjectClasses=["inetOrgPerson"]' \
-s 'config.editMode=["READ_ONLY"]' \
-s 'config.syncRegistrations=["false"]' \
-s 'config.importEnabled=["true"]' \
-s 'config.batchSizeForSync=["1000"]' \
-s 'config.fullSyncPeriod=["3600"]' \
-s 'config.changedSyncPeriod=["60"]'
| Parametre | Recommandation |
|---|---|
editMode | READ_ONLY — Keycloak lit les identités, ne les modifie pas dans LDAP |
importEnabled | true — importe les utilisateurs localement pour les performances |
fullSyncPeriod | 3600 (1h) — synchronisation complète periodique |
changedSyncPeriod | 60 (1 min) — synchronisation incrementale fréquente |
Mapper les groupes LDAP
Apres avoir configuré la fédération, ajouter un mapper de type group-ldap-mapper pour synchroniser les groupes LDAP vers les groupes Keycloak. Cela permet d'utiliser les groupes existants pour le RBAC.
Vérifier la synchronisation¶
# Lancer une synchronisation manuelle
kcadm.sh create user-storage/$COMPONENT_ID/sync \
-r organization \
-s action=triggerFullSync
# Verifier les utilisateurs importes
kcadm.sh get users -r organization --limit 10
Service systemd (Podman)¶
# /etc/systemd/system/keycloak.service
[Unit]
Description=Keycloak IAM Stack
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=keycloak
WorkingDirectory=/home/keycloak
ExecStartPre=/usr/bin/podman-compose pull
ExecStart=/usr/bin/podman-compose up
ExecStop=/usr/bin/podman-compose down
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Vérification post-installation¶
| Vérification | Commande / URL | Résultat attendu |
|---|---|---|
| Health check | curl -s https://auth.example.com/health/ready | {"status": "UP"} |
| Page de login | https://auth.example.com/realms/organization/account | Page de login |
| Admin console | https://auth.example.com/admin/ | Console d'administration |
| Metriques | curl -s http://localhost:9000/metrics | Metriques Prometheus |
| OIDC discovery | https://auth.example.com/realms/organization/.well-known/openid-configuration | JSON de configuration OIDC |