From 494dbb32dcb8c3c42c8f5be2e89b63e13059168a Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Fri, 3 Jul 2026 13:37:33 -0400 Subject: [PATCH] =?UTF-8?q?serveur=5Fkeycloak=20:=20enregistrement=20des?= =?UTF-8?q?=20clients=20OIDC=20codifi=C3=A9=20(kcadm=20idempotent)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Liste déclarative serveur_keycloak_clients (clientId, redirect_uris, web_origins, secret) → tasks/clients-oidc.yml enregistre chaque client confidentiel via kcadm (create-si-absent, no_log). Décision côté Keycloak (creds admin non répandus dans les rôles app) ; secret = même var de voûte que l'app. Prouvé : client grafana supprimé → rôle → recréé → testmail se connecte à Grafana ; redéploiement idempotent (changed=0). Déploiement Grafana au SSO désormais autonome. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 8 +++++ roles/serveur_keycloak/defaults/main.yml | 11 ++++++ roles/serveur_keycloak/tasks/clients-oidc.yml | 34 +++++++++++++++++++ roles/serveur_keycloak/tasks/main.yml | 4 +++ 4 files changed, 57 insertions(+) create mode 100644 roles/serveur_keycloak/tasks/clients-oidc.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e6bda7..e981800 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,14 @@ côté keycloak) ; (2) la **résolution** `keycloak.…internal → edge` sur obs-01 est un `/etc/hosts` manuel (**PowerDNS devrait porter les A d'exposition** — chaînon récurrent) ; (3) mapping de rôles Grafana (tous Viewer par défaut). +- **`serveur_keycloak` — enregistrement des clients OIDC codifié (gap précédent fermé).** Le rôle + gère une **liste déclarative** `serveur_keycloak_clients` (`clientId`, `redirect_uris`, + `web_origins`, `secret`) et enregistre chaque client confidentiel via **kcadm idempotent** + (`tasks/clients-oidc.yml`, create-si-absent, `no_log`). Décision : **côté Keycloak** (les creds + admin restent dans le seul rôle Keycloak, pas répandus dans chaque rôle app) ; le `secret` + référence la même variable de voûte que l'app. **Prouvé** : client `grafana` supprimé → rôle → + recréé → `testmail` se connecte à Grafana (`login: testmail`) ; redéploiement **idempotent** + (`changed=0`). Le déploiement de Grafana au SSO est désormais **autonome**. ## 2026-07-02 diff --git a/roles/serveur_keycloak/defaults/main.yml b/roles/serveur_keycloak/defaults/main.yml index 4db9608..6685728 100644 --- a/roles/serveur_keycloak/defaults/main.yml +++ b/roles/serveur_keycloak/defaults/main.yml @@ -35,3 +35,14 @@ serveur_keycloak_ldap_uuid_attr: "entryUUID" serveur_keycloak_ldap_object_classes: "inetOrgPerson" # Bundle d'AC de confiance (contient la racine step_ca, posée par client_pki) pour LDAPS. serveur_keycloak_truststore_path: "/etc/ssl/certs/ca-certificates.crt" + +# --- Clients OIDC applicatifs (enregistres dans le realm, kcadm idempotent) --- +# Chaque app qui delegue son auth a Keycloak declare ici son client confidentiel. +# Le 'secret' reference la MEME variable de voute que l'app (ex. grafana : vault_grafana_oidc). +# Ex. (a mettre en group_var d'instance, pas en defaut) : +# serveur_keycloak_clients: +# - clientId: grafana +# redirect_uris: ["https://grafana.{{ domaine_interne }}/login/generic_oauth"] +# web_origins: ["https://grafana.{{ domaine_interne }}"] +# secret: "{{ vault_grafana_oidc }}" +serveur_keycloak_clients: [] diff --git a/roles/serveur_keycloak/tasks/clients-oidc.yml b/roles/serveur_keycloak/tasks/clients-oidc.yml new file mode 100644 index 0000000..9a98b74 --- /dev/null +++ b/roles/serveur_keycloak/tasks/clients-oidc.yml @@ -0,0 +1,34 @@ +--- +# Enregistre les clients OIDC applicatifs dans le realm via kcadm — idempotent. +# Crée le client s'il est absent (confidentiel, authorization code). La réconciliation +# fine (mettre à jour un client existant) n'est pas faite : create-si-absent. +- name: Enregistrer les clients OIDC (kcadm, idempotent) + ansible.builtin.shell: + executable: /bin/bash + cmd: | + set -euo pipefail + KC={{ serveur_keycloak_home }}/bin/kcadm.sh + "$KC" config credentials --server http://localhost:8080 --realm master \ + --user {{ serveur_keycloak_admin_user }} --password "$KC_ADMIN_PW" >/dev/null + if "$KC" get clients -r {{ serveur_keycloak_realm }} -q clientId={{ item.clientId }} 2>/dev/null \ + | grep -q '"{{ item.clientId }}"'; then + echo SETOPS_OK + else + "$KC" create clients -r {{ serveur_keycloak_realm }} \ + -s clientId={{ item.clientId }} -s enabled=true -s protocol=openid-connect \ + -s publicClient=false -s standardFlowEnabled=true -s directAccessGrantsEnabled=false \ + -s 'redirectUris={{ item.redirect_uris | to_json }}' \ + -s 'webOrigins={{ item.web_origins | default([]) | to_json }}' \ + -s secret="$OIDC_SECRET" >/dev/null + echo SETOPS_CHANGED + fi + environment: + KC_ADMIN_PW: "{{ serveur_keycloak_admin_password }}" + OIDC_SECRET: "{{ item.secret }}" + loop: "{{ serveur_keycloak_clients }}" + loop_control: + label: "{{ item.clientId }}" + register: serveur_keycloak_client_reg + changed_when: "'SETOPS_CHANGED' in serveur_keycloak_client_reg.stdout" + no_log: true + when: not ansible_check_mode diff --git a/roles/serveur_keycloak/tasks/main.yml b/roles/serveur_keycloak/tasks/main.yml index ed8a183..8e8a697 100644 --- a/roles/serveur_keycloak/tasks/main.yml +++ b/roles/serveur_keycloak/tasks/main.yml @@ -132,3 +132,7 @@ - name: Configurer la federation LDAP (realm + provider OpenLDAP) ansible.builtin.include_tasks: federation-ldap.yml when: serveur_keycloak_ldap_federation | bool + +- name: Enregistrer les clients OIDC applicatifs + ansible.builtin.include_tasks: clients-oidc.yml + when: serveur_keycloak_clients | length > 0