--- - name: Set domain for slapd ansible.builtin.debconf: name: slapd question: slapd/domain vtype: string value: "{{ ldap_server_domain }}" - name: Set organisation for slapd ansible.builtin.debconf: name: slapd question: shared/organization vtype: string value: "{{ ldap_server_organization }}" - name: Install slapd ansible.builtin.apt: name: slapd state: present - name: Allow OpenLDAP user to traverse the directory with TLS private keys ansible.builtin.user: name: openldap append: true groups: ssl-cert register: openldap_in_ssl_cert - name: Restart slapd if group membership has changed (apply immediatelly) # noqa no-handler # [no-handler] Tasks that run when changed should likely be handlers # In order to be able to change LDAP server TLS configuration, it must be # able to read both the private key and certificate. Therefore we need to # immediatelly restart (since configuration is done live on the server. ansible.builtin.service: name: slapd state: restarted when: openldap_in_ssl_cert.changed - name: Install Python LDAP bindings ansible.builtin.apt: name: python3-pyldap state: present - name: Set-up LDAP server to listen on legacy SSL port ansible.builtin.lineinfile: dest: /etc/default/slapd state: present backrefs: true regexp: '^SLAPD_SERVICES=.*' line: 'SLAPD_SERVICES="ldap:/// ldaps:/// ldapi:///"' notify: - Restart slapd - name: Enable and start slapd service ansible.builtin.service: name: slapd state: started enabled: true - name: Change log level for slapd community.general.ldap_attrs: dn: cn=config attributes: olcLogLevel: "{{ ldap_server_log_level }}" state: exact - name: Test if LDAP misc schema has been applied ansible.builtin.command: "ldapsearch -H ldapi:/// -Q -LLL -A -Y EXTERNAL -b cn=schema,cn=config -s one '(cn={*}misc)' cn" register: ldap_misc_schema_present changed_when: false - name: Deploy LDAP misc schema ansible.builtin.command: "ldapadd -H ldapi:/// -Q -Y EXTERNAL -f /etc/ldap/schema/misc.ldif" when: not ldap_misc_schema_present.stdout changed_when: true # Always results in change due to task logic. # Technically, the only thing this does is pick the size of DH # parameters to use, with GnuTLS (against which slapd is linked # against under Debian) picking a matching DH parameter from RFC-7919 # (https://www.ietf.org/rfc/rfc7919.txt). - name: Generate the LDAP server Diffie-Hellman parameter community.crypto.openssl_dhparam: owner: root group: openldap mode: "0640" path: "/etc/ssl/private/{{ ansible_fqdn }}_ldap.dh.pem" size: 2048 notify: - Restart slapd - name: Deploy LDAP TLS private key ansible.builtin.template: src: "ldap_tls_key.j2" dest: "/etc/ssl/private/{{ ansible_fqdn }}_ldap.key" mode: "0640" owner: root group: openldap notify: - Restart slapd - name: Deploy LDAP TLS certificate ansible.builtin.template: src: "ldap_tls_cert.j2" dest: "/etc/ssl/certs/{{ ansible_fqdn }}_ldap.pem" mode: "0644" owner: root group: root notify: - Restart slapd - name: Deploy configuration file for checking certificate validity via cron ansible.builtin.copy: content: "/etc/ssl/certs/{{ ansible_fqdn }}_ldap.pem" dest: "/etc/check_certificate/{{ ansible_fqdn }}_ldap.conf" owner: root group: root mode: "0644" - name: Configure TLS community.general.ldap_attrs: dn: cn=config attributes: olcTLSCertificateFile: "/etc/ssl/certs/{{ ansible_fqdn }}_ldap.pem" olcTLSCertificateKeyFile: "/etc/ssl/private/{{ ansible_fqdn }}_ldap.key" olcTLSDHParamFile: "/etc/ssl/private/{{ ansible_fqdn }}_ldap.dh.pem" olcTLSCipherSuite: "{{ ldap_tls_ciphers }}" state: exact - name: Configure required SSF (make sure local unix socket connections are allowed) community.general.ldap_attrs: dn: cn=config attributes: olcLocalSSF: "{{ ldap_server_ssf }}" olcSecurity: "ssf={{ ldap_server_ssf }}" state: exact - name: Enable the memberof module community.general.ldap_attrs: dn: "cn=module{0},cn=config" attributes: olcModuleLoad: "{1}memberof" state: present - name: Enable the memberof overlay for database community.general.ldap_entry: dn: "olcOverlay={0}memberof,olcDatabase={1}mdb,cn=config" objectClass: - olcConfig - olcMemberOf - olcOverlayConfig attributes: olcOverlay: memberof olcMemberOfRefInt: "TRUE" olcMemberOfGroupOC: groupOfUniqueNames olcMemberOfMemberAD: uniqueMember - name: Apply database permissions m_ldap_permissions: filter: "(olcSuffix={{ ldap_server_int_basedn }})" rules: "{{ ldap_permissions }}" - name: Drop the admin entry corresponding to olcRootDN for database from directory community.general.ldap_entry: dn: "cn=admin,{{ ldap_server_int_basedn }}" state: absent - name: Create basic LDAP directory structure community.general.ldap_entry: dn: "ou={{ item }},{{ ldap_server_int_basedn }}" objectClass: - organizationalUnit attributes: ou: "{{ item }}" with_items: - people - groups - services - name: Create the entry that will contain mail service information community.general.ldap_entry: dn: "ou=mail,ou=services,{{ ldap_server_int_basedn }}" objectClass: - organizationalUnit attributes: ou: mail - name: Create LDAP directory structure for mail service community.general.ldap_entry: dn: "ou={{ item }},ou=mail,ou=services,{{ ldap_server_int_basedn }}" objectClass: - organizationalUnit attributes: ou: "{{ item }}" with_items: - domains - aliases - name: Create or remove login entries for services community.general.ldap_entry: dn: "cn={{ item.name }},ou=services,{{ ldap_server_int_basedn }}" objectClass: - applicationProcess - simpleSecurityObject attributes: cn: "{{ item.name }}" userPassword: "{{ item.password }}" state: "{{ item.state | default('present') }}" with_items: "{{ ldap_server_consumers }}" - name: Update services login passwords community.general.ldap_attrs: dn: "cn={{ item.name }},ou=services,{{ ldap_server_int_basedn }}" attributes: userPassword: "{{ item.password }}" state: exact with_items: "{{ ldap_server_consumers }}" when: "item.state | default('present') == 'present'" - name: Create or remove user-supplied groups community.general.ldap_entry: dn: "cn={{ item.name }},ou=groups,{{ ldap_server_int_basedn }}" objectClass: - groupOfUniqueNames attributes: cn: "{{ item.name }}" uniqueMember: "cn=NONE" state: "{{ item.state | default('present') }}" with_items: "{{ ldap_server_groups }}" - name: Create user-supplied LDAP entries community.general.ldap_entry: dn: "{{ item.dn }}" objectClass: "{{ item.attributes.objectClass }}" attributes: "{{ item.attributes }}" state: "{{ item.state | default('present') }}" with_items: "{{ ldap_entries }}" - name: Deploy firewall configuration for LDAP ansible.builtin.copy: src: "ferm_ldap.conf" dest: "/etc/ferm/conf.d/10-ldap.conf" owner: root group: root mode: "0640" notify: - Restart ferm # @TODO: This whole thing could be dropped if newer version of Ansible # was in use (where community collection has the ldap_search # module. - name: Deploy temporary file with LDAP admin password ansible.builtin.template: src: "ldap_admin_password.j2" dest: "/root/.ldap_admin_password" owner: root group: root mode: "0400" changed_when: false - name: Test if LDAP admin password needs to be changed ansible.builtin.command: "ldapwhoami -H ldapi:/// -D 'cn=admin,{{ ldap_server_int_basedn }}' -x -y /root/.ldap_admin_password" register: ldap_admin_password_check changed_when: ldap_admin_password_check.rc != 0 failed_when: false - name: Update LDAP admin password community.general.ldap_attrs: dn: "olcDatabase={1}mdb,cn=config" attributes: olcRootPW: "{{ ldap_admin_password | ldap_password_hash }}" state: exact when: ldap_admin_password_check.rc != 0 - name: Remove temporary file with LDAP admin password ansible.builtin.file: path: "/root/.ldap_admin_password" state: absent changed_when: false - name: Enable backup ansible.builtin.include_tasks: backup.yml when: enable_backup - name: Explicitly run all handlers ansible.builtin.include_tasks: ../handlers/main.yml when: "run_handlers | default(False) | bool()" tags: - handlers