openldap : charge ppolicy — le jeton d'amorcage devient a usage unique
Le module etait sur disque mais jamais charge (seul back_mdb l'etait). Depuis
OpenLDAP 2.5 son schema est INTEGRE au module : aucun .ldif a charger.
La contrainte mord, mesuree sur un compte fraichement amorce :
Insufficient access (50)
Operations are restricted to bind/unbind/abandon/StartTLS/modify password
Le sysadmin peut se connecter et RIEN d'autre que changer son mot de passe. Ce
que la doctrine promettait est garanti techniquement, plus seulement demande.
`pwdMustChange` est ce qui donne son effet a `pwdReset` : sans lui, marquer une
entree n'oblige a rien. La politique apporte aussi longueur minimale 12,
verrouillage apres 5 echecs, historique. `olcPPolicyUseLockout` reste FALSE :
annoncer « compte verrouille » renseignerait un attaquant sur son existence.
Le DN de la base est LU, pas suppose : olcDatabase={1}mdb est l'usage mais
l'index n'est pas garanti.
La detection du role d'amorcage s'est verifiee d'elle-meme : rejoue apres le
chargement, il annonce « Changement FORCE » la ou il disait l'inverse une heure
plus tot.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
ba70b80197
commit
69536a2b1a
4 changed files with 158 additions and 5 deletions
35
CHANGELOG.md
35
CHANGELOG.md
|
|
@ -2,6 +2,41 @@
|
|||
|
||||
## 2026-08-06 — le chemin nord-sud devient dérivable
|
||||
|
||||
### `ppolicy` chargé — le jeton d'amorçage devient vraiment à usage unique
|
||||
|
||||
`serveur_openldap` charge désormais l'overlay `ppolicy` et pose une politique par défaut.
|
||||
Le module était sur disque (`/usr/lib/ldap/ppolicy.so`) mais jamais chargé : seul
|
||||
`back_mdb` l'était. Depuis OpenLDAP 2.5 son schéma est **intégré au module** — aucun
|
||||
`.ldif` à charger, contrairement à 2.4.
|
||||
|
||||
**La contrainte mord**, mesuré sur un compte fraîchement amorcé :
|
||||
|
||||
```
|
||||
$ ldapsearch -D uid=sysadmin,… -w <jeton>
|
||||
Insufficient access (50)
|
||||
Operations are restricted to bind/unbind/abandon/StartTLS/modify password
|
||||
```
|
||||
|
||||
Le sysadmin peut se connecter et **rien d'autre** que changer son mot de passe. Ce que la
|
||||
doctrine promettait est maintenant garanti techniquement, pas seulement demandé.
|
||||
|
||||
`pwdMustChange` est ce qui donne son effet à `pwdReset` : sans lui, marquer une entrée
|
||||
n'oblige à rien. Les deux vont ensemble, et c'est le genre de couple qu'on découvre en le
|
||||
testant. La politique apporte aussi la longueur minimale (12 — `pwdMinLength` n'est appliqué
|
||||
que si `pwdCheckQuality` > 0), le verrouillage après 5 échecs et l'historique.
|
||||
|
||||
`olcPPolicyUseLockout` reste à `FALSE`, délibérément : répondre « ce compte est verrouillé »
|
||||
renseignerait un attaquant sur l'existence du compte.
|
||||
|
||||
**Le DN de la base est lu, pas supposé.** `olcDatabase={1}mdb` est l'usage courant mais
|
||||
l'index n'est pas garanti — le rôle le cherche.
|
||||
|
||||
**Et la détection du rôle d'amorçage s'est vérifiée d'elle-même** : rejoué après le
|
||||
chargement, il annonce « Changement FORCÉ à la première ouverture (ppolicy actif) » là où il
|
||||
disait l'inverse une heure plus tôt. C'est exactement pourquoi il détecte au lieu de
|
||||
supposer.
|
||||
|
||||
|
||||
### Le rôle d'amorçage : `amorcage_acces`
|
||||
|
||||
Crée **un** compte (`uid=sysadmin`) et **un** groupe (`cn=sysadmin`) dans LDAP, puis se
|
||||
|
|
|
|||
|
|
@ -103,9 +103,9 @@ ansible-vault view instance/inventories/principal/group_vars/all/vault.yml \
|
|||
| grep vault_sysadmin_amorcage
|
||||
```
|
||||
|
||||
**Change-le immédiatement.** Tant que l'overlay `ppolicy` n'est pas chargé, rien ne
|
||||
l'impose techniquement (§3) — c'est à toi de le faire, et c'est le premier geste de la
|
||||
reprise.
|
||||
**Tu ne pourras rien faire d'autre avant de l'avoir changé** : l'annuaire refuse toute
|
||||
opération sauf le changement lui-même (§3). C'est le premier geste de la reprise, et il
|
||||
n'est pas optionnel.
|
||||
|
||||
> Le mot de passe de la voûte est lu depuis `ANSIBLE_VAULT_PASSWORD_FILE`
|
||||
> (`~/.config/setops-vault-pass` par défaut). **Sans ce fichier, rien de ce qui suit n'est
|
||||
|
|
@ -139,8 +139,8 @@ dépendent ni de Keycloak, ni de l'annuaire. C'est le sens de D-40.
|
|||
|
||||
### 6.4 Ce que tu dois changer en priorité
|
||||
|
||||
1. **Le mot de passe d'amorçage** — dès la première connexion. Rien ne l'impose encore :
|
||||
voir §3.
|
||||
1. **Le mot de passe d'amorçage** — imposé dès la première connexion, tu n'as pas le
|
||||
choix (§3).
|
||||
2. **Les comptes de secours** générés au déploiement : ils sont en voûte, et l'auteur du
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -32,3 +32,41 @@ serveur_openldap_tls_source_ca: "/etc/step/certs/root_ca.crt"
|
|||
serveur_openldap_tls_dir: "/etc/ldap/tls"
|
||||
# Services slapd exposes (ldaps ajoute pour le TLS ; ldapi pour l'admin local).
|
||||
serveur_openldap_services: "ldap:/// ldaps:/// ldapi:///"
|
||||
|
||||
# --- Politique de mot de passe (overlay ppolicy) ------------------------------
|
||||
# Sans cet overlay, `pwdReset` n'existe pas dans le schema : le changement force a
|
||||
# la premiere ouverture est impossible, et rien ne contraint la qualite ni ne
|
||||
# verrouille apres des echecs repetes. Mesure le 2026-08-07 : seul `back_mdb`
|
||||
# etait charge. Voir docs/autorisation.md §3.
|
||||
#
|
||||
# Depuis OpenLDAP 2.5, le schema ppolicy est INTEGRE au module : aucun fichier
|
||||
# `.ldif` a charger, contrairement a 2.4.
|
||||
serveur_openldap_ppolicy_actif: true
|
||||
|
||||
# `pwdMustChange` est ce qui donne son effet a `pwdReset` : sans lui, marquer une
|
||||
# entree n'oblige a rien. Les deux vont ensemble.
|
||||
serveur_openldap_ppolicy:
|
||||
pwdMustChange: "TRUE"
|
||||
# Qualite : 1 = verifier si possible, accepter sinon. `pwdMinLength` n'est
|
||||
# applique QUE si `pwdCheckQuality` > 0.
|
||||
pwdCheckQuality: "1"
|
||||
pwdMinLength: "12"
|
||||
# Verrouillage apres echecs repetes. `pwdLockoutDuration` en secondes.
|
||||
pwdLockout: "TRUE"
|
||||
pwdMaxFailure: "5"
|
||||
pwdFailureCountInterval: "900"
|
||||
pwdLockoutDuration: "900"
|
||||
# Historique : interdit de reprendre un mot de passe recent.
|
||||
pwdInHistory: "3"
|
||||
# L'utilisateur peut changer son propre mot de passe — indispensable, puisque
|
||||
# `pwdMustChange` l'y oblige.
|
||||
pwdAllowUserChange: "TRUE"
|
||||
|
||||
# Reglages de l'OVERLAY lui-meme (pas de la politique). `HashCleartext` hache un
|
||||
# mot de passe recu en clair au lieu de le stocker tel quel : filet de securite si
|
||||
# un client oublie de hacher.
|
||||
# `UseLockout` laisse volontairement a FALSE : dire « ce compte est verrouille »
|
||||
# renseigne un attaquant sur l'existence du compte. L'utilisateur legitime voit
|
||||
# « identifiants invalides » et attend la duree de verrouillage.
|
||||
serveur_openldap_ppolicy_hash_cleartext: true
|
||||
serveur_openldap_ppolicy_use_lockout: false
|
||||
|
|
|
|||
|
|
@ -66,6 +66,86 @@
|
|||
label: "ou={{ item }}"
|
||||
no_log: true
|
||||
|
||||
# --- Politique de mot de passe (overlay ppolicy) ------------------------------
|
||||
# Sans cet overlay, `pwdReset` n'existe pas dans le schema : `amorcage_acces` ne peut
|
||||
# pas forcer le changement du jeton d'amorcage, et rien ne contraint la qualite ni ne
|
||||
# verrouille apres des echecs. Voir docs/autorisation.md §3.
|
||||
- name: Ppolicy — Charger le module
|
||||
community.general.ldap_attrs:
|
||||
dn: "cn=module{0},cn=config"
|
||||
attributes:
|
||||
olcModuleLoad: "ppolicy"
|
||||
state: present
|
||||
server_uri: "ldapi:///"
|
||||
when: serveur_openldap_ppolicy_actif | bool
|
||||
|
||||
# Le DN de la base varie (`olcDatabase={1}mdb` en general, mais l'index n'est pas
|
||||
# garanti) : on le LIT au lieu de le supposer.
|
||||
- name: Ppolicy — Trouver la base mdb
|
||||
community.general.ldap_search:
|
||||
dn: "cn=config"
|
||||
scope: onelevel
|
||||
filter: "(olcDatabase={1}mdb)"
|
||||
attrs:
|
||||
- dn
|
||||
server_uri: "ldapi:///"
|
||||
register: serveur_openldap_base_mdb
|
||||
changed_when: false
|
||||
when: serveur_openldap_ppolicy_actif | bool
|
||||
|
||||
- name: Ppolicy — Retenir le DN de la base
|
||||
ansible.builtin.set_fact:
|
||||
serveur_openldap_mdb_dn: >-
|
||||
{{ (serveur_openldap_base_mdb.results | first).dn }}
|
||||
when:
|
||||
- serveur_openldap_ppolicy_actif | bool
|
||||
- (serveur_openldap_base_mdb.results | default([]) | length) > 0
|
||||
|
||||
- name: Ppolicy — Creer l unite des politiques
|
||||
community.general.ldap_entry:
|
||||
dn: "ou=policies,{{ serveur_openldap_base_dn }}"
|
||||
objectClass: organizationalUnit
|
||||
server_uri: "ldapi:///"
|
||||
bind_dn: "{{ serveur_openldap_admin_dn }}"
|
||||
bind_pw: "{{ serveur_openldap_admin_password }}"
|
||||
no_log: true
|
||||
when: serveur_openldap_ppolicy_actif | bool
|
||||
|
||||
# L'overlay AVANT la politique : `olcPPolicyDefault` reference une entree qui doit
|
||||
# exister, mais l'overlay doit etre en place pour que le schema pwd* soit connu.
|
||||
# L'ordre inverse echouerait des deux cotes.
|
||||
- name: Ppolicy — Activer l overlay sur la base
|
||||
community.general.ldap_entry:
|
||||
dn: "olcOverlay=ppolicy,{{ serveur_openldap_mdb_dn }}"
|
||||
objectClass:
|
||||
- olcOverlayConfig
|
||||
- olcPPolicyConfig
|
||||
attributes:
|
||||
olcOverlay: "ppolicy"
|
||||
olcPPolicyDefault: "cn=default,ou=policies,{{ serveur_openldap_base_dn }}"
|
||||
olcPPolicyHashCleartext: "{{ 'TRUE' if serveur_openldap_ppolicy_hash_cleartext | bool else 'FALSE' }}"
|
||||
olcPPolicyUseLockout: "{{ 'TRUE' if serveur_openldap_ppolicy_use_lockout | bool else 'FALSE' }}"
|
||||
server_uri: "ldapi:///"
|
||||
when:
|
||||
- serveur_openldap_ppolicy_actif | bool
|
||||
- serveur_openldap_mdb_dn is defined
|
||||
|
||||
- name: Ppolicy — Creer la politique par defaut
|
||||
community.general.ldap_entry:
|
||||
dn: "cn=default,ou=policies,{{ serveur_openldap_base_dn }}"
|
||||
objectClass:
|
||||
- pwdPolicy
|
||||
- person
|
||||
- top
|
||||
attributes: >-
|
||||
{{ {'cn': 'default', 'sn': 'default',
|
||||
'pwdAttribute': 'userPassword'} | combine(serveur_openldap_ppolicy) }}
|
||||
server_uri: "ldapi:///"
|
||||
bind_dn: "{{ serveur_openldap_admin_dn }}"
|
||||
bind_pw: "{{ serveur_openldap_admin_password }}"
|
||||
no_log: true
|
||||
when: serveur_openldap_ppolicy_actif | bool
|
||||
|
||||
# --- TLS (LDAPS + STARTTLS) via le certificat step_ca depose par client_pki ---
|
||||
- name: TLS — creer le repertoire pont lisible par openldap
|
||||
ansible.builtin.file:
|
||||
|
|
|
|||
Loading…
Reference in a new issue