Set-OPS-Public/roles/amorcage_acces/tasks/main.yml

186 lines
7.3 KiB
YAML
Raw Normal View History

---
# Amorcage des acces — D-67. Voir defaults/main.yml pour la doctrine, et
# docs/autorisation.md §6 pour le runbook de reprise.
- name: Résoudre la connexion à l'annuaire
ansible.builtin.include_role:
name: resoudre_annuaire
- name: Exiger le secret d'amorçage et le mot de passe d'annuaire (Vault)
ansible.builtin.assert:
that:
- amorcage_acces_secret | length > 0
- amorcage_acces_admin_password | length > 0
fail_msg: >-
`vault_sysadmin_amorcage` et `vault_openldap_admin` sont requis (voûte).
Le premier est un jeton d'amorçage à usage unique — pas un mot de passe
permanent : voir docs/autorisation.md §6.
when: amorcage_acces_actif | bool
# --- LE CŒUR DU RÔLE : on REGARDE avant d'agir --------------------------------
# La condition n'est pas la conformité, c'est l'EXISTENCE. Un compte présent est
# laissé strictement intact, quel que soit son état — c'est ce qui distingue ce
# rôle de tous les autres du dépôt (D-67).
- name: Le compte d'amorçage existe-t-il déjà ?
community.general.ldap_search:
dn: "ou=people,{{ resoudre_annuaire_base_dn }}"
scope: onelevel
filter: "(uid={{ amorcage_acces_uid }})"
server_uri: "ldapi:///"
bind_dn: "{{ resoudre_annuaire_bind_dn }}"
bind_pw: "{{ amorcage_acces_admin_password }}"
register: amorcage_acces_existant
changed_when: false
no_log: true
when: amorcage_acces_actif | bool
- name: Retenir si l'amorçage a déjà eu lieu
ansible.builtin.set_fact:
amorcage_acces_deja_fait: >-
{{ (amorcage_acces_existant.results | default([]) | length) > 0 }}
when: amorcage_acces_actif | bool
- name: Annoncer que l'amorçage est déjà fait (aucune action)
ansible.builtin.debug:
msg: >-
Compte `{{ amorcage_acces_uid }}` déjà présent : Set-OPS n'y touche pas.
Les habilitations sont gouvernées par une personne (D-67) — voir
docs/autorisation.md §6.
when:
- amorcage_acces_actif | bool
- amorcage_acces_deja_fait | bool
# --- 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.
- name: Hacher le jeton d'amorçage (slappasswd, SSHA)
ansible.builtin.command:
argv:
- slappasswd
- "-h"
- "{SSHA}"
- "-s"
- "{{ amorcage_acces_secret }}"
register: amorcage_acces_hache
changed_when: false
no_log: true
when:
- amorcage_acces_actif | bool
- not amorcage_acces_deja_fait | bool
# Le changement forcé s'appuie sur `pwdReset`, qui vient de l'overlay `ppolicy`.
# On le DÉTECTE au lieu de le supposer : sans l'overlay, l'attribut est inconnu du
# schéma et l'entrée entière est rejetée — panne obscure, et le compte n'existe pas
# du tout. Mesuré le 2026-08-07 : seul `back_mdb` était chargé.
- name: L'overlay ppolicy est-il chargé (attribut `pwdReset` connu) ?
ansible.builtin.command:
argv:
- ldapsearch
- "-Y"
- EXTERNAL
- "-H"
- "ldapi:///"
- "-b"
- "cn=schema,cn=config"
- "(olcAttributeTypes=*pwdReset*)"
- dn
register: amorcage_acces_ppolicy
changed_when: false
failed_when: false
when:
- amorcage_acces_actif | bool
- not amorcage_acces_deja_fait | bool
- name: Retenir si le changement forcé est applicable
ansible.builtin.set_fact:
amorcage_acces_reset_possible: >-
{{ amorcage_acces_changement_force | bool
and 'dn:' in (amorcage_acces_ppolicy.stdout | default('')) }}
when:
- amorcage_acces_actif | bool
- not amorcage_acces_deja_fait | bool
- name: Créer le compte d'amorçage du sysadmin
community.general.ldap_entry:
dn: "uid={{ amorcage_acces_uid }},ou=people,{{ resoudre_annuaire_base_dn }}"
objectClass:
- inetOrgPerson
- organizationalPerson
- person
- top
attributes:
uid: "{{ amorcage_acces_uid }}"
cn: "{{ amorcage_acces_nom }}"
sn: "{{ amorcage_acces_nom.split(' ') | last }}"
identite : une declaration de politique de mot de passe, deux executants Quatre defauts mesures dans l'integration Keycloak/LDAP, meme famille : une valeur declaree d'un cote, consommee de l'autre, rien qui verifie la jonction. 1. Aucune regle ne s'appliquait sur le chemin d'un vrai utilisateur. Sonde : « abcd » refuse par l'operation etendue LDAP, accepte par Keycloak (204), puis actif pour l'authentification. Keycloak ecrivait userPassword en direct (ppolicy aveugle) et le realm n'avait aucune passwordPolicy. 2. ldap_entry ne fait que CREER : la politique etait figee a sa creation. Le depot disait pwdMustChange TRUE, le serveur FALSE — une reconstruction from-zero aurait ressuscite la boucle du 2026-08-07. ldap_attrs state=exact reconcilie la politique et l'overlay (DN lu, pas devine). 3. syncRegistrations absent : un compte cree dans Keycloak n'atteignait jamais ou=people — acces web, aucune boite, invisible du modele de groupes. 4. Le prenom pointait sur cn (nom complet) : « Administrateur systeme systeme ». Ajoute roles/resoudre_politique_mdp : LA declaration, traduite en pwdPolicy, passwordPolicy et anti-force-brute. Les deux roles la consomment sans la redeclarer. usePasswordModifyExtendedOp ET validatePasswordPolicy : la seconde est porteuse, Keycloak se liant en rootDN et slapd n'appliquant pas ses controles de qualite au rootDN. La premiere seule aurait paru juste sans tenir. Verification : abcd -> 400 « minimum length 12 » et absent de LDAP ; mot de passe conforme -> 204 puis ldapwhoami accepte ; POST users -> 201 ET present dans ou=people. Second passage des deux playbooks : changed=0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 06:28:10 -04:00
# `cn` porte le nom complet ; le prenom a son propre attribut, et c'est lui
# que Keycloak doit lire — sinon il affiche le nom entier suivi du nom de
# famille. Derive comme `sn`, par le meme decoupage.
givenName: "{{ (amorcage_acces_nom.split(' ')[:-1] | join(' ')) | default(amorcage_acces_nom, true) }}"
userPassword: "{{ amorcage_acces_hache.stdout | trim }}"
pwdReset: "{{ 'TRUE' if amorcage_acces_reset_possible | bool else omit }}"
mail: "{{ amorcage_acces_courriel | default(omit, true) }}"
server_uri: "ldapi:///"
bind_dn: "{{ resoudre_annuaire_bind_dn }}"
bind_pw: "{{ amorcage_acces_admin_password }}"
no_log: true
when:
- amorcage_acces_actif | bool
- not amorcage_acces_deja_fait | bool
# `groupOfNames` EXIGE au moins un membre : un groupe vide est refusé par le
# schéma — ce qui tombe bien, un groupe d'habilitation sans personne n'aurait
# aucun sens à l'amorçage.
- name: Créer le groupe d'habilitation avec le sysadmin pour membre
community.general.ldap_entry:
dn: "cn={{ amorcage_acces_groupe }},ou=groups,{{ resoudre_annuaire_base_dn }}"
objectClass:
- groupOfNames
- top
attributes:
cn: "{{ amorcage_acces_groupe }}"
member: "uid={{ amorcage_acces_uid }},ou=people,{{ resoudre_annuaire_base_dn }}"
description: >-
Habilitation d'administration. Amorcé par Set-OPS, gouverné ensuite par
une personne — le dépôt ne réconcilie pas ses membres (D-67).
server_uri: "ldapi:///"
bind_dn: "{{ resoudre_annuaire_bind_dn }}"
bind_pw: "{{ amorcage_acces_admin_password }}"
no_log: true
when:
- amorcage_acces_actif | bool
- not amorcage_acces_deja_fait | bool
- name: Annoncer la reprise en main
ansible.builtin.debug:
msg: >-
{{ [
'Accès d amorçage créé : uid=' ~ amorcage_acces_uid ~ ', groupe=' ~ amorcage_acces_groupe ~ '.',
'Mot de passe en voûte sous `vault_sysadmin_amorcage`.',
(amorcage_acces_reset_possible | bool)
| ternary('Changement FORCÉ à la première ouverture (ppolicy actif).',
'ATTENTION : rien ne force le changement — l overlay ppolicy n est pas chargé. '
~ 'Le changer à la main dès la première connexion (docs/autorisation.md §6).'),
'À partir d ici, Set-OPS ne touche plus aux habilitations (D-67).'
] }}
when:
- amorcage_acces_actif | bool
- not amorcage_acces_deja_fait | bool