2026-06-24 20:17:46 -04:00
|
|
|
---
|
|
|
|
|
serveur_openldap_paquets:
|
|
|
|
|
- slapd
|
|
|
|
|
- ldap-utils
|
|
|
|
|
- python3-ldap # requis par community.general.ldap_entry sur la cible
|
|
|
|
|
|
|
|
|
|
serveur_openldap_service: "slapd"
|
|
|
|
|
|
|
|
|
|
# Identite de l'annuaire.
|
|
|
|
|
serveur_openldap_domaine: "{{ domaine_interne }}"
|
2026-07-05 00:47:44 -04:00
|
|
|
serveur_openldap_organisation: "{{ organisation | default('Organisation') }}"
|
2026-06-24 20:17:46 -04:00
|
|
|
# Base DN derivee du domaine interne (ex. acme.local -> dc=acme,dc=local).
|
|
|
|
|
serveur_openldap_base_dn: "dc={{ serveur_openldap_domaine.split('.') | join(',dc=') }}"
|
|
|
|
|
serveur_openldap_admin_dn: "cn=admin,{{ serveur_openldap_base_dn }}"
|
|
|
|
|
|
|
|
|
|
# Secret OBLIGATOIRE (Ansible Vault).
|
2026-07-01 21:29:18 -04:00
|
|
|
serveur_openldap_admin_password: "{{ vault_openldap_admin | default('') }}" # rempli depuis la voute (vault_openldap_admin)
|
2026-06-24 20:17:46 -04:00
|
|
|
|
|
|
|
|
# Unites organisationnelles de base.
|
|
|
|
|
serveur_openldap_ou:
|
|
|
|
|
- people
|
|
|
|
|
- groups
|
annuaire : un compte de service par consommateur, et la porte se ferme
Keycloak, Dovecot, Postfix et Icinga Web 2 se liaient TOUS avec cn=admin, le compte
d administration de la base. C est le rootDN : slapd lui fait contourner toutes les
ACL. Un seul secret, quatre services, tous les droits sur l arbre — pour ce qui est,
trois fois sur quatre, une simple lecture.
Et les droits livres par Debian etaient intacts : `to * by * read`. Sur ldap://, sans
s authentifier, une machine du reseau enumerait tous les comptes et toutes les
adresses. Des comptes a droits mesures n auraient rien valu tant que cette ligne
restait : on aurait ferme la porte en laissant la fenetre.
L indice etait deja dans le depot. `validatePasswordPolicy` existe parce que slapd
n applique pas ses controles de qualite au rootDN : la consequence etait compensee,
la cause intacte.
- ou=services, un compte par consommateur, secret propre en voute
- sept regles d acces posees EN ENTIER (state: exact) : l ordre est la regle, et
inserer c est parier sur ce que le paquet aura mis avant nous
- amorcage_acces garde le compte d administration, NOMME comme l exception : il ne
consomme pas l annuaire, il le provisionne depuis la socket locale
- la sonde passe de -x a -Y EXTERNAL : elle lisait en anonyme et aurait annonce un
annuaire VIDE sur un annuaire parfaitement sain
- la rotation du compte d administration devient possible (elle n etait posee qu a
l installation, par debconf : la voute et slapd divergeaient en silence)
Quatre marches payees en chemin :
1. un cinquieme appelant oublie, dont l echec etait masque par no_log — la garde
refuse desormais SANS no_log : elle nomme la cle absente, jamais son contenu
2. la federation Keycloak ne reecrivait son bindDn que si l URL ou le mode changeaient
— nouveau secret, ancien nom, error code 49
3. la rotation placee APRES les taches qui se lient en administrateur
4. ansible-vault et son tube : sortie non bloquante = echec silencieux, la voute
paraissait tournee et etait identique a l octet
P72 exige que tout role incluant resoudre_annuaire NOMME son compte, et qu aucun sauf
amorcage_acces ne nomme admin. Eprouvee dans les deux sens.
Verifie sur l infrastructure : chaque compte lit ce qu il doit, aucun ne voit les
autres, la lecture anonyme rend 0 entree, et les quatre services repondent (doveadm
user, postmap -q, decouverte OIDC 200, portier SSO 200).
vault_openldap_admin et vault_ldap_bind_postfix renouveles : les deux avaient transite
en clair par une session d exploitation. Les anciennes valeurs rendent Invalid
credentials (49).
make prouver : 71 OK, 0 echec, 1 saute. ansible-lint : 0 failure, profil production.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-13 13:36:38 -04:00
|
|
|
# LES COMPTES DE SERVICE VIVENT A PART (2026-09-13). Melanges aux personnes, ils
|
|
|
|
|
# apparaitraient dans chaque recherche d'usager — et un annuaire ou « qui travaille
|
|
|
|
|
# ici » retourne des demons ne se lit plus.
|
|
|
|
|
- services
|
|
|
|
|
|
|
|
|
|
# --- QUI SE LIE A L'ANNUAIRE, ET AVEC QUELS DROITS (2026-09-13) ----------------------
|
|
|
|
|
#
|
|
|
|
|
# AVANT : les quatre consommateurs — Keycloak, Dovecot, Postfix, Icinga Web 2 — se liaient
|
|
|
|
|
# tous avec `cn=admin`, le compte d'administration de la base. C'est le rootDN : slapd lui
|
|
|
|
|
# fait CONTOURNER TOUTES LES ACL, y compris ses propres controles de qualite de mot de
|
|
|
|
|
# passe. Un seul secret, quatre services, tous les droits sur l'arbre entier — pour des
|
|
|
|
|
# operations qui sont, pour trois d'entre eux, de simples LECTURES.
|
|
|
|
|
#
|
|
|
|
|
# L'INDICE ETAIT DEJA DANS LE DEPOT. `serveur_keycloak_ldap_durcissement` porte
|
|
|
|
|
# `validatePasswordPolicy: true` avec ce motif : « Keycloak se lie en rootDN, et slapd
|
|
|
|
|
# n'applique pas ses controles de qualite au rootDN ». On avait compense la consequence
|
|
|
|
|
# sans traiter la cause. Avec un compte ordinaire, l'annuaire redevient juge de sa propre
|
|
|
|
|
# politique — le reglage reste, mais il n'est plus la seule barriere.
|
|
|
|
|
#
|
|
|
|
|
# `ecriture: true` n'est accorde qu'a ce qui DOIT ecrire. Keycloak est le seul : il porte
|
|
|
|
|
# les mots de passe a travers (edit_mode WRITABLE) et cree les comptes que le web ouvre
|
|
|
|
|
# (syncRegistrations). Les trois autres lisent.
|
|
|
|
|
serveur_openldap_comptes_service:
|
|
|
|
|
- cn: keycloak
|
|
|
|
|
secret: vault_ldap_bind_keycloak
|
|
|
|
|
ecriture: true
|
|
|
|
|
raison: "Federation OIDC : ecrit les mots de passe a travers et cree les comptes."
|
|
|
|
|
- cn: dovecot
|
|
|
|
|
secret: vault_ldap_bind_dovecot
|
|
|
|
|
ecriture: false
|
|
|
|
|
raison: "Qui a droit a sa boite aux lettres."
|
|
|
|
|
- cn: postfix
|
|
|
|
|
secret: vault_ldap_bind_postfix
|
|
|
|
|
ecriture: false
|
|
|
|
|
raison: "A qui livrer un courriel."
|
|
|
|
|
- cn: icingaweb2
|
|
|
|
|
secret: vault_ldap_bind_icingaweb2
|
|
|
|
|
ecriture: false
|
|
|
|
|
raison: "Qui peut voir la supervision."
|
|
|
|
|
|
|
|
|
|
serveur_openldap_services_dn: "ou=services,{{ serveur_openldap_base_dn }}"
|
|
|
|
|
|
|
|
|
|
# Empreinte du mot de passe d'administration APPLIQUE. `slappasswd` sale son condensat :
|
|
|
|
|
# comparer ce que slapd detient ne dit rien. On garde donc l'empreinte de la valeur
|
|
|
|
|
# voulue, exactement comme la federation Keycloak le fait pour sa liaison.
|
|
|
|
|
serveur_openldap_marqueur_admin: "/etc/ldap/.setops-admin-sha256"
|
|
|
|
|
serveur_openldap_admin_empreinte: "{{ serveur_openldap_admin_password | hash('sha256') }}"
|
|
|
|
|
|
|
|
|
|
# L'ANNUAIRE ETAIT LISIBLE PAR N'IMPORTE QUI (mesure du 2026-09-13).
|
|
|
|
|
#
|
|
|
|
|
# La regle livree par Debian est `to * by * read` : sur `ldap://`, sans s'authentifier,
|
|
|
|
|
# une machine du reseau enumerait tous les comptes et toutes les adresses. Des comptes de
|
|
|
|
|
# service a droits mesures n'auraient servi a rien tant que cette ligne restait — on
|
|
|
|
|
# aurait ferme la porte en laissant la fenetre.
|
|
|
|
|
#
|
|
|
|
|
# `false` restaure le comportement d'origine, pour un ecosysteme qui aurait une raison
|
|
|
|
|
# explicite de publier son annuaire. Ce n'est pas le cas par defaut.
|
|
|
|
|
serveur_openldap_lecture_anonyme: false
|
|
|
|
|
|
|
|
|
|
# L'identite du root LOCAL sur le socket unix. La sonde de cet annuaire l'utilise :
|
|
|
|
|
# elle compte les entrees sans reseau et sans secret. Fermer la lecture sans lui accorder
|
|
|
|
|
# ce droit rendrait la sonde aveugle — elle annoncerait un annuaire vide, c'est-a-dire la
|
|
|
|
|
# panne la plus grave, sur un annuaire parfaitement sain.
|
|
|
|
|
serveur_openldap_root_local_dn: "gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth"
|
|
|
|
|
|
|
|
|
|
# Raccourcis lisibles, pour que les regles ci-dessous se lisent comme des phrases.
|
|
|
|
|
serveur_openldap_ecrivains: >-
|
|
|
|
|
{% for c in serveur_openldap_comptes_service if c.ecriture | default(false) -%}
|
|
|
|
|
by dn.exact="cn={{ c.cn }},{{ serveur_openldap_services_dn }}" write {% endfor %}
|
|
|
|
|
serveur_openldap_lecteurs: >-
|
|
|
|
|
{% for c in serveur_openldap_comptes_service if not (c.ecriture | default(false)) -%}
|
|
|
|
|
by dn.exact="cn={{ c.cn }},{{ serveur_openldap_services_dn }}" read {% endfor %}
|
|
|
|
|
serveur_openldap_root_local: 'by dn.exact="{{ serveur_openldap_root_local_dn }}" read'
|
|
|
|
|
# Ce que voit une identite qui n'est designee par aucune regle precedente.
|
|
|
|
|
serveur_openldap_reste: "{{ 'by * read' if serveur_openldap_lecture_anonyme | bool else 'by users read by * none' }}"
|
|
|
|
|
|
|
|
|
|
# LES DROITS, DANS L'ORDRE OU slapd LES LIT. Voir tasks/main.yml pour ce que chaque
|
|
|
|
|
# numero protege — et pourquoi deplacer {0} ferme l'annuaire a tout le monde.
|
|
|
|
|
serveur_openldap_acces:
|
|
|
|
|
- '{0}to attrs=userPassword by self write by anonymous auth {{ serveur_openldap_ecrivains }} by * none'
|
|
|
|
|
- '{1}to attrs=shadowLastChange by self write by * read'
|
|
|
|
|
- '{2}to dn.subtree="{{ serveur_openldap_services_dn }}" {{ serveur_openldap_root_local }} by * none'
|
|
|
|
|
- >-
|
|
|
|
|
{3}to dn.subtree="ou=people,{{ serveur_openldap_base_dn }}"
|
|
|
|
|
{{ serveur_openldap_ecrivains }} {{ serveur_openldap_lecteurs }}
|
|
|
|
|
by self read {{ serveur_openldap_root_local }} by * none
|
|
|
|
|
- >-
|
|
|
|
|
{4}to dn.subtree="ou=groups,{{ serveur_openldap_base_dn }}"
|
|
|
|
|
{{ serveur_openldap_ecrivains }} {{ serveur_openldap_lecteurs }}
|
|
|
|
|
by users read {{ serveur_openldap_root_local }} by * none
|
|
|
|
|
- '{5}to dn.base="" by * read'
|
|
|
|
|
- '{6}to * {{ serveur_openldap_root_local }} {{ serveur_openldap_reste }}'
|
2026-07-02 07:27:43 -04:00
|
|
|
|
|
|
|
|
# --- TLS (LDAPS + STARTTLS) via le certificat d'hote step_ca ---
|
|
|
|
|
# Requiert l'integration client_pki sur le noeud (fournit le cert + le renouvellement).
|
|
|
|
|
serveur_openldap_tls_actif: true
|
|
|
|
|
# Certificat SOURCE depose par client_pki (root:root 600, illisible par openldap).
|
|
|
|
|
serveur_openldap_tls_source_cert: "/etc/step/certs/{{ ansible_fqdn | default(ansible_hostname) }}.crt"
|
|
|
|
|
serveur_openldap_tls_source_cle: "/etc/step/certs/{{ ansible_fqdn | default(ansible_hostname) }}.key"
|
|
|
|
|
serveur_openldap_tls_source_ca: "/etc/step/certs/root_ca.crt"
|
|
|
|
|
# Emplacement PONT, lisible par openldap ; resynchronise a chaque renouvellement.
|
|
|
|
|
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:///"
|
2026-08-07 13:52:00 -04:00
|
|
|
|
|
|
|
|
# --- 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.
|
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
|
|
|
# Les REGLES elles-memes ne sont plus declarees ici : elles vivent dans
|
|
|
|
|
# `resoudre_politique_mdp`, qui les traduit AUSSI pour Keycloak. Tant qu'elles
|
|
|
|
|
# n'existaient que de ce cote, Keycloak n'en validait aucune et le chemin que
|
|
|
|
|
# prend un vrai utilisateur ne verifiait rien (mesure du 2026-08-08).
|
|
|
|
|
# Laisser vide ; ne surcharger que pour ajouter un attribut pwd* hors politique.
|
|
|
|
|
serveur_openldap_ppolicy_extra: {}
|
2026-08-07 13:52:00 -04:00
|
|
|
|
|
|
|
|
# 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
|
sondes : les cinq qui manquaient le plus (autorite, base, annuaire, zones, edge)
CORRECTION DE COMPTE D ABORD. J avais annonce cinq groupes sans sonde. La
mesure en donne VINGT-SEPT : j avais compte ceux que j avais en tete, pas
ceux que le depot contient. Il en reste vingt-deux.
LES CINQ, PAR ORDRE DE DEGAT SILENCIEUX.
autorite (step_ca) — la plus urgente : nos certificats vivent 24 h, une AC
muette ne casse rien aujourd hui et casse TOUT demain, d un coup, sur les
21 machines. Elle surveille aussi l expiration de la RACINE, que personne
ne regarde parce qu elle vit des annees (relevee a 3642 jours).
base (postgresql) — une VRAIE requete, pas pg_isready : celui-ci dit que
le port repond, pas que la base sert. Plus le compte des connexions : a
saturation, chaque application tombe sans que la base ait l air morte.
annuaire (openldap) — elle COMPTE les entrees. Un annuaire vide repond
success a tout : le mensonge des sauvegardes vides, vert et sans contenu.
zones (powerdns) — un autoritatif sans zone repond NXDOMAIN a tout, ce qui
se lit comme « ce nom n existe pas ».
edge (nginx) — elle valide la configuration SUR DISQUE : nginx sert la
derniere valide, et une configuration cassee ne se voit qu au prochain
demarrage, souvent des mois plus tard.
Les cinq eprouvees vertes sur le sain puis rouges PAR PARAMETRE, sans
toucher a un service.
Etat : 20 sondes sur 19 roles, 23 services distincts, 70 instances, 67 au
vert. Les trois autres sont connues : deux sauvegardes sans donnee a
emporter, et Loki en delai de stabilisation apres redeploiement.
make prouver : CONFORME, 64 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-10 03:47:03 -04:00
|
|
|
|
|
|
|
|
# --- Sonde de supervision -----------------------------------------------------------
|
|
|
|
|
serveur_openldap_sonde_base: "{{ serveur_openldap_base_dn | default('dc=chezlepro,dc=internal') }}"
|
|
|
|
|
# UN MINIMUM, PAS UNE EGALITE : l'annuaire grandit. Ce chiffre dit « il reste peuple »,
|
|
|
|
|
# pas « il contient exactement ceci ». Trop haut, il crierait a chaque menage.
|
|
|
|
|
serveur_openldap_sonde_minimum: 5
|