Set-OPS-Public/roles/serveur_keycloak/tasks/deconnexion-oidc.yml
Daniel Allaire 9c1c790f9d 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

78 lines
3.4 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.
- 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 }}"
register: serveur_keycloak_clients_actuels
no_log: true
- 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