Introduction
OpenLDAP is the standard open-source LDAP directory server for centralized authentication and user management. Ansible automates the full stack: install OpenLDAP, configure the DIT (Directory Information Tree), manage users and groups, set up TLS, configure replication, and deploy SSSD clients for Linux authentication.
Deploy OpenLDAP Server
---
- name: Deploy OpenLDAP server
hosts: ldap_servers
become: true
vars:
ldap_domain: example.com
ldap_base_dn: "dc=example,dc=com"
ldap_org: "Example Corp"
ldap_admin_password: "{{ vault_ldap_admin_password }}"
tasks:
- name: Set debconf selections for slapd
ansible.builtin.debconf:
name: slapd
question: "{{ item.question }}"
value: "{{ item.value }}"
vtype: "{{ item.vtype }}"
loop:
- { question: slapd/internal/generated_adminpw, value: "{{ ldap_admin_password }}", vtype: password }
- { question: slapd/internal/adminpw, value: "{{ ldap_admin_password }}", vtype: password }
- { question: slapd/password1, value: "{{ ldap_admin_password }}", vtype: password }
- { question: slapd/password2, value: "{{ ldap_admin_password }}", vtype: password }
- { question: slapd/domain, value: "{{ ldap_domain }}", vtype: string }
- { question: shared/organization, value: "{{ ldap_org }}", vtype: string }
no_log: true
when: ansible_os_family == 'Debian'
- name: Install OpenLDAP
ansible.builtin.apt:
name: [slapd, ldap-utils]
state: present
when: ansible_os_family == 'Debian'
- name: Start slapd
ansible.builtin.service:
name: slapd
state: started
enabled: true
- name: Allow LDAP through firewall
ansible.posix.firewalld:
service: "{{ item }}"
permanent: true
state: enabled
immediate: true
loop: [ldap, ldaps]
- name: Create base OUs
community.general.ldap_entry:
dn: "ou={{ item }},{{ ldap_base_dn }}"
objectClass: organizationalUnit
attributes:
ou: "{{ item }}"
server_uri: ldap://localhost
bind_dn: "cn=admin,{{ ldap_base_dn }}"
bind_pw: "{{ ldap_admin_password }}"
loop:
- People
- Groups
- Services
- name: Create default groups
community.general.ldap_entry:
dn: "cn={{ item.name }},ou=Groups,{{ ldap_base_dn }}"
objectClass: posixGroup
attributes:
cn: "{{ item.name }}"
gidNumber: "{{ item.gid }}"
server_uri: ldap://localhost
bind_dn: "cn=admin,{{ ldap_base_dn }}"
bind_pw: "{{ ldap_admin_password }}"
loop:
- { name: developers, gid: 5000 }
- { name: ops, gid: 5001 }
- { name: admins, gid: 5002 }
Manage Users
- name: Create LDAP users
community.general.ldap_entry:
dn: "uid={{ item.uid }},ou=People,{{ ldap_base_dn }}"
objectClass:
- inetOrgPerson
- posixAccount
- shadowAccount
attributes:
uid: "{{ item.uid }}"
cn: "{{ item.cn }}"
sn: "{{ item.sn }}"
givenName: "{{ item.given_name }}"
mail: "{{ item.email }}"
uidNumber: "{{ item.uid_number }}"
gidNumber: "{{ item.gid_number }}"
homeDirectory: "/home/{{ item.uid }}"
loginShell: "{{ item.shell | default('/bin/bash') }}"
userPassword: "{{ item.password }}"
server_uri: ldap://localhost
bind_dn: "cn=admin,{{ ldap_base_dn }}"
bind_pw: "{{ ldap_admin_password }}"
loop: "{{ ldap_users }}"
no_log: true
- name: Add users to groups
community.general.ldap_attrs:
dn: "cn={{ item.group }},ou=Groups,{{ ldap_base_dn }}"
attributes:
memberUid: "{{ item.uid }}"
state: present
server_uri: ldap://localhost
bind_dn: "cn=admin,{{ ldap_base_dn }}"
bind_pw: "{{ ldap_admin_password }}"
loop: "{{ ldap_group_members }}"
User Variables
ldap_users:
- uid: jdoe
cn: John Doe
sn: Doe
given_name: John
email: jdoe@example.com
uid_number: 10001
gid_number: 5000
password: "{SSHA}hashed_password_here"
- uid: jsmith
cn: Jane Smith
sn: Smith
given_name: Jane
email: jsmith@example.com
uid_number: 10002
gid_number: 5001
password: "{SSHA}hashed_password_here"
ldap_group_members:
- { uid: jdoe, group: developers }
- { uid: jsmith, group: ops }
- { uid: jsmith, group: admins }
TLS Configuration
- name: Configure LDAP TLS
community.general.ldap_attrs:
dn: cn=config
attributes:
olcTLSCACertificateFile: /etc/ldap/ssl/ca.crt
olcTLSCertificateFile: /etc/ldap/ssl/server.crt
olcTLSCertificateKeyFile: /etc/ldap/ssl/server.key
state: exact
server_uri: ldapi:///
bind_dn: cn=config
bind_pw: "{{ ldap_config_password }}"
- name: Force TLS
community.general.ldap_attrs:
dn: cn=config
attributes:
olcSecurity: tls=1
state: exact
server_uri: ldapi:///
bind_dn: cn=config
bind_pw: "{{ ldap_config_password }}"
SSSD Client Configuration
---
- name: Configure LDAP authentication on clients
hosts: ldap_clients
become: true
tasks:
- name: Install SSSD and LDAP client
ansible.builtin.package:
name: [sssd, sssd-ldap, ldap-utils]
state: present
- name: Deploy SSSD config
ansible.builtin.template:
src: sssd.conf.j2
dest: /etc/sssd/sssd.conf
mode: '0600'
notify: restart sssd
- name: Enable SSSD
ansible.builtin.service:
name: sssd
state: started
enabled: true
- name: Enable mkhomedir
ansible.builtin.command: pam-auth-update --enable mkhomedir
changed_when: true
# templates/sssd.conf.j2
[sssd]
domains = {{ ldap_domain }}
services = nss, pam, ssh
[domain/{{ ldap_domain }}]
id_provider = ldap
auth_provider = ldap
ldap_uri = ldaps://{{ groups['ldap_servers'][0] }}
ldap_search_base = {{ ldap_base_dn }}
ldap_tls_reqcert = demand
ldap_tls_cacert = /etc/ldap/ssl/ca.crt
# User/group mappings
ldap_user_search_base = ou=People,{{ ldap_base_dn }}
ldap_group_search_base = ou=Groups,{{ ldap_base_dn }}
cache_credentials = true
offline_credentials_expiration = 7
enumerate = false
Password Policy
- name: Load password policy overlay
community.general.ldap_attrs:
dn: cn=module{0},cn=config
attributes:
olcModuleLoad: ppolicy
state: present
server_uri: ldapi:///
bind_dn: cn=config
bind_pw: "{{ ldap_config_password }}"
- name: Create password policy
community.general.ldap_entry:
dn: "cn=default,ou=Policies,{{ ldap_base_dn }}"
objectClass:
- pwdPolicy
- person
attributes:
cn: default
sn: default
pwdMaxAge: 7776000 # 90 days
pwdMinLength: 12
pwdInHistory: 5
pwdMaxFailure: 5
pwdLockout: "TRUE"
pwdLockoutDuration: 1800 # 30 minutes
pwdFailureCountInterval: 600
server_uri: ldap://localhost
bind_dn: "cn=admin,{{ ldap_base_dn }}"
bind_pw: "{{ ldap_admin_password }}"
Search and Verify
- name: Search for user
ansible.builtin.command: >
ldapsearch -x -H ldap://localhost
-b "{{ ldap_base_dn }}" "(uid=jdoe)" cn mail
register: ldap_search
changed_when: false
- name: Test authentication
ansible.builtin.command: >
ldapwhoami -x -H ldap://localhost
-D "uid=jdoe,ou=People,{{ ldap_base_dn }}"
-w "{{ test_password }}"
register: auth_test
changed_when: false
no_log: true
Troubleshooting
Check slapd Status
- name: Check slapd is listening
ansible.builtin.wait_for:
port: 389
timeout: 5
- name: Test LDAP connectivity
ansible.builtin.command: ldapsearch -x -H ldap://localhost -b "" -s base namingContexts
register: base_search
changed_when: false
Related Articles
- Ansible User Module
- Ansible SSH Key Management
- Ansible Compliance Guide
- Ansible OpenSSL Certificates
Conclusion
OpenLDAP provides centralized identity management for Linux infrastructure — Ansible automates the server deployment, DIT structure, user/group creation, TLS encryption, and SSSD client configuration. Use community.general.ldap_entry and ldap_attrs modules for declarative directory management. Users defined in YAML variables, password policies enforced centrally, and every client configured from inventory. Identity as code.