Cree uid=sysadmin et cn=sysadmin dans LDAP. Prouve sur idm-01 : le compte
s'authentifie, et le second passage ne touche a rien (changed=0, 7 taches
sautees) — idempotence par EXISTENCE, pas par conformite (D-67).
Il suit l'annuaire au lieu de se declarer au plan : il ecrit par `ldapi:///` et
doit tourner sur cet hote. Le declarer comme groupe obligerait chaque instance a
le poser sur le bon hote, et elles ne le nomment pas pareil (idm-01 ici,
id-ldap-01 chez Technolibre) — je l'ai d'abord pose sur infra-pki-01 par erreur.
Trois defauts trouves en le construisant :
1. `pwdReset` n'existe pas dans ce schema (overlay ppolicy non charge) :
l'entree entiere etait rejetee et `no_log` masquait la cause. J'avais suppose
un mecanisme sans verifier. Le role le DETECTE maintenant, et la doctrine ne
promet plus un changement force qui n'a pas lieu.
2. Le mot de passe aurait ete stocke EN CLAIR : `ldap_entry` ecrit userPassword
litteralement. Hache par `slappasswd -h {SSHA}` desormais.
3. `voute.py` ne scannait que roles/<groupe> : un role applique par un playbook
sans etre un groupe echappait au recensement, ce que D-20 interdit. Il suit
maintenant les listes `roles:` des playbooks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
165 lines
6.2 KiB
YAML
165 lines
6.2 KiB
YAML
---
|
|
# 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 -----------------
|
|
|
|
# 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 }}"
|
|
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
|