OpenLDAP + phpLDAPadmin¶
OpenLDAP est une implémentation open-source du protocole LDAP (Lightweight Directory Access Protocol). Il s'agit du serveur d'annuaire le plus populaire pour gérer de maniÚre centralisée les identités, les utilisateurs, les groupes et leurs attributs.
L'image Docker que nous utilisons est osixia/openldap car c'est la référence communautaire. Elle est accompagnée de phpLDAPadmin, une interface graphique web pour visualiser et éditer le contenu de l'annuaire facilement.
đ Emplacement¶
La configuration complĂšte avec le conteneur d'initialisation se trouve dans :
compose/11-security-identity/identity-providers/openldap
đ FonctionnalitĂ©s¶
- Initialisation automatique avec un conteneur dédié (création OUs, utilisateurs, groupes).
- Interface web (phpLDAPadmin).
- Persistance des données.
- Mots de passe gérés par Docker Secrets et hachés en
{SSHA}. - Politique de mots de passe (
ppolicy) : longueur minimale et verrouillage anti-bruteforce. memberOfmaintenu automatiquement par l'overlaymemberof.- Script d'initialisation idempotent (relançable sans erreur).
đ ïž Utilisation¶
1. DĂ©marrer la stack¶
La stack comprend OpenLDAP, phpLDAPadmin et un script d'initialisation sécurisé par Docker Secrets :
cd compose/11-security-identity/identity-providers/openldap
# Initialiser le fichier d'environnement et les secrets
cp .env.example .env
cp -r .secrets.example .secrets
# Créer le réseau Traefik (une seule fois, si Traefik ne tourne pas déjà )
docker network create traefik-public
# Démarrer la stack
docker compose up -d
2. Le conteneur d'initialisation (init-ldap)¶
Nous avons inclus un conteneur nommé init-ldap.
Celui-ci attend que le serveur OpenLDAP soit prĂȘt, puis injecte un script d'initialisation (scripts/init.sh) qui :
1. Crée les OUs (Unités Organisationnelles) : ou=devops, ou=appdev.
2. Crée les utilisateurs cn=aurelien (uid nkaurelien), cn=idriss (uid nnid) et cn=michel (uid edmich), avec des mots de passe hachés en {SSHA}.
3. Reconfigure l'overlay memberof pour suivre groupOfNames/member (osixia le configure par défaut pour groupOfUniqueNames/uniqueMember).
4. Crée les groupes appdev-team (aurelien, idriss) et devops-team (aurelien, michel). L'overlay renseigne automatiquement memberOf sur chaque utilisateur.
5. Modifie les ACL (Listes de ContrĂŽle d'AccĂšs) pour autoriser cn=aurelien Ă lire l'annuaire.
6. Active l'overlay ppolicy et crée la politique par défaut cn=default,ou=policies.
Ce conteneur s'arrĂȘte une fois sa tĂąche terminĂ©e (restart: "no"). Il sort en erreur (code 1) si un secret est absent ou vide, ou si une opĂ©ration LDAP Ă©choue rĂ©ellement.
Le script est idempotent : les résultats « existe déjà » (codes LDAP 68 et 20) sont considérés comme des succÚs, et l'overlay ppolicy n'est ajouté que s'il est absent. Pour le relancer :
3. Interface Web phpLDAPadmin¶
Une fois le serveur démarré, vous pouvez accéder à l'interface d'administration. Les mots de passe ci-dessous sont les valeurs par défaut de .secrets.example/ ; ce sont les fichiers de .secrets/ qui font foi.
- AccĂšs direct (HTTP local) : http://localhost:8088
- AccÚs via Traefik (HTTPS / TLS) : https://ldap.kamitbrains.local (ou le nom d'hÎte configuré dans PHPLDAPADMIN_HOSTNAME)
â ïž Important pour la connexion : phpLDAPadmin requiert le DN complet (Distinguished Name) dans le champ Login DN, et non pas un simple identifiant/username.

Option A : Connexion Administrateur (Gestion complĂšte)¶
- Login DN :
cn=admin,dc=kamitbrains,dc=local - Mot de passe :
password(.secrets/ldap_admin_password.txt)
Option B : Connexion Utilisateur (ex: Aurelien)¶
- Login DN :
cn=aurelien,ou=devops,dc=kamitbrains,dc=local - Mot de passe :
Aurelien@123(.secrets/user_aurelien_password.txt) (Note : dans les applications tierces comme Nextcloud ou Grafana qui utilisent l'attributuid, l'identifiant Ă saisir serankaurelien).
Une fois connecté en admin, l'arbre de l'annuaire affiche les groupes, les OUs et le conteneur de politiques de mots de passe créés par init-ldap :

4. RequĂȘtes CLI (VĂ©rification)¶
Vous pouvez tester l'accĂšs LDAP directement depuis le conteneur principal avec la commande suivante :
docker exec -it openldap ldapsearch -x -D "cn=aurelien,ou=devops,dc=kamitbrains,dc=local" -w Aurelien@123 -b "dc=kamitbrains,dc=local"
cn=aurelien (uid nkaurelien) et son mot de passe sont correctement configurés et qu'il a l'accÚs en lecture.
Pour vérifier les appartenances aux groupes calculées par l'overlay :
docker exec openldap ldapsearch -x -LLL -D "cn=admin,dc=kamitbrains,dc=local" -w password \
-b "dc=kamitbrains,dc=local" "(objectClass=inetOrgPerson)" memberOf
đł Concepts et Objets LDAP Fondamentaux¶
L'annuaire LDAP organise les données sous forme d'un arbre hiérarchique (DIT : Directory Information Tree). Chaque entrée dans l'arbre est définie par son DN (Distinguished Name, son chemin absolu) et ses objectClass (qui définissent les attributs obligatoires et optionnels).
1. Structure de l'annuaire dĂ©ployĂ©¶
dc=kamitbrains,dc=local (Racine / Domaine)
â
âââ cn=appdev-team [Classe: groupOfNames]
âââ cn=devops-team [Classe: groupOfNames]
â
âââ ou=devops [Classe: organizationalUnit]
â âââ cn=aurelien [Classe: inetOrgPerson] (uid: nkaurelien)
â
âââ ou=appdev [Classe: organizationalUnit]
âââ cn=idriss [Classe: inetOrgPerson] (uid: nnid)
âââ cn=michel [Classe: inetOrgPerson] (uid: edmich)
2. Principaux types d'objets¶
| Type d'Objet | Classe principale | Description | Attributs usuels |
|---|---|---|---|
| Racine / Domaine | dcObject, organization |
Le sommet de l'arbre, représentant le domaine. | dc (Domain Component), o (Organization) |
| Unité d'organisation (OU) | organizationalUnit |
Dossier logique pour regrouper utilisateurs ou machines. | ou (Organizational Unit name) |
| Utilisateur / Compte | inetOrgPerson |
Compte utilisateur standard pour l'authentification. | cn, sn, givenName, mail, uid, userPassword |
| Groupe d'utilisateurs | groupOfNames |
Groupe statique contenant la liste des membres. | cn, description, member (DNs complets) |
| RĂŽle | organizationalRole |
Poste ou fonction dans l'organisation. | cn, roleOccupant |
3. Relation Utilisateur / Groupe (member vs memberOf)¶
member(sur le groupe) : Contient le DN de chaque personne membre (ex:member: cn=aurelien,ou=devops,dc=kamitbrains,dc=local).memberOf(sur l'utilisateur) : Attribut miroir placĂ© directement sur la fiche utilisateur pour simplifier les requĂȘtes de droits d'accĂšs depuis les applications clientes (Nextcloud, Keycloak, Grafana, Portainer).
Ne jamais écrire memberOf à la main
memberOf doit ĂȘtre calculĂ© par l'overlay memberof Ă partir des member des groupes. L'Ă©crire Ă la main crĂ©e des incohĂ©rences : ajouts et retraits de membres ne sont plus rĂ©percutĂ©s. Point d'attention avec osixia/openldap : l'overlay suit par dĂ©faut groupOfUniqueNames/uniqueMember. Avec des groupOfNames, il faut changer olcMemberOfGroupOC et olcMemberOfMemberAD avant de crĂ©er les groupes, ce que fait le script d'initialisation.
đ SĂ©curitĂ© et Durcissement¶
1. Hachage des Mots de Passe ({SSHA})¶
Les mots de passe ne sont jamais stockés en clair. Le conteneur d'initialisation utilise l'outil officiel OpenLDAP slappasswd pour générer un hachage salé au format {SSHA} (SHA-1 + sel aléatoire) lors de la création de chaque compte. {SSHA} reste un hachage rapide, suffisant pour un lab ; en production, préférez un hachage lent comme Argon2 (module pw-argon2).
# Exemple de génération d'un mot de passe sécurisé :
slappasswd -s "MonMotDePasseSecret"
# Résultat : {SSHA}hSJamTTdc8MudXG9O2Bw5pq6uifvPrdC
userPassword contient uniquement cette empreinte. Lors de l'authentification (via phpLDAPadmin ou une application cliente), OpenLDAP compare le sel et le hash sans jamais avoir besoin de connaĂźtre le mot de passe en clair.
2. SĂ©grĂ©gation des PrivilĂšges d'Administration (Double Secret)¶
Deux comptes administrateurs distincts sont configurés avec des mots de passe séparés via Docker Secrets :
- ldap_admin_password (cn=admin,dc=kamitbrains,dc=local) : Administrateur du DIT (données de l'annuaire : utilisateurs, groupes, OUs).
- ldap_config_password (cn=admin,cn=config) : Administrateur systÚme du moteur OpenLDAP (schémas, overlays, ACLs, modules).
Cette sĂ©paration empĂȘche qu'un compte ayant des droits sur les donnĂ©es puisse compromettre ou altĂ©rer le moteur OpenLDAP sous-jacent.
3. Overlay Password Policy (ppolicy) & Protection Anti-Bruteforce¶
L'overlay OpenLDAP ppolicy est activé automatiquement sur la base de données mdb. Une politique globale par défaut est appliquée sous cn=default,ou=policies,dc=kamitbrains,dc=local :
- ContrÎle qualité (pwdCheckQuality: 1) : indispensable, sans lui pwdMinLength est ignoré.
- Longueur minimale (pwdMinLength) : 8 caractĂšres obligatoires.
- Verrouillage de compte (pwdLockout) : Activé (TRUE).
- Tolérance aux échecs (pwdMaxFailure) : 5 tentatives infructueuses autorisées.
- Durée de verrouillage (pwdLockoutDuration) : 15 minutes (900 secondes) de blocage automatique du compte en cas de dépassement.
- FenĂȘtre de comptage des Ă©checs (pwdFailureCountInterval) : 15 minutes.
- Hachage automatique des modifications (olcPPolicyHashCleartext) : Tout mot de passe modifié via LDAP en clair est automatiquement haché avant stockage.
La politique ne s'applique pas au root DN (cn=admin,...), qui contourne ppolicy. Test rapide (doit échouer avec Constraint violation) :
docker exec openldap ldappasswd -x -D "cn=michel,ou=appdev,dc=kamitbrains,dc=local" -w Michel@123 -s abc
4. Bonnes Pratiques en Production¶
- Chiffrement réseau (TLS/LDAPS) : En production, privilégiez le port sécurisé
636(LDAPS) ouStartTLSsur le port389pour Ă©viter l'interception des requĂȘtes sur le rĂ©seau local. - phpLDAPadmin via Reverse Proxy HTTPS : Si l'interface web doit ĂȘtre exposĂ©e en dehors du rĂ©seau local, placez-la impĂ©rativement derriĂšre un Reverse Proxy avec certificat SSL valide (Traefik ou Nginx Proxy Manager).
- Certificats Traefik : Let's Encrypt ne délivre pas de certificat pour un nom en
.local. Utilisez un vrai domaine (challenge DNS pour un hÎte interne) ou acceptez le certificat auto-signé par défaut de Traefik en lab. - Label
traefik.docker.network: phpLDAPadmin est sur deux réseaux ; ce label force Traefik à passer partraefik-public, sinon il peut viser l'IP deldap-networket renvoyer une 502. - Modification des secrets : Les fichiers du dossier
.secrets/doivent impĂ©rativement ĂȘtre modifiĂ©s avec des mots de passe uniques et forts avant tout dĂ©ploiement en production.