Files @ 3d1e72c4cbaf
Branch filter:

Location: majic-ansible-roles/roles/ldap_server/tasks/main.yml

branko
MAR-192: Drop rsyslog/logrotate configuration for ldap_server role under Debian 12 Bookworm:

- Default installations of Debian 12 Bookworm no longer come with
rsyslog pre-installed (and it is considered to be deprecated as
default system logger under Debian 12 Bookworm).
---

- name: Set domain for slapd
  debconf:
    name: slapd
    question: slapd/domain
    vtype: string
    value: "{{ ldap_server_domain }}"

- name: Set organisation for slapd
  debconf:
    name: slapd
    question: shared/organization
    vtype: string
    value: "{{ ldap_server_organization }}"

- name: Install slapd
  apt:
    name: slapd
    state: present

- name: Allow OpenLDAP user to traverse the directory with TLS private keys
  user:
    name: openldap
    append: true
    groups: ssl-cert
  register: openldap_in_ssl_cert

- name: Restart slapd if group membership has changed (apply immediatelly)  # noqa 503
  # [503] 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.
  service:
    name: slapd
    state: restarted
  when: openldap_in_ssl_cert.changed

- name: Install Python LDAP bindings
  apt:
    name: python3-pyldap
    state: present

- name: Set-up LDAP server to listen on legacy SSL port
  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
  service:
    name: slapd
    state: started
    enabled: true

- name: Deploy system logger configuration file for slapd
  copy:
    src: slapd_rsyslog.conf
    dest: /etc/rsyslog.d/slapd.conf
    owner: root
    group: root
    mode: 0644
  when: "ansible_distribution_release == 'bullseye'"
  notify:
    - Restart rsyslog

- name: Deploy configuration file for log rotation of slapd logs
  copy:
    src: slapd_logrotate
    dest: /etc/logrotate.d/slapd
    owner: root
    group: root
    mode: 0644
  when: "ansible_distribution_release == 'bullseye'"

- name: Change log level for slapd
  ldap_attr:
    dn: cn=config
    state: exact
    name: olcLogLevel
    values: "{{ ldap_server_log_level }}"

- name: Test if LDAP misc schema has been applied
  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
  command: "ldapadd -H ldapi:/// -Q -Y EXTERNAL -f /etc/ldap/schema/misc.ldif"
  when: not ldap_misc_schema_present.stdout

# 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
  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
  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
  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
  copy:
    content: "/etc/ssl/certs/{{ ansible_fqdn }}_ldap.pem"
    dest: "/etc/check_certificate/{{ ansible_fqdn }}_ldap.conf"
    owner: root
    group: root
    mode: 0644

# We need to have this hack around TLS configuration because OpenLDAP
# expects both private key and certificate to be set at the same
# time.
#
# OpenLDAP server behaviour is a bit weird around this thing, so here
# is what happens:
#
# 1. First we set the private key, but ignore all errors. This has not
#    yet changed the private key path, though.
#
# 2. Then we set the certificate. This succeeds, but the private key
#    path still has the old value. If we haven't done the step (1),
#    this task would fail too.
#
# 3. Now we can finally change the private key too, and LDAP server
#    will be able to validate it against the corresponding certificate.
#
# See https://github.com/ansible/ansible/issues/25665 for more
# information.
- name: Configure TLS private key (ignore errors)
  ldap_attr:
    dn: cn=config
    name: olcTLSCertificateKeyFile
    values: "/etc/ssl/private/{{ ansible_fqdn }}_ldap.key"
    state: exact
  failed_when: false

- name: Configure TLS certificate
  ldap_attr:
    dn: cn=config
    name: olcTLSCertificateFile
    values: "/etc/ssl/certs/{{ ansible_fqdn }}_ldap.pem"
    state: exact

- name: Configure TLS private key
  ldap_attr:
    dn: cn=config
    name: olcTLSCertificateKeyFile
    values: "/etc/ssl/private/{{ ansible_fqdn }}_ldap.key"
    state: exact

- name: Configure DH parameter
  ldap_attr:
    dn: cn=config
    name: olcTLSDHParamFile
    values: "/etc/ssl/private/{{ ansible_fqdn }}_ldap.dh.pem"
    state: exact

- name: Configure TLS cipher suites
  ldap_attr:
    dn: cn=config
    name: olcTLSCipherSuite
    values: "{{ ldap_tls_ciphers }}"
    state: exact

- name: Configure SSF for local unix socket connections
  ldap_attr:
    dn: cn=config
    state: exact
    name: olcLocalSSF
    values: "{{ ldap_server_ssf }}"

- name: Configure required SSF
  ldap_attr:
    dn: cn=config
    state: exact
    name: olcSecurity
    values: "ssf={{ ldap_server_ssf }}"

- name: Enable the memberof module
  ldap_attr:
    dn: "cn=module{0},cn=config"
    state: present
    name: olcModuleLoad
    values: "{1}memberof"

- name: Enable the memberof overlay for database
  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
  ldap_entry:
    dn: "cn=admin,{{ ldap_server_int_basedn }}"
    state: absent

- name: Create basic LDAP directory structure
  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
  ldap_entry:
    dn: "ou=mail,ou=services,{{ ldap_server_int_basedn }}"
    objectClass:
      - organizationalUnit
    attributes:
      ou: mail

- name: Create LDAP directory structure for mail service
  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
  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
  ldap_attr:
    dn: "cn={{ item.name }},ou=services,{{ ldap_server_int_basedn }}"
    name: userPassword
    values: "{{ item.password }}"
    state: exact
  with_items: "{{ ldap_server_consumers }}"
  when: "item.state | default('present') == 'present'"

- name: Create or remove user-supplied groups
  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
  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
  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
  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
  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
  ldap_attr:
    dn: "olcDatabase={1}mdb,cn=config"
    name: olcRootPW
    values: "{{ ldap_admin_password | ldap_password_hash }}"
    state: exact
  when: ldap_admin_password_check.rc != 0

- name: Remove temporary file with LDAP admin password
  file:
    path: "/root/.ldap_admin_password"
    state: absent
  changed_when: false

- name: Enable backup
  include: backup.yml
  when: enable_backup

- name: Explicitly run all handlers
  include: ../handlers/main.yml
  when: "run_handlers | default(False) | bool()"
  tags:
    - handlers