diff --git a/CHANGELOG.md b/CHANGELOG.md index 945fa5f..5611956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,33 @@ ## 2026-08-06 — le chemin nord-sud devient dérivable +### « Federated storage is not writable » — la fédération était en lecture seule + +Le changement de mot de passe imposé échouait : `editMode=READ_ONLY` était **codé en dur** +dans `federation-ldap.yml`. Keycloak lisait l'annuaire sans jamais pouvoir y écrire — donc +`pwdReset` était un cul-de-sac : LDAP *exige* le changement, et Keycloak *ne peut pas* le +faire. + +**Trois modes, un seul tient avec la doctrine :** + +| Mode | Effet | Verdict | +|---|---|---| +| `READ_ONLY` | Keycloak n'écrit jamais | le changement de mot de passe est impossible | +| `UNSYNCED` | Keycloak écrit dans **sa** base | Dovecot et Postfix, qui se lient directement à LDAP (D-39), valideraient encore l'ancien — une identité, deux mots de passe | +| `WRITABLE` | Keycloak écrit **à travers** vers LDAP | l'annuaire reste la source unique ; Keycloak n'en est qu'un client | + +`UNSYNCED` aurait « marché » à l'écran tout en cassant le courriel en silence. C'est le piège +qu'il fallait éviter. + +Le mode devient une variable, et il est **réconcilié** — comme l'URL depuis hier. Un provider +créé en `READ_ONLY` le serait resté à vie. + +**Deux fautes de ma part dans le même correctif.** Mon extraction du mode actuel s'ancrait sur +`$` alors que la ligne finit par un guillemet : elle ne correspondait jamais. Et sans `|| true`, +un `grep` sans correspondance tue le script entier sous `pipefail`. La tâche échouait — masquée +par `no_log`, pour la troisième fois aujourd'hui. + + ### « invalid username or password » — c'était la porte, pas le mot de passe Première tentative de connexion du sysadmin : refusée. Ni le jeton ni le compte n'étaient en diff --git a/roles/serveur_keycloak/defaults/main.yml b/roles/serveur_keycloak/defaults/main.yml index 355a216..d8206a9 100644 --- a/roles/serveur_keycloak/defaults/main.yml +++ b/roles/serveur_keycloak/defaults/main.yml @@ -95,3 +95,20 @@ serveur_keycloak_groupes_roles: [] # jamais les services : Forgejo et Nextcloud liraient un claim vide. serveur_keycloak_groupes_claim: "groups" serveur_keycloak_groupes_mapper_clients: [] + +# Mode d'ecriture de la federation LDAP. TROIS valeurs possibles, et une seule tient +# avec la doctrine : +# +# READ_ONLY Keycloak lit, n'ecrit jamais. Un changement de mot de passe echoue sur +# « Federated storage is not writable » — donc `pwdReset` est un cul-de-sac : +# l'annuaire EXIGE le changement, et Keycloak ne peut pas le faire. +# UNSYNCED Keycloak ecrit dans SA base. Le mot de passe ne serait plus dans LDAP : +# Dovecot et Postfix, qui s'y lient DIRECTEMENT (D-39), continueraient de +# valider l'ancien. Une identite, deux mots de passe — exactement ce que la +# doctrine interdit. +# WRITABLE Keycloak ecrit A TRAVERS vers LDAP. L'annuaire reste la source unique ; +# Keycloak n'en est qu'un client. C'est le seul mode coherent. +# +# Constate le 2026-08-07 : READ_ONLY etait code en dur, et la premiere reprise du +# sysadmin butait dessus. +serveur_keycloak_ldap_edit_mode: "WRITABLE" diff --git a/roles/serveur_keycloak/tasks/federation-ldap.yml b/roles/serveur_keycloak/tasks/federation-ldap.yml index f35360e..d0432aa 100644 --- a/roles/serveur_keycloak/tasks/federation-ldap.yml +++ b/roles/serveur_keycloak/tasks/federation-ldap.yml @@ -39,11 +39,18 @@ if [ -n "$FEDID" ]; then URL_ACTUELLE=$("$KC" get components/"$FEDID" -r {{ serveur_keycloak_realm }} 2>/dev/null \ | grep -o '"connectionUrl" : \[ "[^"]*"' | grep -o 'ldaps\?://[^"]*' | head -1) - if [ "$URL_ACTUELLE" != "{{ serveur_keycloak_ldap_url }}" ]; then + # `|| true` en fin de chaine : avec `pipefail`, un grep sans correspondance + # tuerait le script entier. Et l'extraction ne s'ancre pas sur `$` — la ligne + # se termine par un guillemet, pas par la valeur. + MODE_ACTUEL=$("$KC" get components/"$FEDID" -r {{ serveur_keycloak_realm }} 2>/dev/null \ + | sed -n 's/.*"editMode" : \[ "\([A-Z_]*\)".*/\1/p' | head -1 || true) + if [ "$URL_ACTUELLE" != "{{ serveur_keycloak_ldap_url }}" ] \ + || [ "$MODE_ACTUEL" != "{{ serveur_keycloak_ldap_edit_mode }}" ]; then "$KC" update components/"$FEDID" -r {{ serveur_keycloak_realm }} \ -s 'config.connectionUrl=["{{ serveur_keycloak_ldap_url }}"]' \ -s 'config.usersDn=["{{ serveur_keycloak_ldap_users_dn }}"]' \ - -s 'config.bindDn=["{{ serveur_keycloak_ldap_bind_dn }}"]' >/dev/null + -s 'config.bindDn=["{{ serveur_keycloak_ldap_bind_dn }}"]' \ + -s 'config.editMode=["{{ serveur_keycloak_ldap_edit_mode }}"]' >/dev/null CHANGED=1 fi fi @@ -53,7 +60,7 @@ -s providerType=org.keycloak.storage.UserStorageProvider -s parentId="$RID" \ -s 'config.enabled=["true"]' \ -s 'config.vendor=["other"]' \ - -s 'config.editMode=["READ_ONLY"]' \ + -s 'config.editMode=["{{ serveur_keycloak_ldap_edit_mode }}"]' \ -s 'config.importEnabled=["true"]' \ -s 'config.connectionUrl=["{{ serveur_keycloak_ldap_url }}"]' \ -s 'config.usersDn=["{{ serveur_keycloak_ldap_users_dn }}"]' \