--- 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 }}" serveur_openldap_organisation: "{{ organisation | default('Organisation') }}" # 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). serveur_openldap_admin_password: "{{ vault_openldap_admin | default('') }}" # rempli depuis la voute (vault_openldap_admin) # Unites organisationnelles de base. serveur_openldap_ou: - people - groups # 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 }}' # --- 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:///" # --- 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. # 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: {} # 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 # --- 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