Set-OPS-Public/roles/serveur_keycloak/tasks/deconnexion-oidc.yml

100 lines
4.6 KiB
YAML
Raw Normal View History

---
# URI de retour APRES DECONNEXION, par client.
#
# Keycloak les valide separement des URI de rappel : un client peut donc se connecter
# parfaitement et echouer a la deconnexion, sur un « We are sorry… invalid redirect uri »
# qui ne dit pas de quelle liste il parle. Constate le 2026-08-08 sur Nextcloud — et
# AUCUN des quatre clients ne declarait cet attribut ; Nextcloud etait seulement le seul
# a envoyer une URI de retour.
#
# DERIVEE de `web_origins`, qui porte deja l'URL de base du service : la connaissance
# existe, il n'y a pas a la reecrire. `/*` couvre la page d'accueil et les chemins de
# retour que chaque application choisit.
#
# Par l'API et non `kcadm -s` : `attributes` est une MAP, et sur une map kcadm accepte
# la commande, sort en succes et n'ecrit rien (mesure du meme jour sur `smtpServer`).
# On relit systematiquement apres avoir pose.
keycloak : un jeton frais la ou on s'en sert ; grafana : un echec lisible Technolibre remonte depuis zero une seconde fois — 14 hotes, 2588 taches ok, 0 failed. Deux defauts trouves en chemin. LE JETON KEYCLOAK VIVAIT 60 SECONDES. Il etait pris dans politique-mdp.yml — le 2e des neuf fichiers du role — et reutilise jusqu'au 8e. Entre les deux, six fichiers de travail dont groupes-ldap.yml et ses reprises espacees de 15 s. Sur une construction NEUVE le temps depasse la minute : 401. Sur un REJEU tout est converge, ca va vite, ca passe. D'ou les deux echecs du matin, chaque fois suivis d'un succes au rejeu, qui donnaient l'illusion d'une course au demarrage de Keycloak. J'avais ecrit alors ne pas avoir de mesure qui le prouve — c'etait juste, et la cause etait l'AGE du jeton. jeton-admin.yml en prend un frais la ou on s'en sert. GRAFANA : UN ECHEC TRANSITOIRE RENDU ILLISIBLE. Premier demarrage, apres 67 s de migrations : « failed to create admin user: no such column: uid », alors que la migration qui ajoute cette colonne etait journalisee comme reussie. Base neuve : tout remigre, service actif, colonne presente. L'incident ne s'est pas reproduit et Chezlepro ne l'a jamais eu — je n'ai donc PAS corrige la cause, faute de l'avoir reproduite. J'ai corrige ce qui la rendait indechiffrable : - Restart=on-failure venait du paquet SANS RestartSec, donc 100 ms : six relances en une seconde, chacune rejouant les migrations sur la meme base SQLite. Un echec unique se presentait comme un desastre. RestartSec=10 ; - la rotation du compte de secours echouait cinq fois sous no_log en annoncant « the output has been hidden », alors que la vraie cause etait ailleurs et lisible : le serveur ne demarrait pas. Une attente explicite sur le port precede desormais la CLI, avec un message qui renvoie a la PREMIERE erreur du journal. Troisieme fois dans la journee que no_log masque la cause au moment ou elle sert : une garde qui protege un secret ne doit pas emporter le diagnostic. Verifie : 7 devis sur Technolibre — MTU, identite, certificats, PostgreSQL, courriel, frontiere CONFORME ; prouver.py 35 OK ; ansible-lint production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 20:19:50 -04:00
# Jeton FRAIS : celui de `politique-mdp.yml` a plus de six fichiers d'age, et il vit
# 60 s. C'est ce qui faisait echouer cette lecture sur toute construction NEUVE — et
# passer au rejeu, une fois la flotte convergee. Voir `jeton-admin.yml`.
- name: Reprendre un jeton d'administration avant de lire les clients
ansible.builtin.include_tasks: jeton-admin.yml
- name: Lire les clients du realm
ansible.builtin.uri:
url: "http://localhost:8080/admin/realms/{{ serveur_keycloak_realm }}/clients"
headers:
Authorization: "Bearer {{ serveur_keycloak_pol_jeton.json.access_token }}"
keycloak : un jeton frais la ou on s'en sert ; grafana : un echec lisible Technolibre remonte depuis zero une seconde fois — 14 hotes, 2588 taches ok, 0 failed. Deux defauts trouves en chemin. LE JETON KEYCLOAK VIVAIT 60 SECONDES. Il etait pris dans politique-mdp.yml — le 2e des neuf fichiers du role — et reutilise jusqu'au 8e. Entre les deux, six fichiers de travail dont groupes-ldap.yml et ses reprises espacees de 15 s. Sur une construction NEUVE le temps depasse la minute : 401. Sur un REJEU tout est converge, ca va vite, ca passe. D'ou les deux echecs du matin, chaque fois suivis d'un succes au rejeu, qui donnaient l'illusion d'une course au demarrage de Keycloak. J'avais ecrit alors ne pas avoir de mesure qui le prouve — c'etait juste, et la cause etait l'AGE du jeton. jeton-admin.yml en prend un frais la ou on s'en sert. GRAFANA : UN ECHEC TRANSITOIRE RENDU ILLISIBLE. Premier demarrage, apres 67 s de migrations : « failed to create admin user: no such column: uid », alors que la migration qui ajoute cette colonne etait journalisee comme reussie. Base neuve : tout remigre, service actif, colonne presente. L'incident ne s'est pas reproduit et Chezlepro ne l'a jamais eu — je n'ai donc PAS corrige la cause, faute de l'avoir reproduite. J'ai corrige ce qui la rendait indechiffrable : - Restart=on-failure venait du paquet SANS RestartSec, donc 100 ms : six relances en une seconde, chacune rejouant les migrations sur la meme base SQLite. Un echec unique se presentait comme un desastre. RestartSec=10 ; - la rotation du compte de secours echouait cinq fois sous no_log en annoncant « the output has been hidden », alors que la vraie cause etait ailleurs et lisible : le serveur ne demarrait pas. Une attente explicite sur le port precede desormais la CLI, avec un message qui renvoie a la PREMIERE erreur du journal. Troisieme fois dans la journee que no_log masque la cause au moment ou elle sert : une garde qui protege un secret ne doit pas emporter le diagnostic. Verifie : 7 devis sur Technolibre — MTU, identite, certificats, PostgreSQL, courriel, frontiere CONFORME ; prouver.py 35 OK ; ansible-lint production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 20:19:50 -04:00
status_code: [200]
register: serveur_keycloak_clients_actuels
keycloak : un jeton frais la ou on s'en sert ; grafana : un echec lisible Technolibre remonte depuis zero une seconde fois — 14 hotes, 2588 taches ok, 0 failed. Deux defauts trouves en chemin. LE JETON KEYCLOAK VIVAIT 60 SECONDES. Il etait pris dans politique-mdp.yml — le 2e des neuf fichiers du role — et reutilise jusqu'au 8e. Entre les deux, six fichiers de travail dont groupes-ldap.yml et ses reprises espacees de 15 s. Sur une construction NEUVE le temps depasse la minute : 401. Sur un REJEU tout est converge, ca va vite, ca passe. D'ou les deux echecs du matin, chaque fois suivis d'un succes au rejeu, qui donnaient l'illusion d'une course au demarrage de Keycloak. J'avais ecrit alors ne pas avoir de mesure qui le prouve — c'etait juste, et la cause etait l'AGE du jeton. jeton-admin.yml en prend un frais la ou on s'en sert. GRAFANA : UN ECHEC TRANSITOIRE RENDU ILLISIBLE. Premier demarrage, apres 67 s de migrations : « failed to create admin user: no such column: uid », alors que la migration qui ajoute cette colonne etait journalisee comme reussie. Base neuve : tout remigre, service actif, colonne presente. L'incident ne s'est pas reproduit et Chezlepro ne l'a jamais eu — je n'ai donc PAS corrige la cause, faute de l'avoir reproduite. J'ai corrige ce qui la rendait indechiffrable : - Restart=on-failure venait du paquet SANS RestartSec, donc 100 ms : six relances en une seconde, chacune rejouant les migrations sur la meme base SQLite. Un echec unique se presentait comme un desastre. RestartSec=10 ; - la rotation du compte de secours echouait cinq fois sous no_log en annoncant « the output has been hidden », alors que la vraie cause etait ailleurs et lisible : le serveur ne demarrait pas. Une attente explicite sur le port precede desormais la CLI, avec un message qui renvoie a la PREMIERE erreur du journal. Troisieme fois dans la journee que no_log masque la cause au moment ou elle sert : une garde qui protege un secret ne doit pas emporter le diagnostic. Verifie : 7 devis sur Technolibre — MTU, identite, certificats, PostgreSQL, courriel, frontiere CONFORME ; prouver.py 35 OK ; ansible-lint production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 20:19:50 -04:00
# `no_log` protege le jeton porte par l'en-tete. Il masquait aussi la CAUSE : le
# 2026-08-10, cet appel a echoue deux fois sur « the output has been hidden », et il a
# fallu remonter jusqu'a l'age du jeton pour comprendre. On extrait donc le verdict.
no_log: true
keycloak : un jeton frais la ou on s'en sert ; grafana : un echec lisible Technolibre remonte depuis zero une seconde fois — 14 hotes, 2588 taches ok, 0 failed. Deux defauts trouves en chemin. LE JETON KEYCLOAK VIVAIT 60 SECONDES. Il etait pris dans politique-mdp.yml — le 2e des neuf fichiers du role — et reutilise jusqu'au 8e. Entre les deux, six fichiers de travail dont groupes-ldap.yml et ses reprises espacees de 15 s. Sur une construction NEUVE le temps depasse la minute : 401. Sur un REJEU tout est converge, ca va vite, ca passe. D'ou les deux echecs du matin, chaque fois suivis d'un succes au rejeu, qui donnaient l'illusion d'une course au demarrage de Keycloak. J'avais ecrit alors ne pas avoir de mesure qui le prouve — c'etait juste, et la cause etait l'AGE du jeton. jeton-admin.yml en prend un frais la ou on s'en sert. GRAFANA : UN ECHEC TRANSITOIRE RENDU ILLISIBLE. Premier demarrage, apres 67 s de migrations : « failed to create admin user: no such column: uid », alors que la migration qui ajoute cette colonne etait journalisee comme reussie. Base neuve : tout remigre, service actif, colonne presente. L'incident ne s'est pas reproduit et Chezlepro ne l'a jamais eu — je n'ai donc PAS corrige la cause, faute de l'avoir reproduite. J'ai corrige ce qui la rendait indechiffrable : - Restart=on-failure venait du paquet SANS RestartSec, donc 100 ms : six relances en une seconde, chacune rejouant les migrations sur la meme base SQLite. Un echec unique se presentait comme un desastre. RestartSec=10 ; - la rotation du compte de secours echouait cinq fois sous no_log en annoncant « the output has been hidden », alors que la vraie cause etait ailleurs et lisible : le serveur ne demarrait pas. Une attente explicite sur le port precede desormais la CLI, avec un message qui renvoie a la PREMIERE erreur du journal. Troisieme fois dans la journee que no_log masque la cause au moment ou elle sert : une garde qui protege un secret ne doit pas emporter le diagnostic. Verifie : 7 devis sur Technolibre — MTU, identite, certificats, PostgreSQL, courriel, frontiere CONFORME ; prouver.py 35 OK ; ansible-lint production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 20:19:50 -04:00
failed_when: false
- name: Dire pourquoi la lecture des clients a échoué, si elle a échoué
ansible.builtin.fail:
msg: >-
Keycloak a refuse la lecture des clients du realme
« {{ serveur_keycloak_realm }} » (HTTP
{{ serveur_keycloak_clients_actuels.status | default('?') }}).
Un 401 signifie un jeton perime — verifier que `jeton-admin.yml` est bien inclus
juste avant cet appel.
when: serveur_keycloak_clients_actuels.status | default(0) != 200
- name: Composer les URI de déconnexion attendues
ansible.builtin.set_fact:
serveur_keycloak_deconnexion: >-
{{ dict(serveur_keycloak_clients | map(attribute='clientId') | list
| zip(serveur_keycloak_clients
| map(attribute='web_origins')
| map('map', 'regex_replace', '/?$', '/*')
| map('join', '##') | list)) }}
- name: Déclarer l'URI de retour après déconnexion
ansible.builtin.uri:
url: >-
http://localhost:8080/admin/realms/{{ serveur_keycloak_realm
}}/clients/{{ item.id }}
method: PUT
status_code: [204]
headers:
Authorization: "Bearer {{ serveur_keycloak_pol_jeton.json.access_token }}"
body_format: json
body: >-
{{ item | combine({'attributes': (item.attributes | default({}))
| combine({'post.logout.redirect.uris': serveur_keycloak_deconnexion[item.clientId]})}) }}
loop: "{{ serveur_keycloak_clients_actuels.json }}"
loop_control:
label: "{{ item.clientId }}"
portabilite : le repli nftables survivait a la bascule et annulait tout Le socle pose l'un de DEUX fichiers dans /etc/nftables.conf : le ruleset derive (table setops_flux) s'il existe, sinon un gabarit de repli plat (table setops_filter) qui n'ouvre que le 22. Ce sont des ALTERNATIVES, jamais des couches. Mais le rechargement est `nft -f`, qui AJOUTE sans purger, et le fichier derive retirait setops_flux — jamais setops_filter. Un hote passe du repli au derive gardait donc DEUX chaines input sur le meme hook, toutes deux en policy drop. Le paquet traverse les deux : seule l'intersection de leurs accept passait. Symptome parfaitement trompeur : AC debout, 8443 en ecoute, regle posee et acceptante, ICMP entre les deux VM a 0,15 ms — et step ca bootstrap qui expire. Il a fallu lire le ruleset entier pour voir la seconde table. Le fichier derive retire desormais les DEUX tables (le repli portait deja flush ruleset). Pas de flush ruleset cote derive : choix du depot pour ne pas detruire de tables etrangeres, respecte. Ce qui l'avait rendu possible : `make reconstruire` ne generait JAMAIS les flux. `make flux` en est maintenant la premiere etape. Et `no_log` a masque la cause trois fois dans la journee — dont au terme d'un deploiement de 157 taches. Les taches concernees extraient desormais le verdict a part (ce qui a echoue, quel code, quel message) sans toucher aux en-tetes. Une garde qui protege un secret ne doit pas emporter le diagnostic. Verifie : TLS OK depuis infra-dns-01 vers l'AC, une seule table nftables, 13/14 hotes deployes sans echec, prouver.py 34 OK, ansible-lint production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 12:24:41 -04:00
# `no_log` protege le JETON porte par l'en-tete — on ne peut pas simplement l'enlever.
# Mais il masquait aussi la CAUSE : le 2026-08-10, l'echec s'est lu « the output has
# been hidden » sur le tout dernier appel d'un deploiement de 157 taches. On journalise
# donc le verdict a part, en n'extrayant que ce qui ne revele rien.
no_log: true
portabilite : le repli nftables survivait a la bascule et annulait tout Le socle pose l'un de DEUX fichiers dans /etc/nftables.conf : le ruleset derive (table setops_flux) s'il existe, sinon un gabarit de repli plat (table setops_filter) qui n'ouvre que le 22. Ce sont des ALTERNATIVES, jamais des couches. Mais le rechargement est `nft -f`, qui AJOUTE sans purger, et le fichier derive retirait setops_flux — jamais setops_filter. Un hote passe du repli au derive gardait donc DEUX chaines input sur le meme hook, toutes deux en policy drop. Le paquet traverse les deux : seule l'intersection de leurs accept passait. Symptome parfaitement trompeur : AC debout, 8443 en ecoute, regle posee et acceptante, ICMP entre les deux VM a 0,15 ms — et step ca bootstrap qui expire. Il a fallu lire le ruleset entier pour voir la seconde table. Le fichier derive retire desormais les DEUX tables (le repli portait deja flush ruleset). Pas de flush ruleset cote derive : choix du depot pour ne pas detruire de tables etrangeres, respecte. Ce qui l'avait rendu possible : `make reconstruire` ne generait JAMAIS les flux. `make flux` en est maintenant la premiere etape. Et `no_log` a masque la cause trois fois dans la journee — dont au terme d'un deploiement de 157 taches. Les taches concernees extraient desormais le verdict a part (ce qui a echoue, quel code, quel message) sans toucher aux en-tetes. Une garde qui protege un secret ne doit pas emporter le diagnostic. Verifie : TLS OK depuis infra-dns-01 vers l'AC, une seule table nftables, 13/14 hotes deployes sans echec, prouver.py 34 OK, ansible-lint production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 12:24:41 -04:00
register: serveur_keycloak_deconnexion_res
failed_when: false
changed_when: true
when:
- item.clientId in serveur_keycloak_deconnexion
- (item.attributes | default({})).get('post.logout.redirect.uris', '')
!= serveur_keycloak_deconnexion[item.clientId]
portabilite : le repli nftables survivait a la bascule et annulait tout Le socle pose l'un de DEUX fichiers dans /etc/nftables.conf : le ruleset derive (table setops_flux) s'il existe, sinon un gabarit de repli plat (table setops_filter) qui n'ouvre que le 22. Ce sont des ALTERNATIVES, jamais des couches. Mais le rechargement est `nft -f`, qui AJOUTE sans purger, et le fichier derive retirait setops_flux — jamais setops_filter. Un hote passe du repli au derive gardait donc DEUX chaines input sur le meme hook, toutes deux en policy drop. Le paquet traverse les deux : seule l'intersection de leurs accept passait. Symptome parfaitement trompeur : AC debout, 8443 en ecoute, regle posee et acceptante, ICMP entre les deux VM a 0,15 ms — et step ca bootstrap qui expire. Il a fallu lire le ruleset entier pour voir la seconde table. Le fichier derive retire desormais les DEUX tables (le repli portait deja flush ruleset). Pas de flush ruleset cote derive : choix du depot pour ne pas detruire de tables etrangeres, respecte. Ce qui l'avait rendu possible : `make reconstruire` ne generait JAMAIS les flux. `make flux` en est maintenant la premiere etape. Et `no_log` a masque la cause trois fois dans la journee — dont au terme d'un deploiement de 157 taches. Les taches concernees extraient desormais le verdict a part (ce qui a echoue, quel code, quel message) sans toucher aux en-tetes. Une garde qui protege un secret ne doit pas emporter le diagnostic. Verifie : TLS OK depuis infra-dns-01 vers l'AC, une seule table nftables, 13/14 hotes deployes sans echec, prouver.py 34 OK, ansible-lint production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 12:24:41 -04:00
- name: Dire quels clients Keycloak a refusés, et pourquoi
ansible.builtin.fail:
msg: >-
Keycloak a refuse la mise a jour des URI de deconnexion pour :
{{ refuses | map(attribute='item.clientId') | list | join(', ') }}.
Codes rendus : {{ refuses | map(attribute='status') | list }}.
Messages : {{ refuses | map(attribute='msg') | map('string')
| map('truncate', 160) | list }}.
vars:
refuses: >-
{{ (serveur_keycloak_deconnexion_res.results | default([]))
| rejectattr('skipped', 'defined')
| selectattr('status', 'defined')
| rejectattr('status', 'equalto', 204) | list }}
when: refuses | length > 0