From 775df924cbd2988a54933be055f964825f8a77b0 Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Sat, 8 Aug 2026 06:08:06 -0400 Subject: [PATCH] =?UTF-8?q?identite=20:=20=C2=AB=20mot=20de=20passe=20oubl?= =?UTF-8?q?ie=20=C2=BB=20=E2=80=94=20raccord=20SMTP=20derive=20du=20plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le realm portait une politique d'acces complete et aucun moyen d'ecrire a qui que ce soit. Tout oubli remontait donc a l'exploitant, qui n'avait d'autre choix que de manipuler le mot de passe d'autrui. - serveur_keycloak/tasks/courriel-realm.yml : reconcilie smtpServer et resetPasswordAllowed ; hote du relais DERIVE de applications.postfix.hote, et refus explicite si le plan ne declare pas de MTA. - passe par l'API d'administration : kcadm.sh accepte les deux formes -s sur une map, sort en succes et n'ecrit rien (smtpServer reste vide). - amorcage_acces_courriel redevient a declarer : cette adresse designe une personne, hors du systeme qu'on amorce ; une boite interne serait illisible tant qu'on n'a pas l'acces qu'on cherche justement a recuperer. - autorisation.md §6.6 : le mecanisme, ses deux conditions, et l'ecart d'adresse laisse par l'ancien mode READ_ONLY de la federation. Preuve : banniere SMTP lue depuis idm-01, RCPT TO accepte, execute-actions-email declenche, MTA en starttls -> relay=mx.chezlepro.ca status=sent (250). Second deploiement changed=0. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 48 +++++++++++- docs/audit/preuve-2026-08-08.md | 66 ++++++++++++++++ docs/autorisation.md | 36 ++++++++- roles/amorcage_acces/defaults/main.yml | 10 ++- roles/amorcage_acces/tasks/main.yml | 16 ++++ roles/serveur_keycloak/defaults/main.yml | 22 ++++++ .../serveur_keycloak/tasks/courriel-realm.yml | 76 +++++++++++++++++++ roles/serveur_keycloak/tasks/main.yml | 4 + 8 files changed, 273 insertions(+), 5 deletions(-) create mode 100644 docs/audit/preuve-2026-08-08.md create mode 100644 roles/serveur_keycloak/tasks/courriel-realm.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index a0a10bd..60f1c6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,49 @@ # CHANGELOG — Set-OPS +## 2026-08-08 — « Mot de passe oublié » : Keycloak sait enfin envoyer + +Le realm affichait une politique d'accès complète et **aucun moyen d'écrire à qui que ce +soit** : `smtpServer` vide, `resetPasswordAllowed` à `false`. Conséquence concrète — tout +oubli de mot de passe remontait à l'exploitant, qui n'avait alors d'autre choix que de +manipuler le mot de passe de quelqu'un d'autre. C'est précisément ce que « une identité, +une personne » (§3 d'`autorisation.md`) cherche à écarter. + +**Ce qui est ajouté.** `serveur_keycloak/tasks/courriel-realm.yml` réconcilie la strophe +courriel du realm et le drapeau « mot de passe oublié ». L'hôte du relais est **dérivé du +plan** (`applications.postfix.hote`) : aucun nom de machine n'est écrit. Si le plan ne +déclare pas de MTA, le rôle **refuse** — un écran qui promet un courriel que personne +n'enverrait serait pire que pas d'écran du tout. + +**`kcadm.sh` ne sait pas écrire une map, et ne le dit pas.** Sur `smtpServer`, les deux +formes documentées — `-s smtpServer.host=…` et `-s 'smtpServer={"host":…}'` — sortent en +**succès, sans rien écrire**. Le champ est resté `{ }` après deux déploiements verts. La +tâche passe donc par l'API d'administration (`uri`), qui répond `204` et écrit vraiment. +Même famille que le reste de ce journal : une valeur déclarée d'un côté, jamais vérifiée +de l'autre. Ce qui l'a rattrapée, c'est d'avoir relu l'état après l'avoir posé — pas le +code de retour. + +**L'adresse de l'amorçage ne se dérive pas.** J'avais d'abord posé +`{{ amorcage_acces_uid }}@{{ domaine_interne }}` comme défaut : c'est un piège. Cette +adresse désigne une **personne**, donc quelque chose d'extérieur au système qu'on amorce, +et une boîte interne n'est pas lisible tant qu'on n'a pas justement l'accès qu'on essaie de +récupérer. `amorcage_acces_courriel` redevient donc à déclarer, et le rôle refuse de créer +le compte sans elle — mais seulement à la création, pour qu'un écosystème déjà amorcé ne se +mette pas à échouer parce qu'on a durci la règle après coup. + +**Un reliquat de `READ_ONLY` mis au jour.** Le compte `sysadmin` portait +`sysadmin@chezlepro.ca` dans Keycloak et **rien** dans LDAP. L'adresse avait été saisie +dans la console de compte quand la fédération était encore en lecture seule : Keycloak +l'avait gardée pour lui, l'annuaire ne l'a jamais reçue, et les deux côtés ont affiché des +valeurs différentes sans que rien ne le signale. Corrigé dans LDAP (source de vérité) puis +resynchronisé ; consigné au runbook (§6.6) parce que d'autres comptes créés avant la +bascule en `WRITABLE` peuvent porter le même écart. + +**Preuve de bout en bout**, et pas un `connect()` : bannière SMTP lue depuis `idm-01`, +`RCPT TO` accepté, puis un vrai `execute-actions-email` déclenché — journal du MTA : +`starttls=1`, `to=, relay=mx.chezlepro.ca[69.70.26.53]:25, +status=sent (250 2.0.0 Ok)`. Le second déploiement rapporte `changed=0` : la tâche +réconcilie, elle ne réécrit pas. + ## 2026-08-06 — le chemin nord-sud devient dérivable ### `vault_openldap_admin` — quatre consommateurs et deux pièges @@ -70,13 +114,13 @@ pu s'authentifier pour poser la nouvelle valeur. ``` C'est une **procédure**, pas un redéploiement — et la même contrainte vaut pour -`vault_openldap_admin`, qui reste à faire. Consigné au runbook (§6.7), avec le rappel qu'une +`vault_openldap_admin`, qui reste à faire. Consigné au runbook (§6.8), avec le rappel qu'une vérification n'est pas un message de succès. ### La rotation des comptes de secours devient possible — elle ne l'était pas -Le runbook §6.6 promettait de régénérer les comptes de secours ; **le code ne savait pas le +Le runbook §6.7 promettait de régénérer les comptes de secours ; **le code ne savait pas le faire**. Les trois rôles ne posaient le mot de passe qu'à la *création* : ``` diff --git a/docs/audit/preuve-2026-08-08.md b/docs/audit/preuve-2026-08-08.md new file mode 100644 index 0000000..4d83e29 --- /dev/null +++ b/docs/audit/preuve-2026-08-08.md @@ -0,0 +1,66 @@ +# Preuve de conformite — Set-OPS — 2026-08-08 + +> Genere par `make prouver` (`scripts/prouver.py`). **Rejouable** : relancer +> reproduit ce rapport. Chaque preuve rejoue l'outillage existant du depot ; +> aucune validation n'est reimplementee ici. Voir le mode d'emploi : +> [`docs/audit/README.md`](README.md), et le registre trace : +> [`docs/audit/affirmations.md`](affirmations.md). + +- **Instance** : `instance` — inventaire `instance/inventories/principal/hosts.yml` +- **Verdict** : ✅ CONFORME (29 OK · 0 echec · 1 saute) + +## Preuves + +| # | Preuve | Affirmations | Statut | Detail | +|---|---|---|---|---| +| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | | +| P02 | Tests unitaires (inventory_host) | — | ✅ OK | 5 tests passes. | +| P03 | Diff-vide du plan (inventaire genere) | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | DIFF VIDE : le plan reproduit exactement l'inventaire actuel. Bascule possible. | +| P04 | Groupes <-> playbooks homonymes | AFF-008 | ✅ OK | | +| P05 | Dependances causales de groupes | AFF-009, AFF-084 | ✅ OK | | +| P06 | Validateurs de registres (serveurs/apps/bases/domaines) | AFF-003 | ✅ OK | Registre des domaines valide. | +| P07 | GUI (node --check) | AFF-033 | ✅ OK | JS du GUI : syntaxe valide (node --check). | +| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 30 groupes classes, aucun cycle, aucune arete en arriere. | +| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 29 rôles, 74 flux, schéma + matrice OK. | +| P10 | Handlers <-> notify | AFF-034, AFF-035 | ✅ OK | Tout notify pointe vers un handler du meme role (49 roles). | +| P11 | Syntaxe des playbooks (--syntax-check) | AFF-083 | ✅ OK | playbook: playbooks/proxmox/cloner_vm_debian.yml | +| P12 | Existence des runbooks cites | AFF-010, AFF-011, AFF-012, AFF-083 | ✅ OK | 17/17 runbooks/registres cites presents. | +| P13 | Invariants structurels/doctrinaux | AFF-015, AFF-022, AFF-037, AFF-038, AFF-062 | ✅ OK | LICENSE, socle dossier, pas de couches paralleles, SSH clef-only, nftables off : OK. | +| P14 | Pas de chemin lab/ code en dur | AFF-097 | ✅ OK | Aucun chemin instance/inventories/lab/group_vars code en dur. | +| P15 | Modele public socle valide | AFF-022, AFF-099 | ✅ OK | Modele public socle : domaines/serveurs/applications/bases valides. | +| P16 | Inventaire Ansible complet (--list) | AFF-030 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). | +| P17 | Tous les modeles valident (registres + underlay) | AFF-022, AFF-099 | ✅ OK | Les 1 modele(s) decouvert(s) valident. | +| P18 | Gabarit de voute complet | AFF-026 | ✅ OK | Gabarit de voute complet : 25 secret(s) exige(s), tous presents. (Voute reelle non lisible ici : verification sautee.) | +| P19 | Le GUI couvre le schema du plan | AFF-002, AFF-095 | ✅ OK | GUI : les 28 champ(s) des plans reels sont editables (2 plan(s) inspecte(s)), registres toleres : nomenclature. | +| P20 | Adressage 100% derive du seed (aucun stocke) | AFF-001, AFF-003 | ✅ OK | 2 nomenclature(s) : adressage 100% derive du seed index. | +| P21 | Federation : aucun index en collision | AFF-102 | ✅ OK | Federation coherente : 2 instance(s) federee(s), aucun index en collision. | +| P22 | Plan de recette a jour (genere du wiki) | AFF-002 | ✅ OK | Plan de recette à jour (19 sections). | +| P23 | Underlay sans collision avec la plage tenant | AFF-103 | ✅ OK | Underlay conforme : 6 reseau(x), aucune collision avec la plage tenant. | +| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | CONFORME : frontiere nord/sud, 31 regles, 2 routes, admin=10.0.0.0/24,192.168.254.2/32,192.168.255.2/32. | +| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 2 tenant(s), 38 groupe(s), 64 regle(s). | +| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 hote(s) x 4 integration(s) universelle(s) : aucune lacune, aucune recopie (1 exemption(s) derivee(s) du service rendu). | +| P27 | Propriete des intrants : hebergeur et tenant separes | AFF-109 | ✅ OK | 0 cle(s) de cluster chez l'hebergeur, aucune recopiee dans les group_vars du tenant. | +| P28 | Pools Proxmox : un par tenant, sans collision | AFF-110 | ✅ OK | CONFORME : 2 pool(s) Proxmox, 28 VM placee(s), aucun nom ni VMID en collision. | +| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 23 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 12, socle-identite 2, web-sso 5) ; 2 lacune(s) nommee(s) : serveur_loki, serv | +| P30 | SDN EVPN : zones, VNets et sous-reseaux derives | AFF-112 | ✅ OK | CONFORME : SDN EVPN, 2 zone(s), 12 VNet(s), 12 sous-reseau(x), aucune collision. | + +## Couverture des affirmations ✅ du registre + +Chaque affirmation ✅ automatisable est couverte par la preuve indiquee ci-dessus. +Les ✅ **structurelles/doctrinales** non rejouables par une commande (ex. AFF-005 +`make`=aide, AFF-014 ciblage groupe, AFF-024 `instancier-appliquer`, AFF-051 autorite +d'AGENTS.md, AFF-073/075 gardes `make`, AFF-090 wiki) ont ete verifiees a l'audit ; +elles restent hors du harnais recurrent (rien d'executable a rejouer). + +## Declarations d'intention (⚪ invérifiables localement — assumees) + +Ces affirmations ne sont pas rejouables hors production ; elles sont **assumees** +comme declarations d'intention, non comme preuves : + +- **AFF-036** — « testables avec `--check` autant que possible » : verifiable seulement + contre une flotte vivante. +- **AFF-091** — contenu pedagogique du wiki : affirmations conceptuelles. +- **AFF-096** — « GUI 100 % francais » : revue exhaustive des libelles rendus, non automatisee. +- **AFF-007** — hote d'exemple `web-frontal-01` : placeholder assume. + +_Rapport genere le 2026-08-08._ diff --git a/docs/autorisation.md b/docs/autorisation.md index 7a7ba5f..92e1703 100644 --- a/docs/autorisation.md +++ b/docs/autorisation.md @@ -186,7 +186,39 @@ occ user:resetpassword Et si LDAP lui-même est en panne, ces trois commandes fonctionnent quand même : elles ne dépendent ni de Keycloak, ni de l'annuaire. C'est le sens de D-40. -### 6.6 Ce que tu dois changer en priorité +### 6.6 « Mot de passe oublié » — pour ne pas dépendre de toi + +Un utilisateur qui oublie son mot de passe ne doit pas avoir à t'appeler : la seule issue +serait alors que tu manipules le mot de passe de quelqu'un d'autre, ce que « une identité, +une personne » cherche précisément à écarter. L'écran de connexion du realm porte donc le +lien **Forgot password?**, et Keycloak envoie lui-même le courriel. + +Deux conditions, toutes deux tenues par le code : + +- **Un relais SMTP.** `serveur_keycloak` le dérive du plan (`applications.postfix.hote`) — + aucun nom de machine n'est écrit nulle part. Le MTA accepte les hôtes du supernet sans + authentification (`mynetworks`) et `idm-01` porte déjà `client_smtp`, donc le flux existe. + Si le plan ne déclare pas de MTA, le rôle **refuse** plutôt que d'afficher un écran qui + promet un courriel que personne n'enverrait. +- **Une adresse sur le compte.** C'est la seule valeur de l'amorçage qui ne se dérive pas : + elle désigne une personne, donc quelque chose d'extérieur au système qu'on amorce. + `amorcage_acces_courriel` doit être déclarée, et le rôle refuse de créer le compte sans + elle. Une adresse `sysadmin@` serait un piège : elle est servie par une + boîte que tu ne peux pas encore lire. + +**Attention à un reliquat.** Tant que la fédération LDAP était en `READ_ONLY`, une adresse +saisie dans la console de compte restait dans la base de Keycloak sans jamais atteindre +l'annuaire. Les deux côtés pouvaient donc afficher des adresses différentes sans que rien +ne le signale. En `WRITABLE` (le mode actuel) le problème ne se reproduit pas, mais les +comptes créés avant peuvent encore porter l'écart. Pour le voir et le réduire : + +``` +ldapsearch -x -H ldapi:/// -D "cn=admin,$BASE" -W -b "uid=,ou=people,$BASE" mail +# puis, si les deux diffèrent : corriger dans LDAP (source de vérité), et resynchroniser +# Identity providers → LDAP → Sync all users +``` + +### 6.7 Ce que tu dois changer en priorité 1. **Le mot de passe d'amorçage** — imposé dès la première connexion, tu n'as pas le choix (§3). @@ -194,7 +226,7 @@ dépendent ni de Keycloak, ni de l'annuaire. C'est le sens de D-40. déploiement y a eu accès. Les régénérer transfère réellement le contrôle. 3. **Le mot de passe de la voûte** elle-même, si le dépôt change de mains. -### 6.7 Faire tourner un secret : l'ordre n'est pas indifférent +### 6.8 Faire tourner un secret : l'ordre n'est pas indifférent **Les comptes de secours des services** (`vault_grafana_admin`, `vault_forgejo_admin`, `vault_nextcloud_admin`) se régénèrent en voûte, puis un redéploiement les applique. Les rôles diff --git a/roles/amorcage_acces/defaults/main.yml b/roles/amorcage_acces/defaults/main.yml index ef11f20..a24c987 100644 --- a/roles/amorcage_acces/defaults/main.yml +++ b/roles/amorcage_acces/defaults/main.yml @@ -15,7 +15,15 @@ amorcage_acces_actif: true # Le compte d'amorcage. `uid` est l'identifiant de connexion. amorcage_acces_uid: "sysadmin" amorcage_acces_nom: "Administrateur systeme" -amorcage_acces_courriel: "" # facultatif ; vide = pas d'attribut mail +# A DECLARER — la seule valeur de ce role qui ne peut PAS se deriver. Elle +# designe une personne, qui est par definition hors du systeme qu'on amorce : +# une adresse `{{ amorcage_acces_uid }}@{{ '{{' }} domaine_interne {{ '}}' }}` +# serait servie par une boite que l'exploitant ne peut pas encore lire. +# +# Sans elle, « mot de passe oublie » (serveur_keycloak) affiche un ecran qui +# promet un courriel dont l'adresse n'existe nulle part, et le seul recours +# redevient l'exploitant manipulant le mot de passe d'autrui. +amorcage_acces_courriel: "" # Le groupe qui porte l'habilitation. Les services le reconnaissent par # `meta/acces.yml` — eux sont reconcilies, l'appartenance ne l'est pas. diff --git a/roles/amorcage_acces/tasks/main.yml b/roles/amorcage_acces/tasks/main.yml index dc83dff..8d64bd5 100644 --- a/roles/amorcage_acces/tasks/main.yml +++ b/roles/amorcage_acces/tasks/main.yml @@ -52,6 +52,22 @@ # --- Tout ce qui suit ne s'exécute QUE si le compte est absent ----------------- +# Verifie ICI et non plus haut : un ecosysteme deja amorce ne doit pas se mettre +# a echouer parce qu'on a durci la regle apres coup. +- name: Exiger une adresse de courriel pour le compte d'amorçage + ansible.builtin.assert: + that: + - amorcage_acces_courriel | length > 0 + fail_msg: >- + `amorcage_acces_courriel` est vide. Ce compte est le seul recours quand + plus rien d'autre ne repond : sans adresse joignable, « mot de passe + oublie » ne mene nulle part et la reprise repasse par un tiers qui + manipule le mot de passe de quelqu'un d'autre. Declarer une adresse que + la personne lit DEJA, hors de l'ecosysteme qu'on amorce. + when: + - amorcage_acces_actif | bool + - not (amorcage_acces_deja_fait | bool) + # Le mot de passe est HACHÉ par slapd lui-même. `ldap_entry` écrirait la chaîne # littéralement : le jeton d'amorçage se retrouverait en clair dans la base, lisible # par quiconque peut lire l'annuaire. diff --git a/roles/serveur_keycloak/defaults/main.yml b/roles/serveur_keycloak/defaults/main.yml index e4955e5..b48d713 100644 --- a/roles/serveur_keycloak/defaults/main.yml +++ b/roles/serveur_keycloak/defaults/main.yml @@ -120,3 +120,25 @@ serveur_keycloak_amorcage_uid: "{{ amorcage_acces_uid | default('sysadmin') }}" # Empreinte du mot de passe de LIAISON LDAP applique — Keycloak le masque, on ne # peut donc pas le comparer. Permet sa rotation. Voir tasks/federation-ldap.yml. serveur_keycloak_marqueur_bind: "/etc/keycloak/.setops-bind-sha256" + +# --- Courriel du realm : « mot de passe oublie » ------------------------------ +# Sans SMTP, `resetPasswordAllowed` ne sert a rien — Keycloak n'a aucun moyen +# d'envoyer le lien. Et sans « mot de passe oublie », chaque oubli passe par +# l'exploitant, qui manipule alors le mot de passe de quelqu'un d'autre : ce que +# « une identite, une personne » cherche precisement a eviter. +# +# Le relais est le MTA du tenant, DERIVE du plan (`applications.postfix.hote`) : +# il accepte les hotes du supernet sans authentification (`mynetworks`), et +# `idm-01` porte deja `client_smtp` — le flux existe. +serveur_keycloak_smtp_actif: true +serveur_keycloak_smtp_app: "postfix" +serveur_keycloak_smtp_port: 25 +serveur_keycloak_smtp_starttls: true +serveur_keycloak_smtp_expediteur: "identite@{{ domaine_interne }}" +serveur_keycloak_smtp_nom_expediteur: "{{ organisation | default('Set-OPS') }}" +# Repli : utilise seulement si le plan ne declare pas d'application `postfix`. +serveur_keycloak_smtp_hote: "" + +# « Mot de passe oublie » sur l'ecran de connexion. Exige le SMTP ci-dessus et une +# adresse `mail` sur le compte — sans elle, Keycloak n'a nulle part ou ecrire. +serveur_keycloak_reset_mot_de_passe: true diff --git a/roles/serveur_keycloak/tasks/courriel-realm.yml b/roles/serveur_keycloak/tasks/courriel-realm.yml new file mode 100644 index 0000000..4ddcd77 --- /dev/null +++ b/roles/serveur_keycloak/tasks/courriel-realm.yml @@ -0,0 +1,76 @@ +--- +# Courriel du realm + « mot de passe oublie ». Voir defaults/main.yml. +# +# API d'administration et NON `kcadm.sh` : sur `smtpServer` (une map), kcadm +# accepte `-s smtpServer.host=…` COMME `-s 'smtpServer={"host":…}'`, sort en +# succes, n'ecrit rien — le champ restait `{ }` apres deux deploiements verts. +# Constate le 2026-08-08. Meme famille que le reste : une valeur declaree d'un +# cote, jamais verifiee de l'autre. + +- name: Dériver l'hôte du relais SMTP depuis le plan + ansible.builtin.set_fact: + serveur_keycloak_smtp_hote: "{{ applications[serveur_keycloak_smtp_app].hote ~ '.' ~ domaine_interne }}" + when: + - serveur_keycloak_smtp_actif | bool + - ((applications | default({})).get(serveur_keycloak_smtp_app, {}).get('hote') | default('')) | length > 0 + +- name: Exiger un relais SMTP résolu + ansible.builtin.assert: + that: + - serveur_keycloak_smtp_hote | length > 0 + fail_msg: >- + Aucun relais SMTP : l'application « {{ serveur_keycloak_smtp_app }} » n'est pas + déclarée au plan et `serveur_keycloak_smtp_hote` est vide. Activer + « mot de passe oublié » sans relais donnerait un écran qui promet un courriel + que personne n'enverrait. + when: serveur_keycloak_smtp_actif | bool + +- name: Obtenir un jeton d'administration + ansible.builtin.uri: + url: "http://localhost:8080/realms/master/protocol/openid-connect/token" + method: POST + body_format: form-urlencoded + body: + grant_type: password + client_id: admin-cli + username: "{{ serveur_keycloak_admin_user }}" + password: "{{ serveur_keycloak_admin_password }}" + register: serveur_keycloak_smtp_jeton + no_log: true + +- name: Lire la strophe courriel en place + ansible.builtin.uri: + url: "http://localhost:8080/admin/realms/{{ serveur_keycloak_realm }}" + headers: + Authorization: "Bearer {{ serveur_keycloak_smtp_jeton.json.access_token }}" + register: serveur_keycloak_realm_actuel + no_log: true + +- name: Composer la strophe courriel attendue + ansible.builtin.set_fact: + serveur_keycloak_smtp_attendu: + host: "{{ serveur_keycloak_smtp_hote }}" + port: "{{ serveur_keycloak_smtp_port | string }}" + from: "{{ serveur_keycloak_smtp_expediteur }}" + fromDisplayName: "{{ serveur_keycloak_smtp_nom_expediteur }}" + starttls: "{{ serveur_keycloak_smtp_starttls | bool | string | lower }}" + auth: "false" + ssl: "false" + +- name: Configurer le courriel du realm et le « mot de passe oublié » + ansible.builtin.uri: + url: "http://localhost:8080/admin/realms/{{ serveur_keycloak_realm }}" + method: PUT + status_code: [204] + headers: + Authorization: "Bearer {{ serveur_keycloak_smtp_jeton.json.access_token }}" + body_format: json + body: + smtpServer: "{{ serveur_keycloak_smtp_attendu }}" + resetPasswordAllowed: "{{ serveur_keycloak_reset_mot_de_passe | bool }}" + no_log: true + changed_when: true + when: >- + (serveur_keycloak_realm_actuel.json.smtpServer | default({})) != serveur_keycloak_smtp_attendu + or (serveur_keycloak_realm_actuel.json.resetPasswordAllowed | default(false) | bool) + != (serveur_keycloak_reset_mot_de_passe | bool) diff --git a/roles/serveur_keycloak/tasks/main.yml b/roles/serveur_keycloak/tasks/main.yml index e9f65e3..815c01e 100644 --- a/roles/serveur_keycloak/tasks/main.yml +++ b/roles/serveur_keycloak/tasks/main.yml @@ -163,6 +163,10 @@ ansible.builtin.include_tasks: federation-ldap.yml when: serveur_keycloak_ldap_federation | bool +- name: Configurer le courriel du realm (« mot de passe oublié ») + ansible.builtin.include_tasks: courriel-realm.yml + when: serveur_keycloak_smtp_actif | bool + - name: Projeter les groupes LDAP (mapper + roles de groupe) ansible.builtin.include_tasks: groupes-ldap.yml when: