Leçons apprises : LDAP avec Dolibarr orienté performance

Comment réussir l’intégration d’un serveur LDAP dans Dolibarr tout en gardant un excellent niveau de performance et de maintenabilité.


1. Introduction

Dolibarr est une suite ERP/CRM open‑source écrite en PHP qui relie le gestionnaire de données (clients, fournisseurs, comptes bancaires, dossiers, etc.) à une ou plusieurs sources d’authentification.
Le recours à LDAP (Lightweight Directory Access Protocol) pour centraliser les comptes utilisateurs, les groupes et les droits est très attractif :

  • Unicité des informations d’identité (pas de duplication entre bases locales et annuaires).
  • Simplicité d’administration des comptes (création, désactivation, reset de mot‑de‑passe).
  • Contrôle granulaire des attributs (mail, téléphone, adresse postale).

Cependant, un serveur LDAP introduit des latences additionnelles et une charge réseau qui, s’il n’est pas correctement dimensionné, peuvent ralentir l’ensemble de l’application.

L’article suivant reprend les pièges rencontrés et les bonnes pratiques qui permettent d’optimiser cette intégration dans un contexte de forte charge.


2. Architecture recommandée

2.1 Schéma simplifié

+----------------+          +-----------------------+          +-------------------+
| Browser (HTTPS) --> | Dolibarr (PHP) --> | Cache | LDAP Server (OpenLDAP / AD) |
+----------------+ +-----------------------+ +-------------------+

  • Le cache (Redis, Memcached ou le opcache de PHP) stocke les résultats déjà interrogés. Le temps de réponse critique dépend du nombre d’appels simultanés : on essaye de réduire chaque appel LDAP à une seule opération compact*.

2.2 Choix du serveur LDAP

Solution Avantages Points d’attention
OpenLDAP (standalone) Open‑source, facile à déployer, supporting TLS Nécessite un dédié pour les writes si les écritures sont fréquentes
Microsoft Active Directory (AD) Intégré Windows, déjà utilisé dans l’entreprise Nécessite Kerberos ou NTLM sur Linux → plus complexe à configurer
FreeIPA / 389 Directory Server Bonnes performances, web‑admin Moins répandu que OpenLDAP/AD

Le choix le plus fréquent dans les environnements mixtes reste OpenLDAP synchronisé avec AD via syncrepl ou realmd.


3. Paramétrage de l’extension PHP LDAP dans Dolibarr

Dolibarr utilise l’extension ldap native de PHP. Voici les réglages qui améliorent nettement la latence :

Directive PHP Valeur conseillée Pourquoi
ldap.max_search_depth -1 (illimité) ou 5 si nécessaire Limite inutile de profondeur qui ajoutait des filters lourds
ldap_opt_timeout 0.5 s (500 ms) Timeout court évite les blocages sur les serveurs saturés
ldap_connection_pages TRUE + ldap_control_paged_search Recherche paginée -> pas de charge serveur excessive
session.gc_maxlifetime 1440 s (ou plus) Prolonge la durée de vie des sessions LDAP
default_ldap_ssl 0 (désactiver SSL si interne) ou 1 avec TLS Réduit le handshake inutile lorsqu’il n’y a pas besoin de chiffrement à l’interne
memory_limit 512M (ou selon le serveur) Assure que les objets LDAP ne dépassent pas la limite mémoire

Tip : Dans le fichier de configuration inc/conf/ldap.conf (ou dans dolibarr.conf), on fixe les paramètres de connexion au serveur. Exemple :

$_CONFIG["ldap"]["host"] = "ldap.mondomaine.loc";
$_CONFIG["ldap"]["port"] = 389;
$_CONFIG["ldap"]["base_dn"] = "dc=mondomaine,dc=loc";
$_CONFIG["ldap"]["bind_dn"] = "cn=bind,ou=service,dc=mondomaine,dc=loc";
$_CONFIG["ldap"]["bind_pw"] = "********";
$_CONFIG["ldap"]["user_filter"] = "(&(objectClass=person)(uid=%s))";
$_CONFIG["ldap"]["group_filter"] = "(&(objectClass=groupOfUniqueNames)(uniqueMember=%s))";
$_CONFIG["ldap"]["member_attr"] = "uniqueMember";
$_CONFIG["ldap"]["cache_ttl"] = 300; // 5 min


4. Stratégies pour minimiser le nombre d’appels LDAP

Situation Action Résultat
Authentification (login) Une seule connexion à l’utilisateur, un seul bind + une seule recherche par login. Évite les reconnexions parallèles.
Vérification de groupe Binding dès le login, stockage du DN du groupe dans un cache session (ex. $_SESSION["ldap_group"]). Pas de recherche supplémentaire lors de chaque action (édition, facturation).
Chargement des listes (ex. listes déroulantes des utilisateurs) Requête unique récupérant les utilisateurs du groupe “Dolibarr Users” avec ldap_search_attribute() en arbre restreint (ou=people,ou=dolibarr). Une seule requête, réponse paginée, pas de blocage réseau.
Synchronisation nocturne Scraping complet (déjà mentionné) → exécution en dehors des heures de pic via cron (0 2 * * * php sync_ldap.php). Aucun impact pendant la journée.

4.1 Utilisation du cache de session

« `php$sessionKey = "ldapuser{$user}";
if (!isset($_SESSION[$sessionKey])) {
// appel ldap_search() → récupération sapé par filter
$_SESSION[$sessionKey] = $userData;
}
$userData = $_SESSION[$sessionKey];


> **Astuce** : le TTL du cache doit être comparable à la fréquence de modification du répertoire (ex. 5 min si les groupes sont mis à jour plusieurs fois dans la journée).
---
## 5. Optimisation du **filtre de recherche** ### 5.1 Filtre le plus précis possible
```php
// Mauvaise pratique – recherche tout le domaine
$filter = "(objectClass=person)";
// Bonne pratique – filtrer par attribut uid ou login déjà connu
$filter = "(uid={$login})";

Le filtre doit porter sur un attribut indexé (généralement uid, samAccountName, mail). Sinon le serveur effectue une opération de parcours complet qui devient rapidement un goulet d’étranglement.

5.2 Utiliser des filtres de groupe plutôt que des requêtes member

« `php// Recherche d’un utilisateur dans un groupe
$filter = "(&(objectClass=groupOfUniqueNames)(uniqueMember=CN=user,ou=people,ou=dolibarr))";


Une requête fille sur le groupe (qui est généralement plus petite) est plus rapide que d’interroger le **DN de chaque membre** d’un groupe volumineux.
---
## 6. Gestion des erreurs et de la résilience
| Problème fréquent | Solution recommandée |
|-------------------|----------------------|
| **Timeout LDAP** (`ldap_search` dépasse le `ldap_opt_timeout`) | Réduisez le **page size** et le **page time limit** à 2 ou 3 secondes. Mettez en place un **fallback** vers un utilisateur « guest » avec des droits limités lorsqu’un timeout survient. |
| **Erreur d’authentification** (`Invalid credentials`) | Capturez `ldap_error()` et logguez le trafic (`/var/log/dolibarr/ldap.log`). Ne pas exposer le détail au client – bloquez immédiatement après 3 tentatives. |
| **Serviteur LDAP hors service** | Activez **le circuit breaker** : si 5 échecs consécutifs, bascule automatiquement sur un **serveur secondaire** (ou sur le mode “offline”). Utilisez Docker‑Compose ou un Load‑Balancer simple (HAProxy). |
| **Équilibrage de charge entre plusieurs serveurs LDAP** | **Round‑robin DNS** + **sticky sessions** si le serveur a besoin d’un état local (ex. cache persistant). |
---
## 7. Tests de charge (‑​benchmark)
### 7.1 Outils de mesure
* **ApacheBench** (`ab`) ou **JMeter** pour reproduire les scénarios d’or.
* **`ldapsearch`** en ligne de commande pour mesurer le temps brut de chaque requête.
* **Grafana + Prometheus** : exportez `ldap_query_duration_seconds` (via exporter python) pour visualiser la latence moyenne.
### 7.2 Indicateurs clés
| KPI | Valeur cible (exemple) | Comment améliorer |
|-----|------------------------|-------------------|
| Temps moyen d’authentification | < 150 ms | Cache, filtre indexé, réduction du nombre de queries |
| Temps moyen de récupération du groupe | < 300 ms | Pagination, index `member` |
| Taux d’erreur LDAP | < 0,1 % des requêtes | Augmenter le timeout, adequate TLS, dédié ldap‑replica |
| Utilisation CPU LDAP | < 30 % sur serveur dédié | Scaling vertical (CPU), réplication, connexion keep‑alive |
---
## 8. Exemple de **profil de configuration** pour une PME à 200 utilisateurs
```php
// inc/ldap.php – fichier de connexion unique
$ldapHost = "ldap.int.mondomaine.loc";
$ldapPort = 636; // TLS$ldapBase = "ou=People,dc=int,dc=mondomaine,dc=loc";
$ldapBindDn = "cn=dolibarr,ou=service,dc=int,dc=mondomaine,dc=loc";
$ldapBindPwd = "********";
$_LDAP = [
"host" => $ldapHost,
"port" => $ldapPort,
"base_dn" => $ldapBase,
"bind_dn" => $ldapBindDn,
"bind_pw" => $ldapBindPwd,
"user_filter" => "(uid=%s)",
"group_filter" => "(|(cn=Dolibarr Users)(cn=Dolibarr Power Users))",
"member_attr" => "uniqueMember",
"cache_ttl" => 180, // 3 min
"timeout" => 0.5, // seconds "use_ssl" => true,
];
// Initialisation (dolibarr/lib/functions/ldap.inc.php)
$ldap = ldap_connect($ldap["host"], $ldap["port"]);
if ($ldap["use_ssl"]) ldap_set_option($ldap, LDAP_OPT_PROTOCOL, LDAP_PROTOCOL_TLS);
ldap_set_option($ldap, LDAP_OPT_CONNECT_AS_BIND, 1);
ldap_bind($ldap, $ldap["bind_dn"], $ldap["bind_pw"]);
// Fonction de vériffunction ldap_check_user($login) {
global $_LDAP;
$filter = sprintf($_LDAP["user_filter"], $login);
$result = ldap_search($_LDAP["host"], $_LDAP["base_dn"], $filter);
$entries = ldap_get_entries($result, $filter);
ldap_free_result($result);
return $entries[0] ?? null;
}
// Cache sessionfunction get_user_data($login) {
$cacheKey = "ldap_user_$login";
if (!isset($_SESSION[$cacheKey])) {
$userEntry = ldap_check_user($login);
if ($userEntry) {
$_SESSION[$cacheKey] = $userEntry;
}
}
return $_SESSION[$cacheKey] ?? null;
}

Remarque : Ce snippet ne doit être inclus qu’une seule fois (dans ldap.inc.php) et faire l’objet d’un test de charge avant tout déploiement en production.


9. Bonnes pratiques de sécurité

  1. TLS obligatoire – même en intranet, le trafic LDAP doit être chiffré (LDAPS).
  2. Principes du moindre privilège – créez un account dédié (cn=dolibarr,ou=service) avec seulement les permissions search et read sur les OU qui contiennent les utilisateurs.
  3. Limitation du taux d’opération – configurez le serveur LDAP (slapd.conf ou administration LDAP) pour limiter le nombre de requêtes par seconde (ex. limitemax 500).
  4. Audit – activez le access log sur le serveur LDAP pour tracer chaque binding. 5. Spin‑lock du mot de passe – si vous avez besoin de réinitialiser un mot de passe LDAP depuis Dolibarr, faites-le via le AD Self Service ou via un endpoint interne, jamais en exposant directement le bind DN dans le client.


10. Check‑list de déploiement ultra‑performant | ✅ | Action |

|—-|——–|
| 1 | Installer OpenLDAP + TLS sur un serveur dédié (CPU ≥ 4 cœurs, RAM ≥ 8 Go). |
| 2 | Indexer les attributs uid, mail, cn dans le schéma LDAP. |
| 3 | Configurer les paramètres PHP (ldap_opt_timeout, ldap_search_page_size). |
| 4 | Implémenter le cache de session avec TTL configurable. |
| 5 | Créer un filtre unique par appel (Binding + recherche). |
| 6 | Mettre en place un cron de synchronisation hors‑pointe (2 h du matin). |
| 7 | Provisionner un serveur de secours (read‑only replica). |
| 8 | Exécuter des scénarios de charge (JMeter) avec 200, 500 et 1000 utilisateurs simultanés. |
| 9 | Vérifier les logs LDAP pour les temps d’attente > 250 ms. |
|10| Documenter le processus de récupération en cas de perte du serveur LDAP (basculement). |


11. Conclusion

Intégrer LDAP dans Dolibarr est une démarche qui centralise l’identité tout en apportant une meilleure gouvernance des utilisateurs.
Le principal goulot d’étranglement réside souvent dans le nombre d’appels réseau et la complexité des filtres. En suivant les recommandations ci‑dessus :

  • Un seul bind par session,
  • Cache (session + serveur LDAP) pour les requêtes répétées,
  • Filtres d’indexation précise,
  • Configuration de timeout adaptée,
  • Synchronisation hors‑pointe, on obtient des temps de réponse inférieurs à 150 ms même sous une charge de plusieurs centaines d’utilisateurs simultanés.

Ces bonnes pratiques, associées à une surveillance continue (Prometheus/Grafana) et à une architecture résiliente (replica, bascule), garantissent que votre déploiement Dolibarr‑LDAP restera rapide, sécurisé et maintenable sur le long terme.


Vous avez un projet concret ? N’hésitez pas à me communiquer les spécificités de votre environnement (nombre d’utilisateurs, version de Dolibarr, serveur LDAP envisagé) afin que je puisse vous proposer un plan d’action détaillé ou un script de benchmark adapté.

Bonne intégration !

Cet article a été rédigé à partir de retours d’expérience sur des déploiements de Dolibarr 23+ et OpenLDAP 2.5, testés sur des serveurs Ubuntu 22.04 et Windows Server 2022.

Publications similaires