ce-provision

LDAP Server

Install slapd and dependencies to run an LDAP directory server.

Configuration

By default the role simply installs slapd and nothing else. However, you can import configuration from an LDIF file using slapadd. To create a config file from another LDAP server use this command:

Place this file in the directory referenced in the ldap_server.config.path variable and set ldap_server.config.import to true and Ansible will attempt to import your config. You may have your config spread across multiple LDIF files, this is fine - put them all in the same directory and name them alphabetically if process order is important. By default the role expects the config to be stored in your ce-provision-config repository.

SSL is optional but if you do use it then you will be obliged to use the manual handling type of the SSL role, because LDAP expects a separate CA certificate file and only manual supports that. See the ssl role documentation for details.

This role does not try to import actual directory data, it concerns itself only with configuring a server. If you want to import data, you can export it from another server using slapcat in a similar way to the config, the only change is the database number:

Because this is sensitive data, you probably do not want to store it anywhere, rather import it and destroy it. If you want to write a private play to import your data, here’s a sample from our own private LDAP project:

---
- name: Stop slapd service. # you must stop the service or the database will be locked
  ansible.builtin.service:
    name: slapd
    state: stopped

# This is defending against a specific bug with Docker and the init script - https://stackoverflow.com/questions/30671693
- name: Ensure running slapd processes are killed.
  ansible.builtin.command:
    cmd: pkill slapd
  failed_when: false

- name: Copy LDAP data from config repo. # we've temporarily placed the directory data on our controller, this copies it to the LDAP server
  ansible.builtin.copy:
    src: "{{ _ce_provision_base_dir }}/config/files/ldap_server/data/data.ldif"
    dest: /usr/local/src/custom-ldap-schemas/data.ldif
    owner: openldap
    group: openldap
    mode: 0644

- name: Import our LDAP data.
  ansible.builtin.command:
    cmd: "slapadd -F /etc/ldap/slapd.d -n 1 -l /usr/local/src/custom-ldap-schemas/data.ldif"
  become_user: openldap
  become: true

- name: Start slapd service.
  ansible.builtin.service:
    name: slapd
    state: started

If you create a similar role on your controller server or in your config repository you can have Ansible import your data.

Replication

By default replication settings are not applied. If you want to set up a server to replicate another, existing LDAP host, you can set the details in ldap_server.replication. Note, passwords will be plain text so it is strongly advised you SOPS encrypt them before storing them in a code repository.

The role sets up basic replication and makes no assumptions about permissions for the replication user it creates on the host. If you need to set special permissions for replication on the host machine then you should write a custom role. You can use delegate_to: "{{ ldap_server.replication.host }}" in your tasks to execute tasks against the LDAP host and still have access to all the variables set for the consumer.

This is a good general resource on LDAP replication and how to configure it: http://www.rjsystems.nl/en/2100-d6-openldap-consumer.php

Default variables

---
ldap_server:
  slapd:
    user: openldap
    services: "ldap:/// ldapi:/// ldaps:///"
    ulimit: "8192"
    options: ""
    purge: false # Use this only for a completely clean install.
  # For safety, by default no config handling will occur. Use these variables to enable and provide LDIF files.
  config:
    import: false
    # The path to your LDIF files which define your schema.
    # All organisations have a different schema so this should be kept in your config repository.
    # Execution order can be important, so ensure your files are named in alphabetical order.
    path: "{{ _ce_provision_base_dir }}/config/files/ldap_server/config"
    purge: false
    backup: false # set to true to create local backups of LDAP
    backup_path: /opt/slap-bak
    backup_script: /usr/local/bin/slap-bak # full filename of the backup script
    slapcat_path: /usr/sbin # path to the location of slapcat on the server
    on_calendar: "*-*-* 23:45:00" # see systemd.time documentation - https://www.freedesktop.org/software/systemd/man/latest/systemd.time.html#Calendar%20Events
  # TLS settings in LDAP are not separately handled, you need to manually set it up or use the config import feature.
  # If you use 'manual' SSL handling you need to provide a separate CA certificate.
  # If you use 'letsencrypt' SSL handling then the LDAP TLS settings in your imported config should be as follows:
  #   olcTLSCACertificateFile: /etc/letsencrypt/live/{{ _domain_name }}/chain.pem
  #   olcTLSCertificateFile: /etc/letsencrypt/live/{{ _domain_name }}/cert.pem
  #   olcTLSCertificateKeyFile:  /etc/letsencrypt/live/{{ _domain_name }}/privkey.pem
  ssl: # @see the 'ssl' role - does nothing by default.
    replace_existing: false
    domain: "{{ _domain_name }}"
    handling: "unmanaged"
    key: ""
    cert: ""
    ca_cert: ""
    # Sample LetsEncrypt config, because include_role will not merge defaults these all need providing:
    # handling: letsencrypt
    # http_01_port: 5000
    # autorenew: true
    # email: sysadm@codeenigma.com
    # services: []
    # web_server: standalone
    # certbot_register_command: "certonly --agree-tos --preferred-challenges http -n"
    # certbot_renew_command: "certonly --agree-tos --force-renew"
    # reload_command: restart
    # reload:
    #   - slapd
    # on_calendar: "Mon *-*-* 04:00:00"
  replication:
    host: "" # host must be present in config/hosts for ce-provision, leave empty if no replication is desired
    port: "636"
    searchbase: "dc=example,dc=com"
    admin_cn: "cn=admin" # the admin user's canonical name, assumed to be the same on host and consumer
    admin_pwd: "" # the host admin bind password
    bind_cn: "cn={{ _domain_name }}" # the canonical name of the user on the host with read access to fetch changes
    bind_pwd: "" # the desired replication user password - will be generated if not provided
    interval: "00:00:07:00" # defaults to every 7 minutes