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>
99 lines
4.6 KiB
YAML
99 lines
4.6 KiB
YAML
---
|
|
# 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.
|
|
|
|
# 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 }}"
|
|
status_code: [200]
|
|
register: serveur_keycloak_clients_actuels
|
|
# `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
|
|
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 }}"
|
|
# `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
|
|
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]
|
|
|
|
- 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
|