From 35602546eb494ba237392eb4eeaa988587f8e626 Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Fri, 7 Aug 2026 22:20:11 -0400 Subject: [PATCH] rotation : vault_forgejo_oidc, et la reconciliation des secrets OIDC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le secret avait fui dans une sortie de diagnostic. Le faire tourner a d'abord demande de rendre la rotation possible : ni Keycloak ni Forgejo ne reconciliaient un secret OIDC existant. Le commentaire de clients-oidc.yml l'avouait (« create-si-absent »), et `update-oauth` ne passait pas --secret. Regenerer la voute aurait laisse les deux cotes sur l'ancienne valeur — ou un seul des deux, et le SSO aurait casse sans que rien ne l'annonce. Keycloak compare desormais le secret EN PLACE a celui voulu avant d'ecrire. Verifie par empreinte aux trois endroits : voute, Keycloak, Forgejo — identiques. Keycloak rejoue a changed=0. Signale sans etre corrige : `Deployer app.ini` change a chaque passage. Forgejo reecrit lui-meme ce fichier (il y persiste ses secrets generes) et le gabarit l'ecrase. Prealable au correctif : decider quelles cles appartiennent au gabarit. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 29 +++++++++++++++++++ roles/serveur_forgejo/tasks/main.yml | 3 ++ roles/serveur_keycloak/tasks/clients-oidc.yml | 27 +++++++++++++---- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bbdcb6..23def1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,35 @@ ## 2026-08-06 — le chemin nord-sud devient dérivable +### `vault_forgejo_oidc` — le secret exposé ne vaut plus rien + +Il avait fui dans une sortie de diagnostic. Le faire tourner a d'abord demandé de rendre la +rotation possible : **ni Keycloak ni Forgejo ne réconciliaient un secret OIDC existant.** + +Le commentaire de `clients-oidc.yml` l'avouait — *« la réconciliation fine n'est pas faite : +create-si-absent »* — et `update-oauth`, côté Forgejo, ne passait pas `--secret`. Régénérer la +voûte aurait donc laissé les deux côtés sur l'ancienne valeur, ou pire, un seul des deux : le +SSO aurait cassé sans que rien ne l'annonce. + +Les deux réconcilient désormais. Keycloak compare le secret **en place** (`get client-secret`) +à celui voulu avant d'écrire — pas de `changed` inutile. + +**Vérifié par empreinte, aux trois endroits :** + +``` +voûte 2484770b53a4fd76f9b57bf1 +Keycloak 2484770b53a4fd76f9b57bf1 +Forgejo 2484770b53a4fd76f9b57bf1 +``` + +Keycloak rejoue à `changed=0`. + +**Une non-idempotence préexistante, signalée sans être corrigée** : `Deployer app.ini` change à +chaque passage sur Forgejo. Le rôle réécrit un fichier que Forgejo modifie lui-même — il y +persiste ses secrets générés. Ce n'est pas lié à la rotation, et le corriger demande de décider +quelles clés appartiennent au gabarit et lesquelles au service. + + ### `vault_keycloak_admin` : le secret qui est la clé de son propre changement Rotation faite, chaîne d'identité intacte (groupe, membre, rôle), rejeu à `changed=0`. diff --git a/roles/serveur_forgejo/tasks/main.yml b/roles/serveur_forgejo/tasks/main.yml index 5b51227..a27c400 100644 --- a/roles/serveur_forgejo/tasks/main.yml +++ b/roles/serveur_forgejo/tasks/main.yml @@ -180,8 +180,11 @@ # `update-oauth` est idempotent cote Forgejo : il reecrit les memes valeurs # sans effet de bord. On ne peut pas comparer avant/apres (la CLI n'expose # pas le detail d'une source), donc on ne signale PAS `changed`. + # `--secret` fait partie de la reconciliation : sans lui, faire tourner + # `vault_forgejo_oidc` laissait Forgejo sur l'ancienne valeur. $FJ admin auth update-oauth --id "$ID" \ --auto-discover-url "{{ serveur_forgejo_oidc_discovery }}" \ + --secret "$OIDC_SECRET" \ $OPTS_GROUPE >/dev/null echo SETOPS_OK fi diff --git a/roles/serveur_keycloak/tasks/clients-oidc.yml b/roles/serveur_keycloak/tasks/clients-oidc.yml index 9a98b74..5736b63 100644 --- a/roles/serveur_keycloak/tasks/clients-oidc.yml +++ b/roles/serveur_keycloak/tasks/clients-oidc.yml @@ -1,7 +1,11 @@ --- # 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. +# Crée le client s'il est absent, et RÉCONCILIE son secret et ses URI s'il existe. +# +# « Create-si-absent » rendait la rotation d'un secret OIDC impossible : la voûte +# changeait, l'application recevait la nouvelle valeur, et Keycloak gardait +# l'ancienne — le SSO cassait sans que rien ne l'annonce. Constaté le 2026-08-07, +# au moment de faire tourner `vault_forgejo_oidc`, exposé dans une transcription. - name: Enregistrer les clients OIDC (kcadm, idempotent) ansible.builtin.shell: executable: /bin/bash @@ -10,9 +14,22 @@ 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 + CID=$("$KC" get clients -r {{ serveur_keycloak_realm }} -q clientId={{ item.clientId }} \ + --fields id --format csv --noquotes 2>/dev/null | head -1 || true) + if [ -n "$CID" ]; then + # Le secret EN PLACE, compare a celui voulu. `get client-secret` le rend en + # clair : c'est la seule facon de savoir s'il faut ecrire. + ACTUEL=$("$KC" get clients/"$CID"/client-secret -r {{ serveur_keycloak_realm }} 2>/dev/null \ + | sed -n 's/.*"value" *: *"\([^"]*\)".*/\1/p' | head -1 || true) + if [ "$ACTUEL" != "$OIDC_SECRET" ]; then + "$KC" update clients/"$CID" -r {{ serveur_keycloak_realm }} \ + -s secret="$OIDC_SECRET" \ + -s 'redirectUris={{ item.redirect_uris | to_json }}' \ + -s 'webOrigins={{ item.web_origins | default([]) | to_json }}' >/dev/null + echo SETOPS_CHANGED + else + echo SETOPS_OK + fi else "$KC" create clients -r {{ serveur_keycloak_realm }} \ -s clientId={{ item.clientId }} -s enabled=true -s protocol=openid-connect \