Téléverser les fichiers vers "RBER Connect"
This commit is contained in:
812
RBER Connect/keycloak-iam-documentation.md
Normal file
812
RBER Connect/keycloak-iam-documentation.md
Normal file
@@ -0,0 +1,812 @@
|
|||||||
|
# Keycloak IAM - Universités du Bénin
|
||||||
|
## Documentation Technique Complète
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Version :** 1.0
|
||||||
|
**Date :** 5 Janvier 2026
|
||||||
|
**Projet :** IAM Multi-Universités (UAC, UNA, UNSTIM, UP)
|
||||||
|
**Infrastructure :** RKE2 / Kubernetes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Table des matières
|
||||||
|
|
||||||
|
1. [Architecture Générale](#1-architecture-générale)
|
||||||
|
2. [Document d'Installation](#2-document-dinstallation)
|
||||||
|
3. [Document d'Administration Générale](#3-document-dadministration-générale)
|
||||||
|
4. [Administration par Realm](#4-administration-par-realm)
|
||||||
|
5. [Procédures Opérationnelles](#5-procédures-opérationnelles)
|
||||||
|
6. [Dépannage](#6-dépannage)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. Architecture Générale
|
||||||
|
|
||||||
|
## 1.1 Vue d'ensemble
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ INTERNET │
|
||||||
|
└─────────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ 102.222.216.6 │
|
||||||
|
│ (NAT/DNS) │
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ HAProxy Externe │
|
||||||
|
│ (rber-ldb-int-01) │
|
||||||
|
│ 10.29.113.21 │
|
||||||
|
│ - Terminaison SNI (port 443) │
|
||||||
|
│ - Routage par domaine │
|
||||||
|
│ - Health checks │
|
||||||
|
└─────────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼ (auth.rber.bj → bk_k8s_ingress_https)
|
||||||
|
┌─────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ HAProxy Ingress Controller │
|
||||||
|
│ 10.29.113.201:443 │
|
||||||
|
│ (Kubernetes Ingress) │
|
||||||
|
└─────────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Keycloak │
|
||||||
|
│ Namespace: keycloak │
|
||||||
|
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||||
|
│ │ keycloak-0 │ │ keycloak-1 │ (2 réplicas) │
|
||||||
|
│ │ Port 8080 │ │ Port 8080 │ │
|
||||||
|
│ └────────┬────────┘ └────────┬────────┘ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └──────────┬───────────┘ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌─────────────────────┐ │
|
||||||
|
│ │ PostgreSQL HA │ │
|
||||||
|
│ │ (CloudNativePG) │ │
|
||||||
|
│ │ keycloak-pg-1/2/3 │ │
|
||||||
|
│ └─────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼ (LDAP Federation)
|
||||||
|
┌─────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Annuaires LDAP Universités │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||||
|
│ │ UAC │ │ UNA │ │ UNSTIM │ │ UP │ │
|
||||||
|
│ │10.24.112.33 │ │10.20.112.33 │ │10.21.112.33 │ │10.25.112.33 │ │
|
||||||
|
│ │ Port 389 │ │ Port 389 │ │ Port 389 │ │ Port 389 │ │
|
||||||
|
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 1.2 Composants
|
||||||
|
|
||||||
|
| Composant | Version | Rôle |
|
||||||
|
|-----------|---------|------|
|
||||||
|
| Keycloak | 26.0 | Serveur IAM |
|
||||||
|
| PostgreSQL | CloudNativePG | Base de données HA |
|
||||||
|
| HAProxy | 2.8.5 | Load Balancer externe |
|
||||||
|
| HAProxy Ingress | - | Ingress Controller K8s |
|
||||||
|
| RKE2 | - | Distribution Kubernetes |
|
||||||
|
|
||||||
|
## 1.3 Realms configurés
|
||||||
|
|
||||||
|
| Realm | Université | LDAP | Base DN |
|
||||||
|
|-------|-----------|------|---------|
|
||||||
|
| master | Administration | - | - |
|
||||||
|
| uac | Université d'Abomey-Calavi | 10.24.112.33 | DC=uac,DC=bj |
|
||||||
|
| una | Université Nationale d'Agriculture | 10.20.112.33 | DC=una,DC=bj |
|
||||||
|
| unstim | Université des Sciences, Technologies, Ingénierie et Mathématiques | 10.21.112.33 | DC=unstim,DC=bj |
|
||||||
|
| up | Université de Parakou | 10.25.112.33 | DC=univ-parakou,DC=bj |
|
||||||
|
|
||||||
|
## 1.4 URLs d'accès
|
||||||
|
|
||||||
|
| Service | URL |
|
||||||
|
|---------|-----|
|
||||||
|
| Console Admin | https://auth.rber.bj/admin |
|
||||||
|
| Realm UAC | https://auth.rber.bj/realms/uac |
|
||||||
|
| Realm UNA | https://auth.rber.bj/realms/una |
|
||||||
|
| Realm UNSTIM | https://auth.rber.bj/realms/unstim |
|
||||||
|
| Realm UP | https://auth.rber.bj/realms/up |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Document d'Installation
|
||||||
|
|
||||||
|
## 2.1 Prérequis
|
||||||
|
|
||||||
|
### Infrastructure
|
||||||
|
- Cluster RKE2 opérationnel
|
||||||
|
- Namespace `keycloak` créé
|
||||||
|
- HAProxy Ingress Controller déployé (10.29.113.201)
|
||||||
|
- Certificat SSL pour auth.rber.bj
|
||||||
|
|
||||||
|
### Réseau
|
||||||
|
- Connectivité vers les LDAP des universités (ports 389)
|
||||||
|
- DNS configuré : auth.rber.bj → 102.222.216.6
|
||||||
|
|
||||||
|
## 2.2 Installation PostgreSQL (CloudNativePG)
|
||||||
|
|
||||||
|
### 2.2.1 Créer le cluster PostgreSQL
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# keycloak-pg-cluster.yaml
|
||||||
|
apiVersion: postgresql.cnpg.io/v1
|
||||||
|
kind: Cluster
|
||||||
|
metadata:
|
||||||
|
name: keycloak-pg
|
||||||
|
namespace: keycloak
|
||||||
|
spec:
|
||||||
|
instances: 3
|
||||||
|
primaryUpdateStrategy: unsupervised
|
||||||
|
storage:
|
||||||
|
size: 10Gi
|
||||||
|
bootstrap:
|
||||||
|
initdb:
|
||||||
|
database: keycloak
|
||||||
|
owner: keycloak
|
||||||
|
backup:
|
||||||
|
barmanObjectStore:
|
||||||
|
# Configurer selon votre stockage
|
||||||
|
retentionPolicy: "30d"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl apply -f keycloak-pg-cluster.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2.2 Vérifier le déploiement
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get pods -n keycloak
|
||||||
|
# Attendu: keycloak-pg-1, keycloak-pg-2, keycloak-pg-3 en Running
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.3 Installation Keycloak
|
||||||
|
|
||||||
|
### 2.3.1 Créer le secret admin
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl create secret generic keycloak-admin-secret -n keycloak \
|
||||||
|
--from-literal=admin-password='VotreMotDePasseSecurise'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3.2 Déployer Keycloak
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# keycloak-deployment.yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: keycloak
|
||||||
|
namespace: keycloak
|
||||||
|
labels:
|
||||||
|
app: keycloak
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: keycloak
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: keycloak
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: keycloak
|
||||||
|
image: quay.io/keycloak/keycloak:26.0
|
||||||
|
args: ["start"]
|
||||||
|
env:
|
||||||
|
- name: KC_HOSTNAME
|
||||||
|
value: "auth.rber.bj"
|
||||||
|
- name: KC_PROXY_HEADERS
|
||||||
|
value: "xforwarded"
|
||||||
|
- name: KC_HTTP_ENABLED
|
||||||
|
value: "true"
|
||||||
|
- name: KC_HEALTH_ENABLED
|
||||||
|
value: "true"
|
||||||
|
- name: KC_HTTP_MANAGEMENT_PORT
|
||||||
|
value: "9000"
|
||||||
|
- name: KC_DB
|
||||||
|
value: "postgres"
|
||||||
|
- name: KC_DB_URL_HOST
|
||||||
|
value: "keycloak-pg-rw"
|
||||||
|
- name: KC_DB_URL_PORT
|
||||||
|
value: "5432"
|
||||||
|
- name: KC_DB_URL_DATABASE
|
||||||
|
value: "keycloak"
|
||||||
|
- name: KC_DB_USERNAME
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: keycloak-pg-app
|
||||||
|
key: username
|
||||||
|
- name: KC_DB_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: keycloak-pg-app
|
||||||
|
key: password
|
||||||
|
- name: KEYCLOAK_ADMIN
|
||||||
|
value: "admin"
|
||||||
|
- name: KEYCLOAK_ADMIN_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: keycloak-admin-secret
|
||||||
|
key: admin-password
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 8080
|
||||||
|
- name: management
|
||||||
|
containerPort: 9000
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health/ready
|
||||||
|
port: 9000
|
||||||
|
initialDelaySeconds: 60
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health/live
|
||||||
|
port: 9000
|
||||||
|
initialDelaySeconds: 120
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
startupProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health/started
|
||||||
|
port: 9000
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 5
|
||||||
|
failureThreshold: 30
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "250m"
|
||||||
|
limits:
|
||||||
|
memory: "1Gi"
|
||||||
|
cpu: "1"
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: keycloak
|
||||||
|
namespace: keycloak
|
||||||
|
labels:
|
||||||
|
app: keycloak
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
selector:
|
||||||
|
app: keycloak
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: keycloak
|
||||||
|
namespace: keycloak
|
||||||
|
annotations:
|
||||||
|
haproxy.org/cookie-persistence: "SERVERID"
|
||||||
|
haproxy.org/timeout-server: "120s"
|
||||||
|
spec:
|
||||||
|
ingressClassName: haproxy
|
||||||
|
rules:
|
||||||
|
- host: auth.rber.bj
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: keycloak
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl apply -f keycloak-deployment.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3.3 Vérifier le déploiement
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pods
|
||||||
|
kubectl get pods -n keycloak
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
kubectl logs -n keycloak -l app=keycloak --tail=50
|
||||||
|
|
||||||
|
# Test health
|
||||||
|
kubectl exec -it -n keycloak deploy/keycloak -- curl -s localhost:9000/health/ready
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.4 Configuration HAProxy
|
||||||
|
|
||||||
|
### 2.4.1 ACL SNI pour Keycloak
|
||||||
|
|
||||||
|
Ajouter dans `/etc/haproxy/haproxy.cfg` section `frontend https_frontend` :
|
||||||
|
|
||||||
|
```
|
||||||
|
# ACL Keycloak
|
||||||
|
acl sni_keycloak req_ssl_sni -i auth.rber.bj
|
||||||
|
|
||||||
|
# Routage (ajouter sni_keycloak à la règle existante)
|
||||||
|
use_backend bk_k8s_ingress_https if sni_unstim or sni_uac or sni_una or sni_up or sni_test_unstim or sni_keycloak
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4.2 ACL HTTP pour Keycloak
|
||||||
|
|
||||||
|
Ajouter dans `frontend http_frontend` :
|
||||||
|
|
||||||
|
```
|
||||||
|
acl is_keycloak hdr(host) -i auth.rber.bj
|
||||||
|
|
||||||
|
# Ajouter à la règle use_backend bk_k8s_ingress
|
||||||
|
use_backend bk_k8s_ingress if ... or is_keycloak
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4.3 Recharger HAProxy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
haproxy -c -f /etc/haproxy/haproxy.cfg
|
||||||
|
systemctl reload haproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.5 Création des Realms
|
||||||
|
|
||||||
|
### 2.5.1 Script de création
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# create-realms.sh
|
||||||
|
|
||||||
|
export KEYCLOAK_URL="https://auth.rber.bj"
|
||||||
|
export ADMIN_PASSWORD=$(kubectl get secret keycloak-admin-secret -n keycloak -o jsonpath='{.data.admin-password}' | base64 -d)
|
||||||
|
|
||||||
|
# Obtenir token
|
||||||
|
export TOKEN=$(curl -sk -X POST "$KEYCLOAK_URL/realms/master/protocol/openid-connect/token" \
|
||||||
|
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||||
|
-d "username=admin" \
|
||||||
|
-d "password=$ADMIN_PASSWORD" \
|
||||||
|
-d "grant_type=password" \
|
||||||
|
-d "client_id=admin-cli" | jq -r '.access_token')
|
||||||
|
|
||||||
|
# Créer les realms
|
||||||
|
declare -A REALMS=(
|
||||||
|
["uac"]="Université d Abomey-Calavi"
|
||||||
|
["una"]="Université Nationale d Agriculture"
|
||||||
|
["unstim"]="Université des Sciences Technologies Ingénierie et Mathématiques"
|
||||||
|
["up"]="Université de Parakou"
|
||||||
|
)
|
||||||
|
|
||||||
|
for REALM in "${!REALMS[@]}"; do
|
||||||
|
curl -sk -X POST "$KEYCLOAK_URL/admin/realms" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"realm\":\"$REALM\",\"enabled\":true,\"displayName\":\"${REALMS[$REALM]}\"}"
|
||||||
|
echo "Realm $REALM créé"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.6 Configuration des Fédérations LDAP
|
||||||
|
|
||||||
|
### 2.6.1 Script de configuration LDAP
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# configure-ldap.sh
|
||||||
|
|
||||||
|
export KEYCLOAK_URL="https://auth.rber.bj"
|
||||||
|
export ADMIN_PASSWORD=$(kubectl get secret keycloak-admin-secret -n keycloak -o jsonpath='{.data.admin-password}' | base64 -d)
|
||||||
|
|
||||||
|
# Obtenir token
|
||||||
|
export TOKEN=$(curl -sk -X POST "$KEYCLOAK_URL/realms/master/protocol/openid-connect/token" \
|
||||||
|
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||||
|
-d "username=admin" \
|
||||||
|
-d "password=$ADMIN_PASSWORD" \
|
||||||
|
-d "grant_type=password" \
|
||||||
|
-d "client_id=admin-cli" | jq -r '.access_token')
|
||||||
|
|
||||||
|
# Configuration LDAP par université
|
||||||
|
configure_ldap() {
|
||||||
|
local REALM=$1
|
||||||
|
local LDAP_URL=$2
|
||||||
|
local BIND_DN=$3
|
||||||
|
local BIND_PASSWORD=$4
|
||||||
|
local USERS_DN=$5
|
||||||
|
|
||||||
|
curl -sk -X POST "$KEYCLOAK_URL/admin/realms/$REALM/components" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"name\": \"ldap-$REALM\",
|
||||||
|
\"providerId\": \"ldap\",
|
||||||
|
\"providerType\": \"org.keycloak.storage.UserStorageProvider\",
|
||||||
|
\"config\": {
|
||||||
|
\"vendor\": [\"ad\"],
|
||||||
|
\"connectionUrl\": [\"$LDAP_URL\"],
|
||||||
|
\"bindDn\": [\"$BIND_DN\"],
|
||||||
|
\"bindCredential\": [\"$BIND_PASSWORD\"],
|
||||||
|
\"usersDn\": [\"$USERS_DN\"],
|
||||||
|
\"usernameLDAPAttribute\": [\"sAMAccountName\"],
|
||||||
|
\"rdnLDAPAttribute\": [\"cn\"],
|
||||||
|
\"uuidLDAPAttribute\": [\"objectGUID\"],
|
||||||
|
\"userObjectClasses\": [\"person, organizationalPerson, user\"],
|
||||||
|
\"editMode\": [\"READ_ONLY\"],
|
||||||
|
\"importEnabled\": [\"true\"],
|
||||||
|
\"enabled\": [\"true\"]
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
echo "LDAP configuré pour $REALM"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Appliquer les configurations
|
||||||
|
configure_ldap "uac" "ldap://10.24.112.33:389" "Administrator@UAC.BJ" "MOT_DE_PASSE" "CN=Users,DC=uac,DC=bj"
|
||||||
|
configure_ldap "una" "ldap://10.20.112.33:389" "Administrator@UNA.BJ" "MOT_DE_PASSE" "CN=Users,DC=una,DC=bj"
|
||||||
|
configure_ldap "unstim" "ldap://10.21.112.33:389" "Administrator@UNSTIM.BJ" "MOT_DE_PASSE" "CN=Users,DC=unstim,DC=bj"
|
||||||
|
configure_ldap "up" "ldap://10.25.112.33:389" "Administrator@UNIV-PARAKOU.BJ" "MOT_DE_PASSE" "CN=Users,DC=univ-parakou,DC=bj"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. Document d'Administration Générale
|
||||||
|
|
||||||
|
## 3.1 Accès à la console d'administration
|
||||||
|
|
||||||
|
### URL
|
||||||
|
```
|
||||||
|
https://auth.rber.bj/admin
|
||||||
|
```
|
||||||
|
|
||||||
|
### Récupérer le mot de passe admin
|
||||||
|
```bash
|
||||||
|
kubectl get secret keycloak-admin-secret -n keycloak -o jsonpath='{.data.admin-password}' | base64 -d && echo
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3.2 Gestion des tokens API
|
||||||
|
|
||||||
|
### Obtenir un token d'administration
|
||||||
|
```bash
|
||||||
|
export KEYCLOAK_URL="https://auth.rber.bj"
|
||||||
|
export ADMIN_PASSWORD="votre_mot_de_passe"
|
||||||
|
|
||||||
|
export TOKEN=$(curl -sk -X POST "$KEYCLOAK_URL/realms/master/protocol/openid-connect/token" \
|
||||||
|
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||||
|
-d "username=admin" \
|
||||||
|
-d "password=$ADMIN_PASSWORD" \
|
||||||
|
-d "grant_type=password" \
|
||||||
|
-d "client_id=admin-cli" | jq -r '.access_token')
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note :** Le token expire après 1 minute par défaut.
|
||||||
|
|
||||||
|
## 3.3 Commandes de base Kubernetes
|
||||||
|
|
||||||
|
### Voir les pods Keycloak
|
||||||
|
```bash
|
||||||
|
kubectl get pods -n keycloak
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logs Keycloak
|
||||||
|
```bash
|
||||||
|
kubectl logs -n keycloak -l app=keycloak -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Redémarrer Keycloak
|
||||||
|
```bash
|
||||||
|
kubectl rollout restart deployment/keycloak -n keycloak
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scaler les réplicas
|
||||||
|
```bash
|
||||||
|
kubectl scale deployment/keycloak -n keycloak --replicas=3
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3.4 Gestion de la base de données
|
||||||
|
|
||||||
|
### Se connecter à PostgreSQL
|
||||||
|
```bash
|
||||||
|
kubectl exec -it keycloak-pg-1 -n keycloak -- psql -U keycloak -d keycloak
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vérifier l'état du cluster PostgreSQL
|
||||||
|
```bash
|
||||||
|
kubectl get cluster -n keycloak
|
||||||
|
kubectl describe cluster keycloak-pg -n keycloak
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3.5 Synchronisation LDAP
|
||||||
|
|
||||||
|
### Via API
|
||||||
|
```bash
|
||||||
|
# Obtenir l'ID du composant LDAP
|
||||||
|
LDAP_ID=$(curl -sk "$KEYCLOAK_URL/admin/realms/uac/components?type=org.keycloak.storage.UserStorageProvider" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" | jq -r '.[0].id')
|
||||||
|
|
||||||
|
# Synchronisation complète
|
||||||
|
curl -sk -X POST "$KEYCLOAK_URL/admin/realms/uac/user-storage/$LDAP_ID/sync?action=triggerFullSync" \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
|
|
||||||
|
# Synchronisation des modifications
|
||||||
|
curl -sk -X POST "$KEYCLOAK_URL/admin/realms/uac/user-storage/$LDAP_ID/sync?action=triggerChangedUsersSync" \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Via Console
|
||||||
|
1. Aller dans le realm concerné
|
||||||
|
2. User Federation → ldap-xxx
|
||||||
|
3. Cliquer sur "Synchronize all users" ou "Synchronize changed users"
|
||||||
|
|
||||||
|
## 3.6 Sauvegarde et restauration
|
||||||
|
|
||||||
|
### Exporter un realm
|
||||||
|
```bash
|
||||||
|
# Via API
|
||||||
|
curl -sk "$KEYCLOAK_URL/admin/realms/uac" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" > realm-uac-export.json
|
||||||
|
|
||||||
|
# Via pod (export complet avec utilisateurs)
|
||||||
|
kubectl exec -it deploy/keycloak -n keycloak -- /opt/keycloak/bin/kc.sh export \
|
||||||
|
--dir /tmp/export --realm uac
|
||||||
|
kubectl cp keycloak/keycloak-xxx:/tmp/export ./keycloak-export
|
||||||
|
```
|
||||||
|
|
||||||
|
### Importer un realm
|
||||||
|
```bash
|
||||||
|
curl -sk -X POST "$KEYCLOAK_URL/admin/realms" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d @realm-uac-export.json
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. Administration par Realm
|
||||||
|
|
||||||
|
## 4.1 Realm UAC (Université d'Abomey-Calavi)
|
||||||
|
|
||||||
|
### Informations
|
||||||
|
| Paramètre | Valeur |
|
||||||
|
|-----------|--------|
|
||||||
|
| Nom | uac |
|
||||||
|
| Display Name | Université d'Abomey-Calavi |
|
||||||
|
| URL | https://auth.rber.bj/realms/uac |
|
||||||
|
| LDAP | 10.24.112.33:389 |
|
||||||
|
| Base DN | DC=uac,DC=bj |
|
||||||
|
| Users DN | CN=Users,DC=uac,DC=bj |
|
||||||
|
| Bind DN | Administrator@UAC.BJ |
|
||||||
|
|
||||||
|
### Endpoints OIDC
|
||||||
|
```
|
||||||
|
Well-Known: https://auth.rber.bj/realms/uac/.well-known/openid-configuration
|
||||||
|
Authorization: https://auth.rber.bj/realms/uac/protocol/openid-connect/auth
|
||||||
|
Token: https://auth.rber.bj/realms/uac/protocol/openid-connect/token
|
||||||
|
UserInfo: https://auth.rber.bj/realms/uac/protocol/openid-connect/userinfo
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4.2 Realm UNA (Université Nationale d'Agriculture)
|
||||||
|
|
||||||
|
### Informations
|
||||||
|
| Paramètre | Valeur |
|
||||||
|
|-----------|--------|
|
||||||
|
| Nom | una |
|
||||||
|
| Display Name | Université Nationale d'Agriculture |
|
||||||
|
| URL | https://auth.rber.bj/realms/una |
|
||||||
|
| LDAP | 10.20.112.33:389 |
|
||||||
|
| Base DN | DC=una,DC=bj |
|
||||||
|
| Users DN | CN=Users,DC=una,DC=bj |
|
||||||
|
| Bind DN | Administrator@UNA.BJ |
|
||||||
|
|
||||||
|
### Endpoints OIDC
|
||||||
|
```
|
||||||
|
Well-Known: https://auth.rber.bj/realms/una/.well-known/openid-configuration
|
||||||
|
Authorization: https://auth.rber.bj/realms/una/protocol/openid-connect/auth
|
||||||
|
Token: https://auth.rber.bj/realms/una/protocol/openid-connect/token
|
||||||
|
UserInfo: https://auth.rber.bj/realms/una/protocol/openid-connect/userinfo
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4.3 Realm UNSTIM
|
||||||
|
|
||||||
|
### Informations
|
||||||
|
| Paramètre | Valeur |
|
||||||
|
|-----------|--------|
|
||||||
|
| Nom | unstim |
|
||||||
|
| Display Name | Université des Sciences, Technologies, Ingénierie et Mathématiques |
|
||||||
|
| URL | https://auth.rber.bj/realms/unstim |
|
||||||
|
| LDAP | 10.21.112.33:389 |
|
||||||
|
| Base DN | DC=unstim,DC=bj |
|
||||||
|
| Users DN | CN=Users,DC=unstim,DC=bj |
|
||||||
|
| Bind DN | Administrator@UNSTIM.BJ |
|
||||||
|
|
||||||
|
### Endpoints OIDC
|
||||||
|
```
|
||||||
|
Well-Known: https://auth.rber.bj/realms/unstim/.well-known/openid-configuration
|
||||||
|
Authorization: https://auth.rber.bj/realms/unstim/protocol/openid-connect/auth
|
||||||
|
Token: https://auth.rber.bj/realms/unstim/protocol/openid-connect/token
|
||||||
|
UserInfo: https://auth.rber.bj/realms/unstim/protocol/openid-connect/userinfo
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4.4 Realm UP (Université de Parakou)
|
||||||
|
|
||||||
|
### Informations
|
||||||
|
| Paramètre | Valeur |
|
||||||
|
|-----------|--------|
|
||||||
|
| Nom | up |
|
||||||
|
| Display Name | Université de Parakou |
|
||||||
|
| URL | https://auth.rber.bj/realms/up |
|
||||||
|
| LDAP | 10.25.112.33:389 |
|
||||||
|
| Base DN | DC=univ-parakou,DC=bj |
|
||||||
|
| Users DN | CN=Users,DC=univ-parakou,DC=bj |
|
||||||
|
| Bind DN | Administrator@UNIV-PARAKOU.BJ |
|
||||||
|
|
||||||
|
### Endpoints OIDC
|
||||||
|
```
|
||||||
|
Well-Known: https://auth.rber.bj/realms/up/.well-known/openid-configuration
|
||||||
|
Authorization: https://auth.rber.bj/realms/up/protocol/openid-connect/auth
|
||||||
|
Token: https://auth.rber.bj/realms/up/protocol/openid-connect/token
|
||||||
|
UserInfo: https://auth.rber.bj/realms/up/protocol/openid-connect/userinfo
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. Procédures Opérationnelles
|
||||||
|
|
||||||
|
## 5.1 Créer un client OIDC (exemple Moodle)
|
||||||
|
|
||||||
|
### Via Console
|
||||||
|
1. Realm concerné → Clients → Create client
|
||||||
|
2. Client ID: `moodle-unstim`
|
||||||
|
3. Client Protocol: `openid-connect`
|
||||||
|
4. Root URL: `https://elearning.unstim.bj`
|
||||||
|
5. Valid Redirect URIs: `https://elearning.unstim.bj/*`
|
||||||
|
6. Client authentication: ON
|
||||||
|
7. Sauvegarder → Onglet Credentials → Copier le secret
|
||||||
|
|
||||||
|
### Via API
|
||||||
|
```bash
|
||||||
|
curl -sk -X POST "$KEYCLOAK_URL/admin/realms/unstim/clients" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"clientId": "moodle-unstim",
|
||||||
|
"enabled": true,
|
||||||
|
"protocol": "openid-connect",
|
||||||
|
"rootUrl": "https://elearning.unstim.bj",
|
||||||
|
"redirectUris": ["https://elearning.unstim.bj/*"],
|
||||||
|
"publicClient": false,
|
||||||
|
"clientAuthenticatorType": "client-secret"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5.2 Créer un groupe
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sk -X POST "$KEYCLOAK_URL/admin/realms/uac/groups" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"name": "enseignants"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5.3 Créer un rôle
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sk -X POST "$KEYCLOAK_URL/admin/realms/uac/roles" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"name": "professeur", "description": "Rôle enseignant"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5.4 Mapper les groupes LDAP
|
||||||
|
|
||||||
|
Dans User Federation → ldap-xxx → Mappers → Add mapper :
|
||||||
|
- Name: `group-mapper`
|
||||||
|
- Mapper type: `group-ldap-mapper`
|
||||||
|
- LDAP Groups DN: `CN=Users,DC=uac,DC=bj`
|
||||||
|
- Group Object Classes: `group`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. Dépannage
|
||||||
|
|
||||||
|
## 6.1 Keycloak ne démarre pas
|
||||||
|
|
||||||
|
### Vérifier les logs
|
||||||
|
```bash
|
||||||
|
kubectl logs -n keycloak -l app=keycloak --tail=100
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problèmes courants
|
||||||
|
| Erreur | Cause | Solution |
|
||||||
|
|--------|-------|----------|
|
||||||
|
| Connection refused DB | PostgreSQL non accessible | Vérifier keycloak-pg-rw service |
|
||||||
|
| health/ready timeout | Démarrage lent | Augmenter initialDelaySeconds |
|
||||||
|
| OutOfMemory | Mémoire insuffisante | Augmenter limits.memory |
|
||||||
|
|
||||||
|
## 6.2 LDAP ne se connecte pas
|
||||||
|
|
||||||
|
### Tester la connectivité
|
||||||
|
```bash
|
||||||
|
kubectl run ldap-test --rm -it --image=alpine --restart=Never -- sh -c "
|
||||||
|
apk add --no-cache openldap-clients &&
|
||||||
|
ldapsearch -x -H ldap://10.24.112.33:389 -D 'Administrator@UAC.BJ' -W -b 'DC=uac,DC=bj' '(objectClass=person)' dn | head -10
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problèmes courants
|
||||||
|
| Erreur | Cause | Solution |
|
||||||
|
|--------|-------|----------|
|
||||||
|
| Can't contact LDAP server | Réseau/Firewall | Vérifier connectivité port 389 |
|
||||||
|
| Invalid credentials | Mauvais mot de passe | Vérifier bindCredential |
|
||||||
|
| No such object | Base DN incorrect | Vérifier usersDn |
|
||||||
|
|
||||||
|
## 6.3 Erreur 503 sur HAProxy
|
||||||
|
|
||||||
|
### Vérifier le backend
|
||||||
|
```bash
|
||||||
|
echo "show stat" | socat stdio /run/haproxy/admin.sock | grep k8s_ingress
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vérifier l'Ingress
|
||||||
|
```bash
|
||||||
|
kubectl get ingress -n keycloak
|
||||||
|
curl -sk -H "Host: auth.rber.bj" https://10.29.113.201/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6.4 Problème de session / cookies
|
||||||
|
|
||||||
|
Si les utilisateurs sont déconnectés aléatoirement :
|
||||||
|
1. Vérifier que la persistence de session est activée dans HAProxy
|
||||||
|
2. Vérifier que les cookies SERVERID sont transmis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sk -c cookies.txt -b cookies.txt https://auth.rber.bj/realms/uac/account
|
||||||
|
cat cookies.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Annexes
|
||||||
|
|
||||||
|
## A. Fichiers de configuration
|
||||||
|
|
||||||
|
### keycloak-deployment.yaml
|
||||||
|
Voir section 2.3.2
|
||||||
|
|
||||||
|
### haproxy.cfg (extrait Keycloak)
|
||||||
|
Voir section 2.4
|
||||||
|
|
||||||
|
## B. Mots de passe (À SÉCURISER)
|
||||||
|
|
||||||
|
**ATTENTION : Ces mots de passe doivent être stockés dans un coffre-fort sécurisé (Vault, etc.)**
|
||||||
|
|
||||||
|
| Service | Compte | Secret |
|
||||||
|
|---------|--------|--------|
|
||||||
|
| Keycloak Admin | admin | (dans secret k8s keycloak-admin-secret) |
|
||||||
|
| PostgreSQL | keycloak | (dans secret k8s keycloak-pg-app) |
|
||||||
|
| LDAP UAC | Administrator@UAC.BJ | *** |
|
||||||
|
| LDAP UNA | Administrator@UNA.BJ | *** |
|
||||||
|
| LDAP UNSTIM | Administrator@UNSTIM.BJ | *** |
|
||||||
|
| LDAP UP | Administrator@UNIV-PARAKOU.BJ | *** |
|
||||||
|
|
||||||
|
## C. Contacts
|
||||||
|
|
||||||
|
| Rôle | Contact |
|
||||||
|
|------|---------|
|
||||||
|
| Admin Infrastructure | À définir |
|
||||||
|
| Support Keycloak | À définir |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Document généré le 5 janvier 2026**
|
||||||
|
**Prochaine mise à jour prévue : Après configuration Identity Linking**
|
||||||
610
RBER Connect/keycloak-setup.md
Normal file
610
RBER Connect/keycloak-setup.md
Normal file
@@ -0,0 +1,610 @@
|
|||||||
|
# Procédure de configuration Keycloak pour l'API LDAP+
|
||||||
|
|
||||||
|
| Info | Valeur |
|
||||||
|
|------|--------|
|
||||||
|
| **Projet** | API LDAP+ — Universités Béninoises |
|
||||||
|
| **Référence** | `docs/keycloak-setup.md` |
|
||||||
|
| **Version** | 1.0 |
|
||||||
|
| **Date** | Mars 2026 |
|
||||||
|
| **Auteur** | IDADU TECH |
|
||||||
|
| **Destinataire** | Administrateur Keycloak (auth.rber.com) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sommaire
|
||||||
|
|
||||||
|
1. [Contexte](#1-contexte)
|
||||||
|
2. [Prérequis](#2-prérequis)
|
||||||
|
3. [Architecture concernée](#3-architecture-concernée)
|
||||||
|
4. [Étape 1 — Obtenir un token admin](#4-étape-1--obtenir-un-token-admin)
|
||||||
|
5. [Étape 2 — Créer le client api-admin dans chaque realm](#5-étape-2--créer-le-client-api-admin-dans-chaque-realm)
|
||||||
|
6. [Étape 3 — Récupérer les secrets des clients](#6-étape-3--récupérer-les-secrets-des-clients)
|
||||||
|
7. [Étape 4 — Attribuer les rôles au service account](#7-étape-4--attribuer-les-rôles-au-service-account)
|
||||||
|
8. [Étape 5 — Injecter les secrets dans Kubernetes](#8-étape-5--injecter-les-secrets-dans-kubernetes)
|
||||||
|
9. [Étape 6 — Redémarrer les pods](#9-étape-6--redémarrer-les-pods)
|
||||||
|
10. [Étape 7 — Vérifier le fonctionnement](#10-étape-7--vérifier-le-fonctionnement)
|
||||||
|
11. [Procédure via l'interface web Keycloak](#11-procédure-via-linterface-web-keycloak)
|
||||||
|
12. [Rotation des secrets](#12-rotation-des-secrets)
|
||||||
|
13. [Dépannage](#13-dépannage)
|
||||||
|
14. [Annexe — Résumé des rôles et accès](#14-annexe--résumé-des-rôles-et-accès)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Contexte
|
||||||
|
|
||||||
|
L'API LDAP+ est un service REST déployé sur le cluster RKE2 de RBER. Elle gère la structure académique (facultés, filières, groupes d'étudiants) des universités béninoises et synchronise ces groupes vers Keycloak.
|
||||||
|
|
||||||
|
L'API a besoin d'un **client Keycloak de type confidentiel** dans chaque realm universitaire pour :
|
||||||
|
|
||||||
|
1. **Valider les tokens JWT** des utilisateurs qui appellent l'API.
|
||||||
|
2. **Appeler l'Admin API Keycloak** pour créer/modifier des groupes et y affecter des utilisateurs (synchronisation).
|
||||||
|
|
||||||
|
Ce client s'appelle `api-admin`. Il doit être créé dans les 4 realms suivants :
|
||||||
|
|
||||||
|
| Realm | Université |
|
||||||
|
|-------|-----------|
|
||||||
|
| `uac` | Université d'Abomey-Calavi |
|
||||||
|
| `una` | Université Nationale d'Agriculture |
|
||||||
|
| `univ-parakou` | Université de Parakou |
|
||||||
|
| `unstim` | UNSTIM |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Prérequis
|
||||||
|
|
||||||
|
| Élément | Détail |
|
||||||
|
|---------|--------|
|
||||||
|
| Accès admin Keycloak | Compte avec droits admin sur `https://auth.rber.com` (realm `master` ou droits admin sur les 4 realms) |
|
||||||
|
| curl | Installé sur la machine depuis laquelle tu exécutes les commandes |
|
||||||
|
| python3 | Pour parser les réponses JSON (installé par défaut sur la plupart des Linux) |
|
||||||
|
| kubectl | Accès au cluster RKE2 avec droits sur le namespace `ldap-api` (pour l'injection des secrets) |
|
||||||
|
|
||||||
|
> **Note :** si tu préfères utiliser l'interface web de Keycloak plutôt que les commandes curl, va directement à la [section 11](#11-procédure-via-linterface-web-keycloak).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Architecture concernée
|
||||||
|
|
||||||
|
```
|
||||||
|
Applications universitaires
|
||||||
|
│
|
||||||
|
│ JWT (émis par Keycloak)
|
||||||
|
▼
|
||||||
|
┌──────────────────────────────────┐
|
||||||
|
│ API LDAP+ (api.rber.bj) │
|
||||||
|
│ │
|
||||||
|
│ 1. Valide le JWT │──── Keycloak (auth.rber.com)
|
||||||
|
│ (clés publiques du realm) │ ← c'est ici qu'on crée le client
|
||||||
|
│ │
|
||||||
|
│ 2. Sync groupes → Keycloak │──── Keycloak Admin API
|
||||||
|
│ (via client api-admin) │ ← c'est ici qu'on a besoin du secret
|
||||||
|
└──────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Étape 1 — Obtenir un token admin
|
||||||
|
|
||||||
|
Ce token permet d'appeler l'Admin API de Keycloak pour créer les clients.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Variables à adapter
|
||||||
|
KEYCLOAK_URL="https://auth.rber.com"
|
||||||
|
ADMIN_USER="admin"
|
||||||
|
ADMIN_PASS="<mot_de_passe_admin_keycloak>"
|
||||||
|
|
||||||
|
# Obtenir le token
|
||||||
|
TOKEN=$(curl -s -X POST \
|
||||||
|
"${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \
|
||||||
|
-d "client_id=admin-cli" \
|
||||||
|
-d "username=${ADMIN_USER}" \
|
||||||
|
-d "password=${ADMIN_PASS}" \
|
||||||
|
-d "grant_type=password" \
|
||||||
|
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
|
||||||
|
|
||||||
|
# Vérifier que le token est récupéré
|
||||||
|
if [ -z "$TOKEN" ]; then
|
||||||
|
echo "ERREUR : impossible d'obtenir le token admin."
|
||||||
|
echo "Vérifie le nom d'utilisateur, le mot de passe et l'URL."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Token admin obtenu (${#TOKEN} caractères)"
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Durée de validité :** le token admin expire au bout de 60 secondes par défaut. Si les étapes suivantes échouent avec une erreur 401, relance cette commande pour en obtenir un nouveau.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Étape 2 — Créer le client api-admin dans chaque realm
|
||||||
|
|
||||||
|
Le script suivant crée un client `api-admin` dans les 4 realms.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for REALM in uac una univ-parakou unstim; do
|
||||||
|
echo ""
|
||||||
|
echo "=== Realm : ${REALM} ==="
|
||||||
|
|
||||||
|
HTTP_CODE=$(curl -s -o /tmp/kc_response_${REALM}.json -w "%{http_code}" \
|
||||||
|
-X POST "${KEYCLOAK_URL}/admin/realms/${REALM}/clients" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"clientId": "api-admin",
|
||||||
|
"name": "API LDAP+ Administration",
|
||||||
|
"description": "Client utilisé par l'\''API LDAP+ pour valider les JWT et synchroniser les groupes académiques.",
|
||||||
|
"enabled": true,
|
||||||
|
"protocol": "openid-connect",
|
||||||
|
"publicClient": false,
|
||||||
|
"serviceAccountsEnabled": true,
|
||||||
|
"authorizationServicesEnabled": false,
|
||||||
|
"directAccessGrantsEnabled": false,
|
||||||
|
"standardFlowEnabled": false,
|
||||||
|
"clientAuthenticatorType": "client-secret",
|
||||||
|
"defaultClientScopes": ["email", "profile", "roles"],
|
||||||
|
"attributes": {
|
||||||
|
"use.refresh.tokens": "false"
|
||||||
|
}
|
||||||
|
}')
|
||||||
|
|
||||||
|
case "$HTTP_CODE" in
|
||||||
|
201) echo " SUCCÈS — client api-admin créé" ;;
|
||||||
|
409) echo " EXISTE DÉJÀ — aucune action nécessaire" ;;
|
||||||
|
401) echo " ERREUR 401 — token expiré, relance l'étape 1" ;;
|
||||||
|
*) echo " ERREUR HTTP ${HTTP_CODE} :"
|
||||||
|
cat /tmp/kc_response_${REALM}.json
|
||||||
|
echo "" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explication des paramètres du client
|
||||||
|
|
||||||
|
| Paramètre | Valeur | Rôle |
|
||||||
|
|-----------|--------|------|
|
||||||
|
| `publicClient` | `false` | Client confidentiel : un `client_secret` est généré. Sans ça, pas de secret. |
|
||||||
|
| `serviceAccountsEnabled` | `true` | Active un compte de service. L'API utilise ce compte pour appeler l'Admin API Keycloak (sync des groupes). |
|
||||||
|
| `standardFlowEnabled` | `false` | Désactive le flux de login navigateur. L'API ne redirige pas des utilisateurs vers Keycloak, elle valide des tokens déjà émis. |
|
||||||
|
| `directAccessGrantsEnabled` | `false` | Désactive le Resource Owner Password Grant. L'API ne collecte jamais de mots de passe. |
|
||||||
|
| `clientAuthenticatorType` | `client-secret` | Méthode d'authentification du client auprès de Keycloak. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Étape 3 — Récupérer les secrets des clients
|
||||||
|
|
||||||
|
Après la création, chaque client a un `client_secret` généré automatiquement. Ce script le récupère :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo " SECRETS CLIENT api-admin PAR REALM"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo " CONSERVE CES VALEURS EN LIEU SÛR."
|
||||||
|
echo " NE LES PARTAGE PAS PAR EMAIL OU CHAT."
|
||||||
|
echo ""
|
||||||
|
echo "--------------------------------------------"
|
||||||
|
|
||||||
|
for REALM in uac una univ-parakou unstim; do
|
||||||
|
# Trouver l'ID interne du client (UUID Keycloak, différent du clientId)
|
||||||
|
CLIENT_UUID=$(curl -s \
|
||||||
|
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients?clientId=api-admin" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
| python3 -c "import sys,json; data=json.load(sys.stdin); print(data[0]['id'] if data else 'NOT_FOUND')")
|
||||||
|
|
||||||
|
if [ "$CLIENT_UUID" = "NOT_FOUND" ]; then
|
||||||
|
echo " ${REALM}: ERREUR — client api-admin introuvable dans ce realm"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Récupérer le secret
|
||||||
|
SECRET=$(curl -s \
|
||||||
|
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${CLIENT_UUID}/client-secret" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
| python3 -c "import sys,json; print(json.load(sys.stdin).get('value', 'ERREUR'))")
|
||||||
|
|
||||||
|
echo " ${REALM}: ${SECRET}"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "--------------------------------------------"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Sortie attendue :**
|
||||||
|
```
|
||||||
|
uac: a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
||||||
|
una: b2c3d4e5-f6a7-8901-bcde-f12345678901
|
||||||
|
univ-parakou: c3d4e5f6-a7b8-9012-cdef-123456789012
|
||||||
|
unstim: d4e5f6a7-b8c9-0123-defa-234567890123
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note ces valeurs.** Tu en auras besoin pour l'étape 5.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Étape 4 — Attribuer les rôles au service account
|
||||||
|
|
||||||
|
Le compte de service du client `api-admin` a besoin de droits pour gérer les groupes et lire les utilisateurs dans chaque realm. Sans ces rôles, la synchronisation des groupes académiques échouera.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for REALM in uac una univ-parakou unstim; do
|
||||||
|
echo ""
|
||||||
|
echo "=== Attribution des rôles — realm ${REALM} ==="
|
||||||
|
|
||||||
|
# ID du client api-admin
|
||||||
|
CLIENT_UUID=$(curl -s \
|
||||||
|
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients?clientId=api-admin" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
| python3 -c "import sys,json; print(json.load(sys.stdin)[0]['id'])")
|
||||||
|
|
||||||
|
# ID du client realm-management (client interne qui porte les rôles d'admin)
|
||||||
|
REALM_MGMT_UUID=$(curl -s \
|
||||||
|
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients?clientId=realm-management" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
| python3 -c "import sys,json; print(json.load(sys.stdin)[0]['id'])")
|
||||||
|
|
||||||
|
# ID du service account (l'utilisateur technique créé automatiquement)
|
||||||
|
SA_USER_ID=$(curl -s \
|
||||||
|
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${CLIENT_UUID}/service-account-user" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
| python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
|
||||||
|
|
||||||
|
# Rôles nécessaires :
|
||||||
|
# manage-users → créer/modifier/supprimer des utilisateurs dans les groupes
|
||||||
|
# query-users → lister les utilisateurs
|
||||||
|
# query-groups → lister les groupes
|
||||||
|
# manage-clients → (optionnel) gérer les scopes si nécessaire
|
||||||
|
# view-users → voir le détail des utilisateurs
|
||||||
|
|
||||||
|
for ROLE_NAME in manage-users query-groups query-users manage-clients view-users; do
|
||||||
|
# Récupérer la définition JSON du rôle
|
||||||
|
ROLE_JSON=$(curl -s \
|
||||||
|
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${REALM_MGMT_UUID}/roles/${ROLE_NAME}" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}")
|
||||||
|
|
||||||
|
# Vérifier que le rôle existe
|
||||||
|
ROLE_CHECK=$(echo "$ROLE_JSON" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('name','ERREUR'))" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "$ROLE_CHECK" = "ERREUR" ] || [ -z "$ROLE_CHECK" ]; then
|
||||||
|
echo " ATTENTION : rôle ${ROLE_NAME} introuvable dans realm-management de ${REALM}"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Attribuer le rôle au service account
|
||||||
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||||
|
-X POST "${KEYCLOAK_URL}/admin/realms/${REALM}/users/${SA_USER_ID}/role-mappings/clients/${REALM_MGMT_UUID}" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "[${ROLE_JSON}]")
|
||||||
|
|
||||||
|
if [ "$HTTP_CODE" = "204" ]; then
|
||||||
|
echo " ${ROLE_NAME} → attribué"
|
||||||
|
else
|
||||||
|
echo " ${ROLE_NAME} → ERREUR HTTP ${HTTP_CODE}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Récapitulatif des rôles attribués
|
||||||
|
|
||||||
|
| Rôle realm-management | Usage par l'API LDAP+ |
|
||||||
|
|-----------------------|-----------------------|
|
||||||
|
| `manage-users` | Affecter/retirer des utilisateurs des groupes Keycloak |
|
||||||
|
| `query-users` | Lister les utilisateurs d'un realm |
|
||||||
|
| `view-users` | Voir le détail d'un utilisateur |
|
||||||
|
| `query-groups` | Lister les groupes existants dans un realm |
|
||||||
|
| `manage-clients` | Optionnel — gestion des scopes si nécessaire |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Étape 5 — Injecter les secrets dans Kubernetes
|
||||||
|
|
||||||
|
Cette étape est exécutée par la personne ayant accès au cluster RKE2.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Remplacer les valeurs ci-dessous par les vrais secrets récupérés à l'étape 3
|
||||||
|
|
||||||
|
kubectl create secret generic keycloak-secrets \
|
||||||
|
--from-literal=UAC_KC_SECRET="<secret_realm_uac>" \
|
||||||
|
--from-literal=UNA_KC_SECRET="<secret_realm_una>" \
|
||||||
|
--from-literal=UP_KC_SECRET="<secret_realm_univ-parakou>" \
|
||||||
|
--from-literal=UNSTIM_KC_SECRET="<secret_realm_unstim>" \
|
||||||
|
-n ldap-api \
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
```
|
||||||
|
|
||||||
|
**Vérification :**
|
||||||
|
```bash
|
||||||
|
# Le secret doit contenir 4 clés
|
||||||
|
kubectl get secret keycloak-secrets -n ldap-api -o jsonpath='{.data}' | python3 -c "
|
||||||
|
import sys,json
|
||||||
|
data = json.load(sys.stdin)
|
||||||
|
for key in sorted(data.keys()):
|
||||||
|
print(f' {key}: (encodé, {len(data[key])} chars)')
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Étape 6 — Redémarrer les pods
|
||||||
|
|
||||||
|
Les pods doivent redémarrer pour charger les nouveaux secrets depuis les variables d'environnement.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Redémarrer les pods API
|
||||||
|
kubectl rollout restart deployment/ldap-api -n ldap-api
|
||||||
|
|
||||||
|
# Redémarrer les workers Celery (sync Keycloak)
|
||||||
|
kubectl rollout restart deployment/celery-worker -n ldap-workers
|
||||||
|
kubectl rollout restart deployment/celery-beat -n ldap-workers
|
||||||
|
|
||||||
|
# Surveiller le redémarrage
|
||||||
|
kubectl get pods -n ldap-api -w
|
||||||
|
kubectl get pods -n ldap-workers -w
|
||||||
|
```
|
||||||
|
|
||||||
|
**Résultat attendu :** tous les pods en état `Running` avec `READY 1/1`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Étape 7 — Vérifier le fonctionnement
|
||||||
|
|
||||||
|
### 10.1 Health check
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s https://uac.api.rber.bj/health | python3 -m json.tool
|
||||||
|
```
|
||||||
|
|
||||||
|
Résultat attendu — tous les composants `healthy` :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "healthy",
|
||||||
|
"components": {
|
||||||
|
"database": "healthy",
|
||||||
|
"redis": "healthy",
|
||||||
|
"ldap": "healthy",
|
||||||
|
"keycloak": "healthy"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.2 Obtenir un token de test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
KEYCLOAK_URL="https://auth.rber.com"
|
||||||
|
REALM="uac"
|
||||||
|
CLIENT_SECRET="<secret_realm_uac>"
|
||||||
|
|
||||||
|
# Token via client credentials (service account)
|
||||||
|
TOKEN=$(curl -s -X POST \
|
||||||
|
"${KEYCLOAK_URL}/realms/${REALM}/protocol/openid-connect/token" \
|
||||||
|
-d "client_id=api-admin" \
|
||||||
|
-d "client_secret=${CLIENT_SECRET}" \
|
||||||
|
-d "grant_type=client_credentials" \
|
||||||
|
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
|
||||||
|
|
||||||
|
echo "Token obtenu (${#TOKEN} caractères)"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.3 Tester les endpoints protégés
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Lister les facultés (doit retourner une liste, même vide)
|
||||||
|
curl -s https://uac.api.rber.bj/faculties \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" | python3 -m json.tool
|
||||||
|
|
||||||
|
# Lister les utilisateurs LDAP (5 premiers)
|
||||||
|
curl -s "https://uac.api.rber.bj/users?limit=5" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" | python3 -m json.tool
|
||||||
|
|
||||||
|
# Lister les groupes
|
||||||
|
curl -s https://uac.api.rber.bj/groups \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" | python3 -m json.tool
|
||||||
|
|
||||||
|
# Statut de la synchronisation
|
||||||
|
curl -s https://uac.api.rber.bj/sync/status \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" | python3 -m json.tool
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.4 Tester la synchronisation Keycloak
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Déclencher une sync manuelle
|
||||||
|
curl -s -X POST https://uac.api.rber.bj/sync/keycloak \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" | python3 -m json.tool
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.5 Vérifier dans Keycloak
|
||||||
|
|
||||||
|
Après une sync réussie, connecte-toi à `https://auth.rber.com`, va dans le realm `uac` → Groups. Tu dois voir apparaître l'arborescence :
|
||||||
|
|
||||||
|
```
|
||||||
|
/facultes
|
||||||
|
/fast
|
||||||
|
/informatique
|
||||||
|
/L1-2024-2025
|
||||||
|
/L2-2024-2025
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Procédure via l'interface web Keycloak
|
||||||
|
|
||||||
|
Si tu préfères utiliser l'interface graphique plutôt que les commandes curl, voici la marche à suivre. **Répète cette procédure pour chaque realm** (uac, una, univ-parakou, unstim).
|
||||||
|
|
||||||
|
### 11.1 Créer le client
|
||||||
|
|
||||||
|
1. Connecte-toi à `https://auth.rber.com/admin`
|
||||||
|
2. Sélectionne le realm dans le menu déroulant en haut à gauche (ex : `uac`)
|
||||||
|
3. Menu latéral → **Clients** → bouton **Create client**
|
||||||
|
4. Remplis les champs :
|
||||||
|
|
||||||
|
| Champ | Valeur |
|
||||||
|
|-------|--------|
|
||||||
|
| Client type | OpenID Connect |
|
||||||
|
| Client ID | `api-admin` |
|
||||||
|
| Name | `API LDAP+ Administration` |
|
||||||
|
| Description | `Client utilisé par l'API LDAP+ pour valider les JWT et synchroniser les groupes académiques.` |
|
||||||
|
|
||||||
|
5. Clique **Next**
|
||||||
|
6. Configuration d'authentification :
|
||||||
|
|
||||||
|
| Champ | Valeur |
|
||||||
|
|-------|--------|
|
||||||
|
| Client authentication | **ON** (active le mode confidentiel → génère un secret) |
|
||||||
|
| Authorization | OFF |
|
||||||
|
| Standard flow | **OFF** |
|
||||||
|
| Direct access grants | **OFF** |
|
||||||
|
| Service accounts roles | **ON** (nécessaire pour la sync) |
|
||||||
|
|
||||||
|
7. Clique **Next** puis **Save**
|
||||||
|
|
||||||
|
### 11.2 Récupérer le secret
|
||||||
|
|
||||||
|
1. Dans la page du client `api-admin`, onglet **Credentials**
|
||||||
|
2. Le champ **Client secret** contient la valeur à noter
|
||||||
|
3. **Note cette valeur** — tu en auras besoin pour l'injection Kubernetes
|
||||||
|
|
||||||
|
### 11.3 Attribuer les rôles au service account
|
||||||
|
|
||||||
|
1. Dans la page du client `api-admin`, onglet **Service account roles**
|
||||||
|
2. Clique **Assign role**
|
||||||
|
3. Dans le filtre, sélectionne **Filter by clients**
|
||||||
|
4. Cherche `realm-management`
|
||||||
|
5. Coche les rôles suivants :
|
||||||
|
|
||||||
|
| Rôle | Coché |
|
||||||
|
|------|-------|
|
||||||
|
| `manage-users` | ✅ |
|
||||||
|
| `query-users` | ✅ |
|
||||||
|
| `view-users` | ✅ |
|
||||||
|
| `query-groups` | ✅ |
|
||||||
|
| `manage-clients` | ✅ |
|
||||||
|
|
||||||
|
6. Clique **Assign**
|
||||||
|
|
||||||
|
### 11.4 Répéter
|
||||||
|
|
||||||
|
Répète les sections 11.1 à 11.3 pour les realms `una`, `univ-parakou` et `unstim`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Rotation des secrets
|
||||||
|
|
||||||
|
Si un secret est compromis ou si la politique de sécurité exige une rotation périodique :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Régénérer le secret dans Keycloak (exemple pour le realm uac)
|
||||||
|
REALM="uac"
|
||||||
|
CLIENT_UUID=$(curl -s \
|
||||||
|
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients?clientId=api-admin" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
| python3 -c "import sys,json; print(json.load(sys.stdin)[0]['id'])")
|
||||||
|
|
||||||
|
# Générer un nouveau secret
|
||||||
|
NEW_SECRET=$(curl -s -X POST \
|
||||||
|
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${CLIENT_UUID}/client-secret" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
| python3 -c "import sys,json; print(json.load(sys.stdin)['value'])")
|
||||||
|
|
||||||
|
echo "Nouveau secret pour ${REALM}: ${NEW_SECRET}"
|
||||||
|
|
||||||
|
# 2. Mettre à jour le secret Kubernetes
|
||||||
|
kubectl create secret generic keycloak-secrets \
|
||||||
|
--from-literal=UAC_KC_SECRET="${NEW_SECRET}" \
|
||||||
|
--from-literal=UNA_KC_SECRET="<garder_ancien_si_pas_changé>" \
|
||||||
|
--from-literal=UP_KC_SECRET="<garder_ancien_si_pas_changé>" \
|
||||||
|
--from-literal=UNSTIM_KC_SECRET="<garder_ancien_si_pas_changé>" \
|
||||||
|
-n ldap-api \
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
|
# 3. Redémarrer les pods
|
||||||
|
kubectl rollout restart deployment/ldap-api -n ldap-api
|
||||||
|
kubectl rollout restart deployment/celery-worker -n ldap-workers
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Dépannage
|
||||||
|
|
||||||
|
### Le token admin ne s'obtient pas (étape 1)
|
||||||
|
|
||||||
|
| Symptôme | Cause probable | Solution |
|
||||||
|
|----------|---------------|----------|
|
||||||
|
| `{"error":"invalid_grant"}` | Mauvais identifiant ou mot de passe | Vérifie les credentials admin |
|
||||||
|
| `curl: (7) Failed to connect` | Keycloak inaccessible | Vérifie que `auth.rber.com` est joignable depuis ta machine |
|
||||||
|
| `{"error":"unauthorized_client"}` | Le client `admin-cli` est désactivé | Active-le dans le realm `master` → Clients → `admin-cli` → Enabled: ON |
|
||||||
|
|
||||||
|
### La création du client échoue (étape 2)
|
||||||
|
|
||||||
|
| Code HTTP | Cause | Solution |
|
||||||
|
|-----------|-------|----------|
|
||||||
|
| 401 | Token expiré | Relance l'étape 1 |
|
||||||
|
| 403 | Pas les droits admin sur le realm | Vérifie que ton compte a le rôle `admin` dans le realm `master` |
|
||||||
|
| 409 | Le client `api-admin` existe déjà | Passe à l'étape 3 |
|
||||||
|
|
||||||
|
### Le health check retourne `keycloak: unhealthy` (étape 7)
|
||||||
|
|
||||||
|
| Cause probable | Solution |
|
||||||
|
|---------------|----------|
|
||||||
|
| Secret incorrect dans Kubernetes | Relance l'étape 3 pour récupérer les vrais secrets, puis étape 5 |
|
||||||
|
| Pods pas redémarrés | Relance l'étape 6 |
|
||||||
|
| Keycloak inaccessible depuis le cluster | Vérifier que les pods peuvent atteindre `auth.rber.com:443` |
|
||||||
|
|
||||||
|
### Les endpoints protégés retournent 401
|
||||||
|
|
||||||
|
| Cause probable | Solution |
|
||||||
|
|---------------|----------|
|
||||||
|
| Token expiré | Récupère un nouveau token (étape 7.2) |
|
||||||
|
| Client `api-admin` désactivé | Vérifie dans Keycloak → Clients → api-admin → Enabled: ON |
|
||||||
|
| Secret ne correspond pas | Régénère le secret (section 12) |
|
||||||
|
|
||||||
|
### La sync Keycloak échoue
|
||||||
|
|
||||||
|
| Cause probable | Solution |
|
||||||
|
|---------------|----------|
|
||||||
|
| Rôles service account manquants | Relance l'étape 4 |
|
||||||
|
| `403 Forbidden` dans les logs | Le service account n'a pas `manage-users` — voir étape 4 |
|
||||||
|
| Pas de groupes dans Keycloak après sync | Vérifie qu'il y a des groupes dans PostgreSQL (`GET /groups`) |
|
||||||
|
|
||||||
|
Consulter les logs des pods :
|
||||||
|
```bash
|
||||||
|
# Logs API
|
||||||
|
kubectl logs -l app=ldap-api -n ldap-api --tail=50
|
||||||
|
|
||||||
|
# Logs Celery Worker (sync)
|
||||||
|
kubectl logs -l app=celery-worker -n ldap-workers --tail=50
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Annexe — Résumé des rôles et accès
|
||||||
|
|
||||||
|
### Ce que le client api-admin peut faire
|
||||||
|
|
||||||
|
| Action | Realm concerné | Utilisé pour |
|
||||||
|
|--------|---------------|-------------|
|
||||||
|
| Valider des tokens JWT | Chaque realm | Authentification des requêtes API |
|
||||||
|
| Lister les utilisateurs | Chaque realm | Vérification d'existence (sync) |
|
||||||
|
| Créer/modifier/supprimer des groupes | Chaque realm | Synchronisation de la structure académique |
|
||||||
|
| Affecter des utilisateurs à des groupes | Chaque realm | Synchronisation des membres |
|
||||||
|
|
||||||
|
### Ce que le client api-admin ne peut PAS faire
|
||||||
|
|
||||||
|
| Action | Raison |
|
||||||
|
|--------|--------|
|
||||||
|
| Connecter des utilisateurs (login) | `standardFlowEnabled: false` |
|
||||||
|
| Collecter des mots de passe | `directAccessGrantsEnabled: false` |
|
||||||
|
| Modifier la configuration du realm | Pas de rôle `realm-admin` |
|
||||||
|
| Accéder aux données d'un autre realm | Chaque client est limité à son realm |
|
||||||
|
|
||||||
|
### Variables d'environnement attendues par l'API
|
||||||
|
|
||||||
|
| Variable | Secret Kubernetes | Contenu |
|
||||||
|
|----------|-------------------|---------|
|
||||||
|
| `UAC_KC_SECRET` | `keycloak-secrets` | Client secret du realm `uac` |
|
||||||
|
| `UNA_KC_SECRET` | `keycloak-secrets` | Client secret du realm `una` |
|
||||||
|
| `UP_KC_SECRET` | `keycloak-secrets` | Client secret du realm `univ-parakou` |
|
||||||
|
| `UNSTIM_KC_SECRET` | `keycloak-secrets` | Client secret du realm `unstim` |
|
||||||
Reference in New Issue
Block a user