Déployer OpenLDAP : cn=config, TLS, ACL, sauvegardes et réplication
EdwardMoon
Ce guide de déploiement OpenLDAP couvre TLS, les ACL au moindre privilège, les index, la sauvegarde-restauration et la haute disponibilité syncrepl avec la configuration dynamique cn=config et le backend MDB d'OpenLDAP 2.6. Un annuaire étant au cœur de l'authentification, vérifiez le chiffrement, les droits, la cohérence et la récupération avant de considérer l'installation comme terminée.
Sous Rocky Linux 9, la source des paquets serveur et les répertoires dépendent du fournisseur ou de la méthode de compilation. Ce guide suppose une installation vérifiée d'OpenLDAP 2.6. Identifiez d'abord les chemins du paquet, puis modifiez cn=config par des opérations LDAP. Pour l'intégration des connexions aux hôtes, envisagez le fournisseur LDAP de SSSD plutôt que nss-pam-ldapd.

Composants et conception
| Composant | Rôle | Exigences d'exploitation |
|---|---|---|
| slapd | Traite les requêtes LDAP | Compte de service non privilégié, systemd et écoute TLS |
| cn=config | DIT de configuration dynamique actif | Modifier via LDAPI et SASL EXTERNAL |
| MDB | Stocke les données de l'annuaire | Taille du mapping, checkpoints et espace disque |
| ACL | Permissions par attribut et DN | Règles spécifiques d'abord ; refus par défaut |
| TLS | Chiffre les binds, recherches et réplications | Confiance dans l'AC, vérification des noms et suivi des expirations |
| syncrepl | Synchronise les changements entre fournisseurs | serverID et rid uniques, horloges synchronisées |
| slapcat/slapadd | Sauvegarde et restauration logiques | Conserver ensemble configuration et données |
Vérifier les paquets et les chemins
Ne devinez pas les noms des paquets. Consignez le binaire, le DIT de configuration, le répertoire de données et les certificats du service. Pour une compilation des sources OpenLDAP Project, conservez les résultats de configure, des tests et de l'installation avec les versions des dépendances. Pour des paquets fournisseur, vérifiez les signatures GPG et la politique de support.
slapd -VVV 2>&1 | head -n 40
ldapsearch -VV 2>&1 | head -n 20
command -v slapd ldapsearch ldapmodify slapcat slapadd slaptest
systemctl cat slapd
systemctl show slapd -p User -p Group -p ExecStart
findmnt -T /var/lib/ldap
df -hT /var/lib/ldap
df -i /var/lib/ldap
N'ajoutez pas de dépôts Internet arbitraires et n'exécutez pas de scripts d'installation non vérifiés directement dans un shell. Maintenez conjointement OpenLDAP 2.6, la bibliothèque TLS, LMDB, Cyrus SASL et la configuration systemd, et vérifiez la compatibilité LDIF en préproduction.
Examiner cn=config et MDB
Se connecter localement comme administrateur via LDAPI
sudo ldapwhoami -Q -Y EXTERNAL -H ldapi:///
sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b cn=config -s base dn cn
sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b cn=config '(&(objectClass=olcMdbConfig)(olcSuffix=*))' dn olcSuffix olcRootDN olcDbDirectory olcDbMaxSize olcDbIndex
-Q -Y EXTERNAL utilise les identifiants du pair sur le socket Unix, évitant un mot de passe cn=config dans la commande. N'exposez pas cn=config sur les interfaces distantes et sauvegardez les entrées en LDIF avant modification. Le {number} d'un DN de base varie selon l'installation : découvrez-le plutôt que de supposer une valeur fixe.
Sauvegarder cn=config
sudo install -d -m 0700 /var/backups/openldap
sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b cn=config '(objectClass=*)' '*' '+' | sudo tee "/var/backups/openldap/cn-config-before-$(date +%F-%H%M%S).ldif" >/dev/null
sudo chmod 0600 /var/backups/openldap/*.ldif
sudo sha256sum /var/backups/openldap/*.ldif
Définir le suffixe et le DN administrateur
L'exemple utilise dc=example,dc=com et cn=admin. Définissez d'abord le véritable nom de l'organisation, la propriété DNS et les règles de nommage. rootDN contourne toutes les ACL et n'est pas un compte applicatif. Réservez-le à la récupération d'urgence et déléguez les modifications courantes à un groupe d'administration.
Trouver le DN MDB et générer un hachage rootPW
MDB_DN=$(sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b cn=config '(&(objectClass=olcMdbConfig)(olcSuffix=*))' dn | awk '/^dn: / {print substr($0,5); exit}')
printf 'MDB_DN=%s
' "$MDB_DN"
test -n "$MDB_DN"
# Saisir le mot de passe interactivement pour l'exclure de l'historique du shell.
ROOT_HASH=$(slappasswd)
test -n "$ROOT_HASH"
Modifier suffix, rootDN et rootPW
sudo install -m 0600 /dev/null /root/openldap-database.ldif
sudo tee /root/openldap-database.ldif >/dev/null <<EOF
dn: ${MDB_DN}
changetype: modify
replace: olcSuffix
olcSuffix: dc=example,dc=com
-
replace: olcRootDN
olcRootDN: cn=admin,dc=example,dc=com
-
replace: olcRootPW
olcRootPW: ${ROOT_HASH}
EOF
unset ROOT_HASH
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f /root/openldap-database.ldif
sudo shred -u /root/openldap-database.ldif
Créer le DIT de base et les utilisateurs
Créer le DN de base et les unités organisationnelles
install -m 0600 /dev/null base.ldif
cat > base.ldif <<'LDIF'
dn: dc=example,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o: Example Directory
dc: example
dn: ou=People,dc=example,dc=com
objectClass: organizationalUnit
ou: People
dn: ou=Groups,dc=example,dc=com
objectClass: organizationalUnit
ou: Groups
LDIF
ldapadd -x -W -D 'cn=admin,dc=example,dc=com' -H ldapi:/// -f base.ldif
shred -u base.ldif
Créer un utilisateur de test et le groupe d'administration
USER_HASH=$(slappasswd)
install -m 0600 /dev/null people.ldif
cat > people.ldif <<EOF
dn: uid=alice,ou=People,dc=example,dc=com
objectClass: inetOrgPerson
uid: alice
cn: Alice Example
sn: Example
mail: alice@example.com
userPassword: ${USER_HASH}
dn: cn=ldap-admins,ou=Groups,dc=example,dc=com
objectClass: groupOfNames
cn: ldap-admins
member: uid=alice,ou=People,dc=example,dc=com
EOF
unset USER_HASH
ldapadd -x -W -D 'cn=admin,dc=example,dc=com' -H ldapi:/// -f people.ldif
shred -u people.ldif
Ne stockez pas les mots de passe utilisateur en clair dans des LDIF, Git ou des tickets. Séparez comptes applicatifs et personnels et tenez compte du schéma lorsque vous retirez le premier membre de groupOfNames. Imposez l'unicité de uid et mail avec un overlay ou un processus de provisionnement plutôt qu'avec les seules applications.
Imposer et valider TLS
olcSecurity: simple_bind=128 exige un chiffrement suffisant pour les simple binds avec mot de passe. Si olcSecurity existe déjà, fusionnez ses restrictions au lieu de les écraser. Les simple binds LDAPI peuvent aussi être refusés si le SSF local est inférieur à 128. Utilisez ensuite LDAPI SASL EXTERNAL pour l'administration et StartTLS/LDAPS avec AC validée pour les utilisateurs et la réplication. Cette politique serveur ne chiffre pas elle-même un mot de passe envoyé par le client : celui-ci doit explicitement utiliser -ZZ ou ldaps://.
Un simple bind sans TLS ne protège pas le mot de passe. Incluez dans les SAN les noms DNS réellement utilisés par les clients et autorisez uniquement le compte slapd à lire la clé. Distribuez la chaîne de l'AC publique ou interne à tous les clients LDAP et nœuds de réplication.
Vérifier les permissions et la validité du certificat
sudo install -d -o root -g ldap -m 0750 /etc/openldap/certs
sudo install -o root -g ldap -m 0640 ldap01.key /etc/openldap/certs/
sudo install -o root -g ldap -m 0644 ldap01.crt /etc/openldap/certs/
sudo install -o root -g ldap -m 0644 org-ca.crt /etc/openldap/certs/
openssl x509 -in /etc/openldap/certs/ldap01.crt -noout -subject -issuer -dates -ext subjectAltName
openssl verify -CAfile /etc/openldap/certs/org-ca.crt /etc/openldap/certs/ldap01.crt
Appliquer les chemins TLS dans cn=config
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// <<'LDIF'
dn: cn=config
changetype: modify
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/openldap/certs/org-ca.crt
-
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/certs/ldap01.crt
-
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/openldap/certs/ldap01.key
-
replace: olcTLSProtocolMin
olcTLSProtocolMin: 3.3
-
replace: olcSecurity
olcSecurity: simple_bind=128
LDIF
sudo systemctl restart slapd
sudo systemctl status slapd --no-pager
Vérifier les noms d'hôtes en StartTLS et LDAPS
openssl s_client -connect ldap01.example.com:636 -servername ldap01.example.com -verify_hostname ldap01.example.com -CAfile /etc/openldap/certs/org-ca.crt -verify_return_error </dev/null
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt ldapwhoami -x -ZZ -H ldap://ldap01.example.com -D 'uid=alice,ou=People,dc=example,dc=com' -W
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt ldapsearch -LLL -x -H ldaps://ldap01.example.com -D 'uid=alice,ou=People,dc=example,dc=com' -W -b 'dc=example,dc=com' '(uid=alice)' dn cn mail
Créer un compte de réplication dédié et tester le refus des binds non chiffrés
Préparez d'abord le compte dédié utilisé par les deux fournisseurs. Son mot de passe doit correspondre au secret de la connexion de réplication. S'il existe déjà, vérifiez-le avec ses ACL au lieu de créer une nouvelle entrée.
REPL_HASH=$(slappasswd)
umask 077
cat > replicator.ldif <<EOF
dn: uid=replicator,ou=People,dc=example,dc=com
objectClass: inetOrgPerson
uid: replicator
cn: Replication Reader
sn: Reader
userPassword: ${REPL_HASH}
EOF
unset REPL_HASH
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt \
ldapadd -x -ZZ -H ldap://ldap01.example.com \
-D 'cn=admin,dc=example,dc=com' -W -f replicator.ldif
rm -f replicator.ldif
# Envoyer une chaîne de test, pas un vrai mot de passe. Vérifier le refus confidentiality required.
ldapwhoami -x -H ldap://ldap01.example.com \
-D 'uid=transport-probe,ou=People,dc=example,dc=com' -w not-a-real-password
Configurer les ACL au moindre privilège
OpenLDAP évalue les ACL de haut en bas selon les premières règles to et by correspondantes. Traitez d'abord les attributs sensibles comme userPassword et placez les règles générales à la fin. Ne comptez pas sur une lecture publique : accordez l'accès anonyme auth uniquement là où le bind l'exige.
sudo tee /root/openldap-acl.ldif >/dev/null <<EOF
dn: ${MDB_DN}
changetype: modify
replace: olcAccess
olcAccess: {0}to * by dn.exact="uid=replicator,ou=People,dc=example,dc=com" read by * break
olcAccess: {1}to attrs=userPassword by self =xw by anonymous auth by * none
olcAccess: {2}to dn.subtree="ou=People,dc=example,dc=com" attrs=entry,children,cn,sn,uid,mail by self write by group.exact="cn=ldap-admins,ou=Groups,dc=example,dc=com" write by users read by * none
olcAccess: {3}to dn.subtree="ou=Groups,dc=example,dc=com" by group.exact="cn=ldap-admins,ou=Groups,dc=example,dc=com" write by users read by * none
olcAccess: {4}to * by users read by * none
-
add: olcLimits
olcLimits: {0}dn.exact="uid=replicator,ou=People,dc=example,dc=com" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
EOF
sudo chmod 0600 /root/openldap-acl.ldif
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f /root/openldap-acl.ldif
sudo shred -u /root/openldap-acl.ldif
Tester les accès autorisés et refusés
# Les clients anonymes ne doivent pas pouvoir lire userPassword.
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt ldapsearch -LLL -x -ZZ -H ldap://ldap01.example.com -b 'uid=alice,ou=People,dc=example,dc=com' userPassword
# Vérifier le bind de l'utilisateur et la lecture des attributs ordinaires.
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt ldapsearch -LLL -x -ZZ -H ldap://ldap01.example.com -D 'uid=alice,ou=People,dc=example,dc=com' -W -b 'uid=alice,ou=People,dc=example,dc=com' cn mail
sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b "$MDB_DN" olcAccess
Index et santé de MDB
Fondez les index sur les filtres de recherche et attributs de bind réels. Ajouter des index eq et sub à tous les attributs augmente le coût des écritures et la taille de la base. Analysez d'abord les journaux d'accès ou requêtes applicatives. Si des données existent déjà, exécutez slapindex pendant une maintenance.
sudo tee /root/openldap-index.ldif >/dev/null <<EOF
dn: ${MDB_DN}
changetype: modify
replace: olcDbIndex
olcDbIndex: objectClass eq
olcDbIndex: entryUUID,entryCSN eq
olcDbIndex: uid,mail eq
olcDbIndex: cn,sn eq,sub
EOF
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f /root/openldap-index.ldif
sudo shred -u /root/openldap-index.ldif
sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b "$MDB_DN" olcDbIndex olcDbMaxSize olcDbCheckpoint
L'indexation des données existantes et le rétablissement des propriétaires du répertoire dépendent des chemins du paquet. Retirez slapd du service et arrêtez-le avant d'exécuter slapindex, puis vérifiez slaptest et les propriétaires avant le redémarrage. Ne copiez ni ne modifiez arbitrairement des fichiers MDB utilisés en production.
Sauvegardes slapcat et exercices de restauration
Sauvegardez cn=config et le suffixe applicatif. Avec un seul nœud, arrêtez slapd pendant une maintenance. En HA, retirez une réplique du répartiteur et créez-y une sauvegarde LDIF cohérente. Chiffrez les fichiers, copiez-les sur un autre système et consignez les sommes de contrôle.
sudo bash -euo pipefail <<'BASH'
backup_dir="/var/backups/openldap/$(date +%F-%H%M%S)"
install -d -m 0700 "$backup_dir"
umask 077
systemctl stop slapd
trap 'systemctl start slapd' EXIT
slapcat -F /etc/openldap/slapd.d -n 0 > "$backup_dir/cn-config.ldif"
slapcat -F /etc/openldap/slapd.d -b 'dc=example,dc=com' > "$backup_dir/example-data.ldif"
test -s "$backup_dir/cn-config.ldif"
test -s "$backup_dir/example-data.ldif"
(cd "$backup_dir" && sha256sum ./*.ldif > SHA256SUMS && sha256sum -c SHA256SUMS)
BASH
sudo systemctl status slapd --no-pager
sudo journalctl -u slapd --since '-5 min' --no-pager
Points à vérifier lors d'une restauration
sudo slaptest -F /etc/openldap/slapd.d -u
sudo slapcat -F /etc/openldap/slapd.d -n 0 | head
sudo slapcat -F /etc/openldap/slapd.d -b 'dc=example,dc=com' | head
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt ldapsearch -LLL -x -ZZ -H ldap://ldap01.example.com -D 'cn=admin,dc=example,dc=com' -W -b 'dc=example,dc=com' '(objectClass=*)' dn | tail
- Sur un hôte isolé avec slapd.d et les répertoires de données vides, exécuter slapadd pour la configuration, puis les données.
- Rétablir le propriétaire selon le compte de service du paquet et restaurer les contextes SELinux.
- Exécuter slaptest, puis valider LDAPI, StartTLS/LDAPS et la vérification des noms d'hôtes.
- Contrôler par échantillonnage le DN de base, le nombre d'entrées, les groupes importants et les binds avec mot de passe.
- Retester les opérations permises aux comptes applicatifs et les refus d'accès anonymes.
- Consigner le délai de récupération et les données manquantes pour affiner RPO et RTO.
syncrepl et haute disponibilité
Le compte de réplication doit lire toutes les données sources, dont userPassword, ainsi que les attributs opérationnels pour conserver des données d'authentification identiques. La première ACL ci-dessus accorde la lecture complète uniquement au DN de réplication ; les autres connexions poursuivent les règles existantes via by * break. Ne réutilisez pas ce compte dans les applications. Si le paquet fournit syncprov comme module dynamique, identifiez son chemin et chargez-le d'abord.
Une configuration multiprovider ne signifie pas que tous les clients doivent écrire simultanément sur les deux fournisseurs. Pour réduire les conflits et le split-brain, faites sélectionner un seul chemin d'écriture actif par le répartiteur et concevez séparément la répartition des lectures, les contrôles de santé et le retour au primaire. Attribuez des serverID et rid syncrepl uniques.
Configurer l'overlay syncprov et les checkpoints
sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// <<EOF
dn: olcOverlay=syncprov,${MDB_DN}
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpCheckpoint: 100 10
olcSpSessionlog: 1000
EOF
Exemple de structure syncrepl pour chaque fournisseur
dn: cn=config
changetype: modify
replace: olcServerID
olcServerID: 1 ldaps://ldap01.example.com
olcServerID: 2 ldaps://ldap02.example.com
dn: olcDatabase={MDB_NUMBER}mdb,cn=config
changetype: modify
add: olcSyncrepl
olcSyncrepl: rid=001 provider=ldaps://ldap02.example.com bindmethod=simple binddn="uid=replicator,ou=People,dc=example,dc=com" credentials=<REPLICATION_SECRET> searchbase="dc=example,dc=com" type=refreshAndPersist retry="5 5 300 +" timeout=10 tls_reqcert=demand tls_cacert=/etc/openldap/certs/org-ca.crt
-
add: olcMultiProvider
olcMultiProvider: TRUE
Ce LDIF illustre la structure. Adaptez {MDB_NUMBER}, serverID, rid, provider et le secret à chaque nœud, puis appliquez-le depuis un fichier protégé. Les secrets simple bind sont stockés dans cn=config : renforcez les permissions de l'annuaire et le chiffrement des sauvegardes, ou envisagez SASL EXTERNAL avec des certificats clients.
Vérifier la cohérence de réplication et la bascule
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt ldapsearch -LLL -x -H ldaps://ldap01.example.com -D 'uid=replicator,ou=People,dc=example,dc=com' -W -b 'dc=example,dc=com' -s base contextCSN
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt ldapsearch -LLL -x -H ldaps://ldap02.example.com -D 'uid=replicator,ou=People,dc=example,dc=com' -W -b 'dc=example,dc=com' -s base contextCSN
sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b cn=Monitor '(objectClass=monitorOperation)' cn monitorOpInitiated monitorOpCompleted
timedatectl status
chronyc tracking
Confirmez que l'ajout, la modification et la suppression d'une entrée de test atteignent l'autre fournisseur. Retirez ensuite un fournisseur du répartiteur et testez les lectures, écritures et le retour dans le service. Des contextCSN différents ne prouvent pas à eux seuls une panne : examinez aussi les journaux, opérations en attente, connexions et délai de convergence.
Contrôles d'exploitation
systemctl --no-pager --full status slapd
ss -lntp | grep -E ':(389|636)\b'
sudo journalctl -u slapd --since '-30 min' --no-pager
sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b cn=Monitor -s base '*' '+'
df -hT /var/lib/ldap /var/log
df -i /var/lib/ldap /var/log
findmnt -T /var/lib/ldap
- Examiner le code de sortie systemd et le premier horodatage d'erreur.
- Vérifier que 389 et 636 écoutent uniquement sur les adresses prévues.
- Contrôler la chaîne d'AC, les SAN, les dates de validité et la vérification des noms côté client.
- Reproduire le DN de bind, le DN de base, le filtre, la portée et l'ordre des ACL avec une requête minimale.
- Vérifier la taille du mapping MDB, l'espace et les inodes, les checkpoints et les requêtes longues.
- Examiner les connexions syncrepl, serverID, rid, contextCSN et la synchronisation horaire.
- Vérifier les dernières sommes slapcat et les résultats de restauration isolée.
Erreurs fréquentes et solutions
| Erreur | Impact | Solution |
|---|---|---|
| Simple binds en clair | Expose les mots de passe | Exiger StartTLS ou LDAPS avec vérification de l'AC |
| Utiliser rootDN dans les applications | Contourne toutes les ACL | Utiliser des DN de service et des groupes au moindre privilège |
| Lecture anonyme complète | Expose des données personnelles et la structure de l'organisation | N'autoriser que l'accès nécessaire à l'authentification, puis refuser par défaut |
| Supposer les numéros de DN de base fixes | Risque de modifier une autre entrée selon l'installation | Découvrir le DN réel par une recherche cn=config |
| Copier les fichiers MDB actifs | Sauvegarde incohérente | Utiliser slapcat et tester la restauration |
| Écritures concurrentes sans restriction sur les deux fournisseurs | Conflits et split-brain | Utiliser un chemin d'écriture actif avec bascule contrôlée |
| Ignorer la vérification TLS | Risque de connexion à un serveur usurpé | Valider l'AC, les SAN, le nom d'hôte et l'expiration |
Ressources associées
- Guide officiel d'administration OpenLDAP 2.6
- Contrôle d'accès OpenLDAP
- Réplication OpenLDAP
- Guide client Red Hat SSSD LDAP et TLS
- Diagnostic de l'espace disque et des inodes sous Linux
- Haute disponibilité DRBD, Pacemaker et STONITH
- Playbooks Ansible, Vault et déploiements progressifs
Conclusion
Un bind réussi ne suffit pas à valider OpenLDAP. Vérifiez les chemins cn=config et MDB, les noms TLS, les ACL par attribut, les index fondés sur les requêtes, les sauvegardes conjointes de configuration et données et la restauration isolée. Pour une authentification prévisible en HA, conservez des serverID et rid uniques, des horloges synchronisées et un seul chemin d'écriture actif.