a97399c2bf
configmap-realm.yaml was a first-install seed: `start --import-realm` skips a realm that already exists and logs "Import skipped", so editing the file changed nothing on a restart and the truth drifted into the database. It already had — four scope grants existed only in the running realm. There is no option to make the startup import overwrite. The --override flag exists only on the standalone `import` command, and the Keycloak documentation is explicit that re-creating realms means running that command before starting the server. So the import moves into an init container that runs `import --dir ... --override true`, and --import-realm comes off the main container's args. The init container empties JAVA_OPTS_APPEND for itself. It inherits the main container's env, which loads the OpenTelemetry agent from a volume the other init container downloads into and this one does not mount; inherited verbatim, the JVM fails before the import runs. What this costs, deliberately: the realm is replaced at every start, so anything created at runtime and not written back to this file is lost, and active sessions are dropped. Checked before enabling it — the five users in the realm are the five declared here, and the only live sessions were two LibreChat ones. Nothing is to be created through the admin console expecting it to last. Verified after the switch: the import logs "Strategy: OVERWRITE_EXISTING", the realm still has its 21 clients with no optionalClientScopes divergence, and the full identity chain still returns 200 — pm-user token, exchanged by the Catalogue agent, accepted by catalog-mcp. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Infra Keycloak (Kubernetes / CaC)
Déploiement de Keycloak 26.6.0 et PostgreSQL 16 en Configuration-as-Code (CaC) sous Kubernetes dans le namespace keycloak.
Structure du Répertoire
k8s/
├── kustomization.yaml # Manifeste Kustomize principal
├── namespace.yaml # Namespace keycloak
├── secret.yaml # Secrets (Admin, DB, OIDC secrets)
├── secret.example.yaml # Gabarit des secrets
├── postgresql/
│ ├── pvc.yaml # Stockage persistant PostgreSQL (2Gi)
│ ├── statefulset.yaml # Base de données PostgreSQL 16
│ └── service.yaml # Service PostgreSQL (port 5432)
├── keycloak/
│ ├── configmap-realm.yaml # Realm classic-models (Configuration-as-Code)
│ ├── deployment.yaml # Déploiement Keycloak 26.6.0 (--import-realm)
│ └── service.yaml # Service Keycloak (ports 8080, 9000)
└── ingress/
├── tls.yaml # Secret TLS wildcard (*.sttlab.local)
└── ingress.yaml # Ingress NGINX (keycloak.sttlab.local)
Fonctionnalités Clés
- Version Keycloak :
quay.io/keycloak/keycloak:26.6.0(Runtime Quarkus). - Import Automatique du Realm (CaC) : Le realm
classic-modelsest chargé au démarrage via l'option--import-realmdepuis leConfigMapmonté dans/opt/keycloak/data/import/. - Token Exchange RFC 8693 : Activé via
KC_FEATURES: "token-exchange,admin-fine-grained-authz". - Clients Configurés :
librechat: Client OIDC pour le login et le flux OAuth MCP.mcp-catalog: Audience du serveur MCP.kong-token-exchange-broker: Échangeur de jetons RFC 8693 de Kong Gateway.catalog-api&classic-models-api: Audiences des services backend.
- Utilisateurs & Rôles :
admin-user(admin)pm-user(product_manager)ro-user(read_only)customer-user(customer)support-user(support)
Déploiement
k8s/secret.yaml n'est pas versionné : il porte les mots de passe réels (base
PostgreSQL, admin Keycloak, secrets clients OIDC). Le créer une fois depuis le
gabarit, puis renseigner chaque <CHANGE_ME> :
cp k8s/secret.example.yaml k8s/secret.yaml
$EDITOR k8s/secret.yaml
k8s/kustomization.yaml le référence, donc le déploiement échoue tant qu'il
est absent.
kubectl apply -k k8s/