Table des matières
Introduction à LDAP
Cet article donne les bases de LDAP et des exemples d'utilisation courante. Il peut paraître dense, mais il s'agit principalement beaucoup de vocabulaire et d'habitudes de notations, les notions étant assez peu complexes.
À quoi sert LDAP ?
En un mot : un des usages principaux est de fournir une base centralisée d'authentification pour des services multiples.
LDAP est un système de base de données pouvant stocker des utilisateurs (avec identifiant, nom, mot de passe chiffré, interpréteur de commandes, etc.), des groupes ou même des comptes RADIUS (avec IP et VLAN à attribuer, par exemple). LDAP n'est en réalité pas limité à des comptes utilisateurs, mais nous nous concentrons ici sur cet aspect.
Beaucoup de logiciels savent s'interfacer avec LDAP pour authentifier des utilisateurs : OpenVPN, RADIUS, DokuWiki, Owncloud, Prosody, Redmine, l'authentification de comptes UNIX, etc.
Une base LDAP permettra donc à un utilisateur d'avoir le même identifiant et mot de passe pour tous ces services. Du côté de l'administrateur, cela permet de centraliser la gestion des accès plutôt que d'avoir une base par service.
Comment se passe une authentification ?
Pour qu'un service (OpenVPN, Dokuwiki, etc.) utilise LDAP pour authentifier des tentatives de connexions, il faut lui indiquer de transmettre ces tentatives à un serveur LDAP. Ce dernier essaiera alors d'authentifier l'utilisateur avec l'identifiant et mot de passe transmis par le service, et répondra si oui ou non l'authentification a réussi.
Le service qui transmet la requête à LDAP peut recevoir des attributs supplémentaires. Par exemple, RADIUS pourra demander l'adresse IP à attribuer, et un système UNIX recevra l'interpréteur de commande à utiliser, le répertoire de l'utilisateur, ses groupes, etc.
Toutes ces données devront avoir été entrées préalablement dans la base LDAP par un administrateur.
À quoi ressemblent des données dans LDAP ?
Oubliez MySQL et consors. C'est par contre similaire à Active Directory. La notion d'objet sera familière à ceux ayant fait de la programmation orientée objet.
Structure arborescente classique
Une base LDAP est un ensemble d'entrées formant une structure d'arbre classique : il y a une entrée de base (la racine) et des sous-entrées, chacune pouvant contenir d'autres sous-entrées, et ainsi de suite. Rien d'original, structurellement cela ressemble à une arborescence de fichiers.
Il n'y a par contre aucune notion de répertoire. Dans LDAP, chaque entrée peut indifféremment avoir ou ne pas avoir de sous-entrées.
Enfin, l'idée de LDAP est de contenir des entrées représentant de façon cohérente des objets réels : comptes utilisateurs, groupes, etc.
Une entrée est un objet uniquement identifiable
Comme pour une arborescence de fichiers, le chemin vers chaque entrée est décrit de façon unique en partant de la racine.
Dans le monde LDAP, ce chemin s'appelle le Distinguished Name (DN). Deux entrées ne peuvent avoir le même DN.
Une entrée LDAP est aussi appelée objet.
Chaque entrée est un ensemble d'attributs
Contrairement à un fichier, une entrée LDAP n'est pas un nom et un contenu quelconque, mais un ensemble d'attributs qui, ensemble, représentent un objet cohérent.
Un attribut, c'est un nom et une valeur. Par exemple, pour une entrée représentant un compte POSIX, l'attribut pour le répertoire de l'utilisateur est homeDirectory
et une valeur est /home/luc
. L'attribut contenant le login est uid
, qui pourra valoir luc
.
Une entrée peut avoir un attribut userPassword
contenant un mot de passe chiffré. C'est grâce à cela que l'utilisateur identifié par son DN pourra s'authentifier.
Certains attributs peuvent être multivalués.
Voir ici pour une explication des attributs les plus courants.
Le DN d'une entrée se construit avec les attributs
Comme une entrée n'est ni plus ni moins qu'un ensemble d'attributs, un attribut accompagné de sa valeur doit être choisi pour figurer dans le DN de ladite entrée. Le DN d'une entrée est donc toujours composé d'une suite d'attribut-valeurs écrits sous la forme nom_attribut=valeur
, partant de la racine pour arriver à l'entrée voulue.
Exemple : dans une base LDAP où l'entrée de base, ou DN de base, est dc=faimaison,dc=net
, un DN pour notre ami Luc sera :
uid=luc,ou=utilisateurs,dc=faimaison,dc=net
La notation ne contient pas de /
comme dans un système de fichiers. La descente dans l'arborescence se fait en lisant le DN de droite à gauche, la virgule faisant office de séparateur : dc=net
, puis dc=faimaison
puis ou=utilisateurs
puis uid=luc
.
Le DN de base dc=faimaison,dc=net
enchaine deux entrées contenant l'attribut dc
, ce qui représente le nom de domaine faimaison.net
. En théorie, le DN dc=faimaison,dc=net
est composé de deux entrées, mais en pratique c'est juste la racine de notre arborescence et nous ne le décomposons jamais : c'est le suffixe invariable de toute entrée dans notre base.
Un DN de base alternatif qui ferait également sens pour représenter FAImaison serait :
o=FAImaison,l=Nantes,c=FR
Le DN de Luc serait alors :
uid=luc,ou=utilisateurs,o=FAImaison,l=Nantes,c=FR
Le choix du DN de base pour une base de donnée LDAP est totalement arbitraire. Les deux exemples ci-dessus représentent simplement un choix courant et sensé.
Les attributs d'un objet doivent avoir un sens
Tout est strictement standardisé : un objet représentant un compte utilisateur POSIX doit par exemple avoir un homeDirectory
. À l'inverse, un objet représentant un groupe ne peut pas avoir cet attribut : cela n'a pas de sens et n'est donc pas permis.
En deux mots : on ne peut pas insérer n'importe quel attribut dans n'importe quelle entrée LDAP.
Compte et groupe POSIX, dans une base LDAP, sont des classes d'objet. Une classe LDAP définit les seuls attributs autorisés pour les objets de cette classe. Certains attributs sont obligatoires, d'autres sont optionnels.
Pour stocker les données dont vous avez besoin, vous devrez donc utiliser les classes appropriées. Les classes auxquelles appartiennent une entrée sont stockées dans l'attribut multivalué objectClass
de l'entrée.
Il y a des centaines de classes LDAP prédéfinies. Pour nous, les plus communes sont :
account
est la classe la plus basique pour la notion de compte, son seul attribut obligatoire étantuid
;posixAccount
représente un compte POSIX, avec comme attributs obligatoirescn
(common name),uid
,uidNumber
,gidNumber
ethomeDirectory
;posixGroup
représente un group POSIX, avec deux attributs obligatoires (cn
etgidNumber
) et comme attribut optionnel multivaluémemberUid
qui permet de lister les membres du groupe ;inetOrgPerson
représente une personne (pas un compte POSIX) avec comme seuls attributs obligatoirescn
etsn
(surname, nom de famille) mais une foule d'attributs optionnels commemail
,homePhone
, etc. ;groupOfNames
représente un group de personnes, aveccn
comme attribut obligatoire etmember
comme attribut obligatoire multivalué permettant de définir les membres du groupe ;organizationalUnit
, au seul attribut obligatoireou
, est souvent utilisée pour regrouper des sous-entrées de même nature (des utilisateurs, des groupes, des pommes …).
Une entrée peut appartenir à plusieurs classes si elles sont compatibles entre elles.
Exemples
Voici à quoi pourrait ressembler l'objet LDAP de Luc :
dn: uid=luc,ou=utilisateurs,dc=faimaison,dc=net objectClass: top objectClass: inetOrgPerson uid: luc cn: Monsieur Luc sn: Luc mail: luc@globalnetcorporation.fail userPassword: {SSHA}Kasdedl2N3xbfbhsjwReyEw5GVKjkNZz
L'entrée est correcte car les attributs cn
et sn
, obligatoires pour inetOrgPerson
, ont une valeur. Les autres attributs présents sont facultatifs pour inetOrgPerson
.
Si Luc doit également avoir un compte POSIX, il est impératif d'ajouter la classe posixAccount
à l'entrée, car c'est elle qui autorisera de nouveaux attributs pour modéliser son compte. Ce qui peut donner :
dn: uid=luc,ou=utilisateurs,dc=faimaison,dc=net objectClass: top objectClass: posixAccount objectClass: inetOrgPerson uid: luc cn: Monsieur Luc sn: Luc mail: luc@globalnetcorporation.fail homeDirectory: /home/luc uidNumber: 2713 gidNumber: 2713 loginShell: /bin/sh userPassword: {SSHA}Kasdedl2N3xbfbhsjwReyEw5GVKjkNZz
L'authentification en plus concret
Maintenant qu'on sait ce qu'est un attribut et un DN, on peut comprendre comment fonctionne concrètement une procédure d'authentification.
Au service qui devra utiliser LDAP pour authentifier les requêtes de connexions, on indique :
- l'hôte et le port où se trouve le serveur LDAP ;
- le DN de base sous lequel rechercher les entrées (exemple :
dc=faimaison,dc=net
) ; - un ou plusieurs attributs contenant ce qui est considéré comme un identifiant de connexion valide pour l'utilisateur (typiquement :
uid
).
Avec ceci, la procédure de connexion se passera comme suit :
- Luc rentre « luc » comme identifiant et « Pomme » comme mot de passe
- le service se connecte à LDAP et demande : « sous l'entrée de base
dc=faimaison,dc=net
, chercher une entrée dont l'attributuid
vaut « luc » » - LDAP trouve cette entrée et retourne au service le DN de l'entrée,
uid=luc,ou=utilisateurs,dc=faimaison,dc=net
- l'objet exact étant maintenant identifié, le service émet une requête d'authentification au serveur LDAP pour ce DN, accompagné du mot de passe
- LDAP vérifie la validité du mot de passe pour cette entrée et indique le résultat de la vérification au service
- le service autorise l'accès à Luc si son mot de passe est correct, affiche un message d'erreur sinon.
On trouve régulièrement des petites variantes autour de cette procédure :
- beaucoup de services acceptent un filtre de recherche à passer à LDAP, permettant d'exclure les objets ne respectant pas une condition donnée, par exemple :
(objectClass=inetOrgPerson)
pour ne rechercher que les entrée de typeinetOrgPerson
ou(!(loginShell=/bin/false))
pour exclure tout objet où l'attributloginShell
vaut/bin/false
; - on peut vouloir empêcher les recherches dans LDAP sans s'authentifier (voir étape 2 au-dessus), auquel cas le service lui-même devra s'authentifier avec un DN donné ;
- certains services peuvent ne pas faire de recherche du tout, et essayer directement d'authentifier avec LDAP en construisant le DN de l'utilisateur en insérant l'identifiant saisi dans un modèle de DN LDAP ;
- certains services sont très peu configurables et attendent des objets LDAP bien précis, typiquement l'authentification de comptes et groupes POSIX qui ne fonctionne qu'avec des objets
posixAccount
etposixGroup
; - beaucoup de services ne se contentent pas d'authentifier, mais utilisent aussi LDAP pour récupérer d'autres données sur l'utilisateur : c'est ici aussi le cas de l'authentification de comptes POSIX, où l'interpréteur de commande, le répertoire de l'utilisateur, son numéro d'UID et son groupe principal sont récupérés des attributs respectifs
loginShell
,homeDirectory
,uidNumber
etgidNumber
.
Passons à la pratique
On ne documente ici ni l'installation ni la configuration d'OpenLDAP (le principal serveur LDAP sous GNU/Linux) mais on donne plutôt quelques exemples d'utilisation d'un serveur déjà fonctionnel : consultation, ajout et suppression d'entrées.
L'installation et la configuration d'OpenLDAP sont documentées sur internet dans des pages plus ou moins à jour. Une méthode facile et rapide consiste à utiliser le rôle openldap
de Caisleàn, un ensemble de recettes Ansible visant à faciliter l'autohébergement.
Outil
On utilise Apache Directory Studio (ADS), un logiciel graphique facilitant grandement les interactions avec un serveur LDAP. Ce logiciel connaît les classes standard et nous avertira si des attributs sont manquants ou en trop. Il nous épargnera aussi pas mal de fautes de frappes faciles à faire si on se paluche tout à la main avec les outils en terminal de base. Il nous laisse aussi le temps d'aller à la machine à café pendant qu'il démarre.
Des alternatives sont : ldapvi, GQ et les outils de base trouvés dans le paquet ldap-utils
de la plupart des distributions GNU/Linux.
Se connecter au serveur LDAP
Demander à l'administrateur du serveur LDAP un nom d'hôte et un port auquel se connecter. Il n'est pas rare qu'un tunnel SSH doive être utilisé, cela fournissant du chiffrement et une forme d'authentification, beaucoup de serveurs LDAP n'ayant pas TLS configuré et autorisant un accès en lecture de la base de donnée sans authentification.
Votre identifiant de connexion au serveur sera un DN, qui peut correspondre à votre compte personnel (comme uid=luc,ou=utilisateurs,dc=faimaison,dc=net
) ou au compte administrateur du serveur (par exemple, cn=admin,dc=faimaison,dc=net
).
Dans ADS, utiliser l'entrée Nouvelle connexion dans le menu LDAP. Spécifiez le nom d'hôte, le port et le type de chiffrement. Si vous utilisez un tunnel SSH, l'hôte sera probablement localhost
, le port dépendra de votre commande SSH et ADS n'utilisera pas de chiffrement (puisqu'il est assuré par le tunnel SSH).