--- - name: Exiger le mot de passe admin LDAP (Vault) ansible.builtin.assert: that: - serveur_openldap_admin_password | length > 0 fail_msg: "serveur_openldap_admin_password est requis (a fournir via Ansible Vault)." - name: Installer debconf-utils (requis par le module debconf, fournit debconf-get-selections) ansible.builtin.apt: name: debconf-utils state: present update_cache: true cache_valid_time: 3600 - name: Preconfigurer slapd (debconf) ansible.builtin.debconf: name: slapd question: "{{ item.question }}" vtype: "{{ item.vtype }}" value: "{{ item.value }}" loop: - { question: "slapd/no_configuration", vtype: "boolean", value: "false" } - { question: "slapd/domain", vtype: "string", value: "{{ serveur_openldap_domaine }}" } - { question: "shared/organization", vtype: "string", value: "{{ serveur_openldap_organisation }}" } - { question: "slapd/backend", vtype: "select", value: "MDB" } - { question: "slapd/purge_database", vtype: "boolean", value: "true" } - { question: "slapd/move_old_database", vtype: "boolean", value: "true" } - { question: "slapd/allow_ldap_v2", vtype: "boolean", value: "false" } loop_control: label: "{{ item.question }}" - name: Preconfigurer le mot de passe admin slapd (debconf) ansible.builtin.debconf: name: slapd question: "{{ item }}" vtype: password value: "{{ serveur_openldap_admin_password }}" loop: - slapd/password1 - slapd/password2 no_log: true - name: Installer OpenLDAP ansible.builtin.apt: name: "{{ serveur_openldap_paquets }}" state: present update_cache: true cache_valid_time: 3600 - name: Activer et demarrer slapd when: not ansible_check_mode ansible.builtin.systemd: name: "{{ serveur_openldap_service }}" enabled: true state: started - name: Creer les unites organisationnelles de base community.general.ldap_entry: dn: "ou={{ item }},{{ serveur_openldap_base_dn }}" objectClass: organizationalUnit server_uri: "ldapi:///" bind_dn: "{{ serveur_openldap_admin_dn }}" bind_pw: "{{ serveur_openldap_admin_password }}" loop: "{{ serveur_openldap_ou }}" loop_control: 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 — Résoudre la politique (déclaration unique) ansible.builtin.include_role: name: resoudre_politique_mdp when: serveur_openldap_ppolicy_actif | bool - 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'} | combine(resoudre_politique_mdp_ldap, serveur_openldap_ppolicy_extra) }} server_uri: "ldapi:///" bind_dn: "{{ serveur_openldap_admin_dn }}" bind_pw: "{{ serveur_openldap_admin_password }}" no_log: true when: serveur_openldap_ppolicy_actif | bool # `ldap_entry` ne fait que CREER : l'entree restait figee a ce qu'elle valait le # jour de sa creation, et toute modification ulterieure de la declaration etait # ignoree en silence. Constate le 2026-08-08 — le depot annoncait # `pwdMustChange: TRUE`, le serveur portait `FALSE` (corrige a la main apres la # boucle de changement de mot de passe du 2026-08-07), et une reconstruction # from-zero aurait ressuscite le defaut. Il faut donc RECONCILIER en plus. - name: Ppolicy — Reconcilier la politique par defaut community.general.ldap_attrs: dn: "cn=default,ou=policies,{{ serveur_openldap_base_dn }}" attributes: "{{ resoudre_politique_mdp_ldap | combine(serveur_openldap_ppolicy_extra) }}" state: exact server_uri: "ldapi:///" bind_dn: "{{ serveur_openldap_admin_dn }}" bind_pw: "{{ serveur_openldap_admin_password }}" no_log: true when: serveur_openldap_ppolicy_actif | bool # Meme raison : l'overlay non plus n'etait pose qu'a la creation. Son DN porte un # index d'ordre (`{0}`, `{1}`…) attribue par slapd — on le LIT, on ne le devine pas. - name: Ppolicy — Trouver le DN de l overlay community.general.ldap_search: dn: "{{ serveur_openldap_mdb_dn }}" scope: onelevel filter: "(olcOverlay=*ppolicy)" attrs: - dn server_uri: "ldapi:///" register: serveur_openldap_overlay_ppolicy changed_when: false when: - serveur_openldap_ppolicy_actif | bool - serveur_openldap_mdb_dn is defined - name: Ppolicy — Reconcilier les reglages de l overlay community.general.ldap_attrs: dn: "{{ (serveur_openldap_overlay_ppolicy.results | first).dn }}" attributes: 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' }}" state: exact server_uri: "ldapi:///" when: - serveur_openldap_ppolicy_actif | bool - (serveur_openldap_overlay_ppolicy.results | default([]) | length) > 0 # --- TLS (LDAPS + STARTTLS) via le certificat step_ca depose par client_pki --- - name: TLS — creer le repertoire pont lisible par openldap ansible.builtin.file: path: "{{ serveur_openldap_tls_dir }}" state: directory owner: openldap group: openldap mode: "0750" when: serveur_openldap_tls_actif | bool - name: TLS — deployer le script de synchronisation du certificat ansible.builtin.template: src: setops-slapd-cert-sync.sh.j2 dest: /usr/local/sbin/setops-slapd-cert-sync owner: root group: root mode: "0755" when: serveur_openldap_tls_actif | bool - name: TLS — deployer les unites de synchronisation (service + path) ansible.builtin.template: src: "{{ item.s }}" dest: "{{ item.d }}" owner: root group: root mode: "0644" loop: - { s: "setops-slapd-cert-sync.service.j2", d: "/etc/systemd/system/setops-slapd-cert-sync.service" } - { s: "setops-slapd-cert-sync.path.j2", d: "/etc/systemd/system/setops-slapd-cert-sync.path" } loop_control: label: "{{ item.d | basename }}" when: serveur_openldap_tls_actif | bool - name: TLS — synchroniser le certificat maintenant (si client_pki l'a depose) ansible.builtin.command: /usr/local/sbin/setops-slapd-cert-sync changed_when: false when: - serveur_openldap_tls_actif | bool - not ansible_check_mode - name: TLS — activer la surveillance du certificat (renouvellement) ansible.builtin.systemd: name: setops-slapd-cert-sync.path enabled: true state: started daemon_reload: true when: - serveur_openldap_tls_actif | bool - not ansible_check_mode - name: TLS — verifier que le certificat pont est en place ansible.builtin.stat: path: "{{ serveur_openldap_tls_dir }}/cert.pem" register: serveur_openldap_cert_pont when: serveur_openldap_tls_actif | bool - name: TLS — avertir si le certificat n'est pas encore disponible ansible.builtin.debug: msg: >- client_pki n'a pas encore depose de certificat sur ce noeud ({{ serveur_openldap_tls_source_cert }}). TLS slapd NON configure : deploie l'integration client_pki sur ce noeud, puis relance le deploiement. when: - serveur_openldap_tls_actif | bool - not ansible_check_mode - not (serveur_openldap_cert_pont.stat.exists | default(false)) - name: TLS — configurer slapd (olcTLS* dans cn=config) community.general.ldap_attrs: dn: cn=config server_uri: ldapi:/// attributes: olcTLSCACertificateFile: "{{ serveur_openldap_tls_dir }}/ca.pem" olcTLSCertificateFile: "{{ serveur_openldap_tls_dir }}/cert.pem" olcTLSCertificateKeyFile: "{{ serveur_openldap_tls_dir }}/key.pem" state: exact when: - serveur_openldap_tls_actif | bool - not ansible_check_mode - serveur_openldap_cert_pont.stat.exists | default(false) notify: Redemarrer slapd - name: TLS — exposer LDAPS (SLAPD_SERVICES) ansible.builtin.lineinfile: path: /etc/default/slapd regexp: '^SLAPD_SERVICES=' line: 'SLAPD_SERVICES="{{ serveur_openldap_services }}"' when: - serveur_openldap_tls_actif | bool - serveur_openldap_cert_pont.stat.exists | default(false) notify: Redemarrer slapd