OpenLDAP est à ce jour l’une des implémentations Open Source de serveur LDAP les plus utilisées, abouties et performantes.
Né en 1998, il a aujourd’hui évolué et intègre de nombreuses fonctionnalités, telles que la réplication, la prise en charge de TLS, le hachage de mots de passe, …
L’une de ses forces est son extensibilité :
- via des plugins de stockage : les backends
- via des plugins ajoutant des fonctionnalités : les overlays
Même si l’objectif premier des backends OpenLDAP est de définir et configurer le stockage physique de l’annuaire, il existe aussi des backends qui permettent de le transformer en mandataire (proxy) LDAP.
C’est par exemple le cas des backends :
- ldap : mandataire LDAP (proxy)
- meta : méta-annuaire, agrégeant les données de plusieurs sources LDAP
Ce dernier backend est particulièrement intéressant car il propose de fusionner des données venant de plusieurs annuaires afin d’en présenter une vue unifiée à des applications qui ne peuvent pas interroger plusieurs instances.
Cette fonctionnalité induit également une complexité : comment homogénéiser les données d’annuaires hétéroclites ? Comment concilier des schémas d’annuaires différents, voire incompatibles comme par exemple Active Directory, Oracle Directory Server, ITDS ? La réponse à ces questions passe par la configuration des modules ldap et meta, et par l’adjonction de fonctionnalités avec l’overlay rwm.
Méta-annuaire simple
Dans ce cas de figure, les données de l’annuaire 1 sont agrégées virtuellement dans une branche ou=ldap1, et ou=ldap2 pour l’annuaire 2.
Le critère d’aiguillage est donc assez simple, et il n’y a aucune ambiguïté.
Une telle configuration peut être réalisée avec le fichier suivant :
include /usr/local/openldap/etc/openldap/schema/core.schema
include /usr/local/openldap/etc/openldap/schema/cosine.schema
include /usr/local/openldap/etc/openldap/schema/inetorgperson.schema
logLevel 256
pidfile slapd.pid
argsfile slapd.args
moduleload back_meta.la
moduleload back_ldap.la
access to * by * write
# Database
database meta
suffix "dc=meta,dc=com"
rootdn "cn=Manager,dc=meta,dc=com"
rootpw secret
# LDAP 1
uri ldap://ldap1.example.com/ou=ldap1,dc=meta,dc=com
suffixmassage "ou=ldap1,dc=meta,dc=com" "dc=ldap1,dc=com"
# LDAP 2
uri ldap://ldap2.example.com/ou=ldap2,dc=meta,dc=com
suffixmassage "ou=ldap2,dc=meta,dc=com" "dc=ldap2,dc=com"
Ici, le paramètre important est suffixmassage. Il permet d’intercepter les requêtes sur l’annuaire meta ayant pour suffixe ou=ldap1,dc=meta,dc=com
, et de le réécrire en dc=ldap1,dc=com
. De plus, il réalise également l’opération inverse : les réponses venant des annuaires ldap1 et ldap2 sont aussi réécrites, cette fois de dc=ldap1,dc=com
en ou=ldap1,dc=meta,dc=com
.
Pour plus d’informations sur les paramètres meta, n’hésitez pas à consulter sa page de manuel.
Notez aussi la présence de la règle d’accès (ACL) access to * by * write
. Souvent, on considère que les règles d’accès sont déjà gérées côté backend, et il suffit alors de définir une ACL très large pour autoriser tous les accès en amont.
Enfin, dans ce scénario simple, on considère que le schéma (ie les classes d’objet et attributs) sont identiques sur tous les annuaires. Bien sûr c’est rarement le cas, et il faut alors choisir entre deux stratégies :
- importer les schémas de tous les annuaires backends dans l’annuaire meta
- réécrire à la volée les attributs des annuaires backends afin d’homogénéiser le schéma
C’est ce dernier point qui sera abordé par la suite.
Meta-annuaire avec réécriture du schéma
Ce cas pratique illustre un annuaire meta qui virtualise des annuaires AD.
Pour la simplicité de l’exemple, le seul objet que nous réécrivons ici est l’utilisateur.
include /usr/local/openldap/etc/openldap/schema/core.schema
include /usr/local/openldap/etc/openldap/schema/cosine.schema
include /usr/local/openldap/etc/openldap/schema/inetorgperson.schema
logLevel 256
pidfile slapd.pid
argsfile slapd.args
moduleload back_meta.la
moduleload back_ldap.la
access to * by * write
# Database
database meta
suffix "dc=meta,dc=com"
rootdn "cn=Manager,dc=meta,dc=com"
rootpw secret
# LDAP 1
uri ldap://ldap1.example.com/ou=ldap1,dc=meta,dc=com
suffixmassage "ou=ldap1,dc=meta,dc=com" "dc=LDAP1,dc=COM"
# LDAP 2
uri ldap://ldap2.example.com/ou=ldap2,dc=meta,dc=com
suffixmassage "ou=ldap2,dc=meta,dc=com" "dc=LDAP2,DC=COM"
map objectclass user inetOrgPerson
map attribute uid sAMAccountName
map attribute employeeNumber employeeID
map attribute o userPrincipalName
map attribute cn cn
map attribute sn sn
map attribute givenName givenName
map attribute displayName displayName
map attribute telephoneNumber telephoneNumber
map attribute mail mail
map attribute *
Le début de la configuration est identique à la précédente.
Chaque nom d’attribut est réécrit de sa valeur locale à sa valeur distante dans le sens de la requête, et inversement au retour.
La dernière directive map attribute *
permet de retirer tout attribut qui n’est pas listé.
Voir la page de manuel de rwm pour plus de détail sur la réécriture d’attribut et de classe d’objet.
Meta-annuaire avec aiguillage suivant le bindDN
Même si le moteur meta permet de satisfaire la plupart des besoins des administrateurs, il peut arriver qu’une problématique plus pointue dépasse ses capacités.
C’est le cas pour ce besoin : aiguiller le trafic sur un annuaire ou un autre suivant le DN utilisé pour l’authentification (bindDN).
La règle de gestion est la suivante : les personnes qui se connectent avec un uid commençant par srv1 doivent être aiguillées sur le serveur LDAP1 et toutes les autres doivent être aiguillées sur le serveur LDAP2.
La difficulté est ici que même si la requête d’authentification est la première, il faut que les requêtes suivantes (recherches, ajouts, modifications,…) soient aiguillées de la même façon, et donc que le serveur se souvienne de la personne qui s’est connectée au départ.
Pour traîter ce cas, le backend meta ne suffit pas, et il faut recourir à l’overlay de réécriture nommé rwm
.
Note : Le backend meta dispose lui-aussi de son propre moteur de réécriture, qu’il est possible d’invoquer. Toutefois, il est inapproprié ici car les règles de réécriture sont appliquées par cible (comprendre : par serveur distant). Dans notre cas, il faut que la règle de réécriture soit décidée en amont afin de sélectionner le serveur cible.
Pour pouvoir traiter cette problématique, nous allons donc utiliser l’overlay rwm déclaré en amont de tout backend :
include /usr/local/openldap/etc/openldap/schema/core.schema
include /usr/local/openldap/etc/openldap/schema/cosine.schema
include /usr/local/openldap/etc/openldap/schema/inetorgperson.schema
logLevel 256
pidfile slapd.pid
argsfile slapd.args
moduleload back_meta.la
moduleload back_ldap.la
moduleload rwm.la
access to * by * write
overlay rwm
rewriteEngine on
# In the context of a BIND operation
rewriteContext bindDN
# Store user who has logged in an internal variable named binddn (for other LDAP operations)
rwm-rewriteRule ".+" "${&&binddn($0)}$0" ":"
# If the LDAP operation is a bind, directly rewrite the binddn
# If binddn starts by or srv1, rewrite suffix to directory1
rewriteRule "^uid=(srv1[^,]+)(.*),dc=domain,dc=com$" "uid=$1$2,dc=directory1,dc=domain,dc=com" ":@I"
# Else, rewrite suffix to directory2
rewriteRule "^uid=([^,]+)(.*),dc=domain,dc=com$" "uid=$1$2,dc=directory2,dc=domain,dc=com" ":@I"
# For SEARCH LDAP operations
rewriteContext searchDN
# Prefix the string to search by the binddn who has connected previously
rewriteRule "(.*)" "${**binddn}<>$1" ":I"
# If binddn starts by srv1, remove binddn prefix from string and rewrite suffix to directory1
rewriteRule "^uid=srv1[^,]+,[^<]+<>(.*)dc=domain,dc=com(.*)$" "$1dc=directory1,dc=domain,dc=com$2" ":@I"
# If binddn not found or does not start by srv1, remove binddn prefix from string and rewrite suffix to directory2
rewriteRule "^.*<>(.*)dc=domain,dc=com(.*)$" "$1dc=directory2,dc=domain,dc=com$2" ":@I"
rewriteRule "^.*<>(.*)$" "$1" ":@I"
# For SEARCH entry operation during the response
rewriteContext searchEntryDN
rewriteRule "^(.*)dc=directory.,dc=domain,dc=com$" "$1dc=domain,dc=com" ":@I"
database meta
suffix "dc=domain,dc=com"
uri "ldap://127.0.0.1:3390/dc=directory1,dc=domain,dc=com"
suffixmassage "dc=directory1,dc=domain,dc=com" "dc=ldap1,dc=com"
uri "ldap://127.0.0.1:3391/dc=directory2,dc=domain,dc=com"
suffixmassage "dc=directory2,dc=domain,dc=com" "dc=ldap2,dc=com"
Après la déclaration de l’overlay rwm, la configuration se présente en 3 paragraphes :
- Le premier ne s’applique que dans le cas d’une authentification. Il commence par stocker le bindDN dans une variable pour usage ultérieur. Il réécrit ensuite le suffixe : les personnes dont l’uid commence par srv1 sont aiguillées sur l’annuaire 1 et les autres sur l’annuaire 2.
- Le second s’applique dans le cas d’une recherche LDAP. La base de recherche est modifiée pour y inclure le bindDN de la personne qui s’est connectée plus tôt. Puis, si la personne a un uid commençant par srv1, la base de recherche est réécrite pour correspondre à celle de l’annuaire 1, sinon elle est réécrite pour correspondre à celle de l’annuaire 2.
- Le dernier permet de récupérer les résultats venant des annuaires 1 et 2, et de les réécrire pour que le suffixe corresponde à celui de l’annuaire meta.
Bien que peu attrayante au premier abord, la syntaxe d’une règle de réécriture (rewriteRule) a toujours la même structure :
rewriteRule "motif" "remplacement" "actions"
- Le motif est une expression régulière qui sera vérifiée par rapport au contexte en cours. Dans le cas de
rewriteContext bindDN
, il s’agit du DN de connexion. Dans le cas derewriteContext searchDN
, il s’agit de la base de recherche. - Le remplacement est ce par quoi le motif va être remplacé. L’utilisation des parenthèses dans le motif permet de récupérer les valeurs correspondantes à l’aide de variables positionnelles : $1, $2,…
- Les actions sont des paramètres de contrôles. Les plus utilisées sont :
:
pour n’appliquer la règle qu’une fois (sinon c’est récursif),@
pour appliquer la règle en cours et pas les suivantes si elle correspond,I
pour ignorer les erreurs.
Pour plus d’informations sur les paramètres de rwm, n’hésitez pas à consulter sa page de manuel
Conclusion
Nous avons vu qu’OpenLDAP configuré en tant qu’annuaire meta est très souple et modulable et permet de répondre à un grand nombre de problématiques.
Si vous souhaitez aller plus loin, n’hésitez pas à jeter un oeil au site LTB Project, qui propose des paquets pour OpenLDAP, divers outils et de la documentation.