Encore plus loin avec OpenLDAP

Magazine
Marque
GNU/Linux Magazine
Numéro
112
|
Mois de parution
janvier 2009
|


Résumé
Le mois dernier, nous avons fait le tour d'un certain nombre d'opérations, de manipulations et de pratiques avec OpenLDAP. Comme précisé en fin d'article, LDAP est un vaste domaine et un livre de recettes concernant le sujet pourrait grossir sans fin jusqu'à dépasser la taille de plusieurs magazines. La preuve, il reste encore des choses à dire ce mois-ci pour compléter ce qui a déjà été détaillé.

Body

1. Mise en place de vues utilisateur

Beaucoup de clients LDAP sont très restrictifs sur la façon dont ils peuvent interroger un annuaire. En particulier, ceux des systèmes restreints embarqués sur des équipements réseau, sur des imprimantes, etc... Un Call Manager Cisco, par exemple, permet de synchroniser sa propre base d´utilisateur sur un annuaire LDAP, mais avec des contraintes assez pénibles. Comment donc gérer l´intégration de ces matériels, sans pour autant réorganiser la structure de l´annuaire pour se plier à leurs contraintes ?

Une solution consiste à déporter le travail de manipulation des données vers le serveur, et à répondre différemment en fonction du client qui l´interroge : c´est le principe des vues, bien connu des utilisateurs de SGBD. Les recettes suivantes montrent comment réaliser ce genre de chose avec OpenLDAP, pour filtrer des entrées d´une part, pour échanger des attributs d´autre part.

1.1 Filtrage d´entrées

Le problème le plus courant est celui du client ne permettant pas de filtrer des entrées indésirables. Le Call Manager Cisco, par exemple, importe avidement tous les utilisateurs situés sous sa base de recherche, sans permettre de filtrer par exemple ceux qui sont dépourvus d´un numéro de téléphone. Il faut donc identifier ce client lorsqu´il interroge l´annuaire, puis effectuer ce filtrage à sa place.

Puisqu´il s´agit d´exclure des entrées de la réponse, les ACL sont parfaitement adaptées. Il suffit en effet d´interdire à ce client l´accès à ces entrées, en appliquant le filtre nécessaire pour les sélectionner, pour toute demande émanant de l´adresse IP ou de l´identifiant de ce client :

# filtrage des entrées visibles par le call manager aux seuls utilisateurs

# pourvus d'un numéro de téléphone

access to dn.subtree="ou=users,dc=domain,dc=tld" filter=(!(telephoneNumber=*))

by dn="cn=callmanager,ou=roles,dc=domain,dc=tld" none

by * break

# filtrage des entrées visibles par le photocopieur aux seuls membres

# des ressources humaines ou des affaires financières

access to dn.subtree="ou=users,dc=domain,dc=tld" filter=(!(|(ou=srh)(ou=saf)))

by anonymous peername.ip=192.168.0.1 none

by * break

Il est alors facile de vérifier la différence de résultats entre les deux requêtes, en fonction de l´identifiant utilisé :

[guillaume@oberkampf ~]$ ldapsearch -x -LLL objectClass=person telephoneNumber

dn: uid=foo,ou=users,dc=domain,dc=tld

telephoneNumber: 1234567890

dn: uid=bar,ou=users,dc=domain,dc=tld

[guillaume@oberkampf ~]$ ldapsearch -x -LLL -D cn=callmanager,ou=roles,dc=domain,dc=tld -w s3cr3t objectClass=person telephoneNumber

dn: uid=foo,ou=users,dc=domain,dc=tld

telephoneNumber: 1234567890

Attention lors de la mise en place de telles ACL à l´impact potentiel sur les autres accès. L´utilisation d´ACL strictement nominatives, comme détaillée dans la recette 2.3, est fortement conseillée pour limiter les risques.

Pour plus d´information, consultez :

- la page de manuel slapd.access(5) ;

- la page contrôle d´accès du Guide d´administration d´OpenLDAP.

1.2 Échange d´attributs

Un problème plus complexe se pose lorsque le client ne permet pas de choisir les attributs utilisés. Le Call Manager Cisco, toujours lui, impose l´utilisation de l´attribut telephoneNumber pour stocker le numéro de téléphone interne, alors que d´autres applications accèdent également à cet attribut, et s´attendent à y voir figuré le numéro externe. Il ne s´agit donc plus de filter cet attribut, mais d´utiliser en fait deux attributs différents, et à présenter l´un ou l´autre sous un même nom en fonction du client.

Les ACL ne suffisent plus. Il faut mettre en place un échange (mapping) d´attributs, au sein d´un contexte virtuel, à l´aide du module de stockage relay. L´utilisation de ce contexte virtuel suffira à identifier le client.

L´exemple suivant montre la mise en place d´un contexte virtuel ou=telephony,dc=domain,dc=tld, répliquant la branche utilisateur ou=users,dc=domain,dc=tld, et dans lequel l´attribut telephoneNumber original est remplacé par l´attribut homePhone. Le fait que ce contexte virtuel appartienne au suffixe dc=domain,dc=tld impose de déclarer la base virtuelle avant la base réelle dans la configuration de slapd :

# chargement du module

moduleload back_relay.la

# mise en place d'un contexte virtuel, dupliquant la branche utilisateur

database relay

suffix ou=telephony,dc=domain,dc=tld

overlay rwm

rwm-suffixmassage ou=users,dc=domain,dc=tld

# substitution de l'attribut telephoneNumber

rwm-map attribute telephoneNumber homePhone

rwm-map attribute telephoneNumber

Cette fois-ci, c´est le contexte de recherche utilisé qui fait la différence de résultats entre les deux requêtes :

[guillaume@oberkampf ~]$ ldapsearch -x -LLL -b ou=users,dc=domain,dc=tld objectClass=person telephoneNumber,homePhone

dn: uid=foo,ou=users,dc=domain,dc=tld

telephoneNumber: 1234567890

homePhone: 7890

[guillaume@oberkampf ~]$ ldapsearch -x -LLL -b ou=telephony,dc=domain,dc=tld objectClass=person telephoneNumber,homePhone

dn: uid=foo,ou=telephony,dc=domain,dc=tld

telephoneNumber: 7890

Cette stratégie permet également de mettre en œuvre le même filtrage des entrées à partir d´ACL que la précédente, en simplifiant l´identification du client, puisque le contexte dédié le fait implicitement. De plus, les ACL exotiques sont ainsi isolées des ACL de la base principale, réduisant les possibilités d´interactions non désirées :

access to dn.subtree="ou=telephony,dc=domain,dc=tld" filter=(!(telephoneNumber=*))

by * none

Attention également à masquer les attributs sensibles, c´est-à-dire notamment les mots de passe, dans cette nouvelle branche, sous peine d´ouvrir une faille de sécurité remarquable... Le plus simple est de ne garder dans le contexte virtuel que les attributs nécessaires, et d´ignorer les autres :

rwm-map attribute telephoneNumber homePhone

rwm-map attribute telephoneNumber

rwm-map attribute *

Néanmoins, un bug dans les versions 2.4.11 et antérieures empêche ceci de fonctionner correctement. Il est plus simple dans ce cas d´utiliser des ACL, en utilisant la syntaxe décrite dans la recette 2.2 pour ne pas avoir à énumérer un par un les attributs à protéger :

access to dn.subtree="ou=telephony,dc=domain,dc=tld"

attrs=@krb5KDCEntry,@krb5Principal,@sambaSamAccount,@posixAccount

by * none

Pour la petite histoire, si cette stratégie fonctionne parfaitement en ligne de commande, un problème de gestion du contrôle pagedResult dans le contexte virtuel avec la version actuelle d´OpenLDAP l´empêche de fonctionner avec le Call Manager Cisco qui sert d´exemple dans cette recette. Le problème est connu, et en attente de résolution (ITS #5724).

Pour plus d´information, consultez :

- la page de manuel slapd-relay(5) ;

- la page de manuel slapo-rwm(5) ;

- la page stockage du Guide d´administration d´OpenLDAP ;

- la page greffons du Guide d´administration d´OpenLDAP.

2. Cohérence des données

Les systèmes de gestion de bases de données relationnels proposent de façon standardisée de nombreux moyens d´assurer la cohérence des données qu´ils stockent. OpenLDAP propose également certains mécanismes assurant les mêmes fonctionnalités.

2.1 Intégrité référentielle

Dès lors qu´on stocke des références vers des données, il faut s´assurer que lorsque ces données changent, les références concernées changent également. C´est le problème de l´intégrité référentielle. Par exemple, les groupes contiennent des références vers les utilisateurs (dans le schéma RFC2307 bis), et il faut s´assurer que quand ces utilisateurs disparaissent, ou changent d´identifiant, les groupes soient également mis à jour.

OpenLDAP propose un contrôle de l´intégrité référentielle avec le greffon refint. Celui-ci assure qu´en cas de changement de DN ou d´effacement d´objet, tous les attributs pointant vers l´objet en question sont automatiquement mis à jour. La contrainte initiale peut se configurer ainsi :

# chargement des modules

moduleload refint.so

...

overlay refint

refint_attributes member

Les commandes suivantes montrent l´effet de l´effacement de l´utilisateur bar sur le groupe users, à savoir que cet utilisateur est effacé de la liste des membres de ce groupe :

[guillaume@oberkampf ~]$ ldapsearch -x -LLL cn=users

dn: cn=users,ou=groups,dc=domain,dc=tld

objectClass: posixGroup

objectClass: groupOfNames

cn: users

member: uid=bar,ou=users,dc=domain,dc=tld

member: uid=foo,ou=users,dc=domain,dc=tld

gidNumber: 5001

[guillaume@oberkampf ~]$ ldapdelete -x -D cn=admin,ou=roles,dc=domain,dc=tld -w secret uid=bar,ou=users,dc=domain,dc=tld

[guillaume@oberkampf ~]$ ldapsearch -x -LLL cn=users

dn: cn=users,ou=groups,dc=domain,dc=tld

objectClass: posixGroup

objectClass: groupOfNames

cn: users

member: uid=foo,ou=users,dc=domain,dc=tld

gidNumber: 5001

Et les commandes suivantes montrent l´effet du renommage de l´utilisateur bar en baz sur ce même groupe, à savoir que la liste des membres du groupe est mise à jour :

[guillaume@oberkampf ~]$ ldapmodrdn -x -D cn=admin,ou=roles,dc=domain,dc=tld -w secret uid=foo,ou=users,dc=domain,dc=tld uid=baz

[guillaume@oberkampf ~]$ ldapsearch -x -LLL cn=users

dn: cn=users,ou=groups,dc=domain,dc=tld

objectClass: posixGroup

objectClass: groupOfNames

cn: users

gidNumber: 5001

member: uid=baz,ou=users,dc=domain,dc=tld

Pour plus d´information, consultez :

- la page de manuel slapo-refint(5) ;

- la page greffon du Guide d´administration d´OpenLDAP.

2.2 Unicité des attributs

Un autre problème de validité classique concerne l´unicité de certaines données. Par exemple, chaque utilisateur doit posséder un identifiant alphanumérique (login) et numérique (uid) distinct. Dans un annuaire LDAP, l´attribut utilisé pour former le RDN d´un objet est déjà couvert par une telle contrainte. L´attribut uid de la classe posixAccount servant de RDN, il ne peut ainsi y avoir deux utilisateurs de même login dans une même branche LDAP. Si les utilisateurs sont répartis dans plusieurs branches, ceci n´est pas suffisant pour assurer le respect de la contrainte initiale. Et de toute façon, ceci ne couvre pas l´attribut uidNumber.

OpenLDAP propose une véritable contrainte d´unicité par le biais du greffon unique. Celui-ci permet d´assurer qu´au sein d´une portée donnée, certains attributs aient tous une valeur unique. La contrainte initiale peut donc se configurer ainsi :

# chargement des modules

moduleload unique.la

...

overlay unique

unique_base ou=users,dc=domain,dc=tld

unique_attributes uid uidNumber

Et toute tentative de modification d´attribut violant cette contrainte se traduit immédiatement par une erreur, comme le montrent les commandes suivantes :

[guillaume@oberkampf ~]$ ldapmodify -x -D cn=admin,ou=roles,dc=domain,dc=tld -w password <<EOF

> dn: uid=bar,ou=users,dc=domain,dc=tld

> changetype: modify

> replace: uidNumber

> gidNumber: 5000

ldap_add: Constraint violation (19)

additional info: some attributes not unique

Pour plus d´information, consultez :

- la page de manuel slapo-unique(5) ;

- la page greffon du Guide d´administration d´OpenLDAP.

3. Audit

Le serveur slapd gère des fichiers journaux (logs), au même titre que n´importe quel serveur. Néanmoins, ceux-ci sont principalement dédiés à la surveillance du fonctionnement du serveur lui-même, de façon à résoudre les éventuels problèmes. Le seul filtrage possible concerne les différents sous-systèmes (requêtes, connexions, filtres, etc...). De plus, à moins d´utiliser une version de syslog avancée, telle syslog-ng ou rsyslog, avec stockage dans une base de donnée relationnelle, ces journaux sont stockés dans des fichiers texte plats, ce qui rend leur exploitation peu aisée. Bref, ils sont peu adaptés à la production d´information synthétique de haut niveau.

3.1 Accès aux données

Le greffon accesslog permet d´enregistrer sous forme d´entrées dans une base LDAP l´ensemble des opérations effectuées sur les données d´une autre base. Elles peuvent ainsi être consultées par des requêtes LDAP ordinaire.

La configuration passe par la définition de la base dédiée avant celle à surveiller, puis à définir différents paramètres d´enregistrement. En particulier, la directive logops spécifie les différents types d´opérations à enregistrer, soit individuellement (ajout, effacement, comparaison, ...), soit par groupe (lecture, écriture, gestion de session). L´exemple de configuration ci-dessous enregistre toutes les modifications effectuées à la base dc=domain,dc=tld dans la base cn=log :

# chargement des modules

moduleload accesslog.la

...

# base d'audit

database bdb

suffix "cn=log"

rootdn "cn=root,cn=log"

directory /var/lib/ldap/log

...

# base principale

database bdb

suffix "dc=domain,dc=tld"

directory /var/lib/ldap/main

...

# enregistrement des modifications réussies

# conservation pendant une semaine

overlay accesslog

logdb cn=log

logops writes

logsuccess TRUE

logpurge 07+00:00 01+00:00

Voici l´entrée correspondant au changement du mot de passe d´un utilisateur uid=user,ou=users,dc=domain,dc=tld, effectuée par cn=pam,ou=roles,dc=domain,dc=tld, c´est-à-dire l´utilisateur système root à travers PAM en utilisant la configuration présentée à la recette 1.4. Les valeurs données sont les nouvelles valeurs, les anciennes n´étant enregistrées que si la directive logold est utilisée également.

# 20080310195005.000004Z, log

dn: reqStart=20080310195005.000004Z,cn=log

objectClass: auditModify

reqStart: 20080310195005.000004Z

reqEnd: 20080310195005.000005Z

reqType: modify

reqSession: 2

reqAuthzID: cn=pam,ou=roles,dc=domain,dc=tld

reqDN: uid=user,ou=users,dc=domain,dc=tld

reqResult: 0

reqMod: userPassword:= {MD5}9x2+UmKKP4OnerSUgXUlxg==

reqMod: pwdChangedTime:= 20080310195005Z

reqMod: pwdFailureTime:-

reqMod: entryCSN:= 20080310195005Z#000000#00#000000

reqMod: modifiersName:= cn=pam,ou=roles,dc=domain,dc=tld

reqMod: modifyTimestamp:= 20080310195005Z

Des informations confidentielles pouvant se trouver dans cette base, comme le montre l´exemple précédent, il convient de restreindre son accès par des ACL, au même titre que la base principale. Il est intéressant également de noter que l´utilisation de ce greffon permet de faire de la synchronisation différentielle avec syncrepl, et donc de ne transmettre que les modifications entre le serveur maître et le serveur esclave.

Pour plus d´information, consultez :

- la page de manuel slapo-accesslog(5) ;

- la page greffon du Guide d´administration d´OpenLDAP.

3.2 Statistiques d´utilisation

Le module de stockage back_monitor permet, lui, d´activer les statistiques d´utilisation, là encore sous forme d´objets dans une base LDAP. La configuration est minimale, puisque la base de stockage de ces informations est obligatoirement cn=monitor, et qu´il n´y a aucune directive :

# chargement des modules

moduleload back_monitor.la

...

database monitor

Voici l´entrée correspondant aux opérations d´ouverture de session (bind), indiquant notamment le nombre d´opérations effectuées (attribut monitorOpInitiated), depuis la date de mise en place du greffon (attribut createTimestamp). Tous les attributs en question sont des attributs opérationnels, il faut prendre garde à les demander explicitement dans la requête.

# Bind, Operations, Monitor

dn: cn=Bind,cn=Operations,cn=Monitor

structuralObjectClass: monitorOperation

monitorOpInitiated: 7690

monitorOpCompleted: 7690

creatorsName:

modifiersName:

createTimestamp: 20080310194959Z

modifyTimestamp: 20080310194959Z

entryDN: cn=Bind,cn=Operations,cn=Monitor

subschemaSubentry: cn=Subschema

hasSubordinates: FALSE

Là encore, des ACL peuvent donc être nécessaires, bien que la confidentialité de ces informations soit moins évidente que dans le cas précédent, et s´apparente à celles de toutes les données générales de fonctionnement du système.

Pour plus d´information, consultez :

- la page de manuel slapd-monitor(5) ;

- la page stockage du Guide d´administration d´OpenLDAP.

3.3 Métrologie

Le greffon monitor, présenté à la recette précédente, fournit de nombreuses statistiques, mais dont l´exploration manuelle présente peu d´intérêt. D´autres sources de données sont également possibles, comme les outils de statistique de la base BDB utilisée pour stocker l´annuaire. Reste à trouver comment présenter ces données sous une forme accessible.

Munin est un outil de métrologie, basé comme de nombreux autres sur rrdtools, présentant de grandes facilités d´auto-configuration. Il est basé sur l´emploi de greffons spécifiques à un type de surveillance, et, dans le cas d´OpenLDAP, il en existe deux, récupérables chez un contributeur anonyme. Le premier de ce plugin utilise les statistiques fournies par le greffon monitor, le deuxième utilise l´outil db_stat (paquetage db42-utils sur une Mandriva 2008.0, db46-utils sur les versions suivantes). Les figures 1 à 3 sont plus parlantes qu´un long discours.

001

002

003

Hobbit est une autre solution de métrologie similaire. Un greffon écrit par Buchan Milne, bb-openldap, permet de récupérer les informations de slapd. En plus des informations de métrologies à proprement parler, il vérifie également la synchronisation des serveurs secondaires, comme présenté dans la recette 8.2.

4. Réplication

La réplication est le mécanisme permettant de mettre en place un ou plusieurs serveurs secondaires, de façon à améliorer la robustesse du service. L´ancien système slurpd est obsolète. Il a même complètement disparu de la branche 2.4, et il a été aujourd´hui remplacé par syncrepl. Les recettes suivantes montrent sa mise en place, présentent quelques façons de surveiller son fonctionnement, puis comment faire en sorte de propager les changements effectués sur un serveur secondaire vers le serveur principal de façon transparente.

4.1 Mise en place

La mise en place proprement dite ne pose guère de problèmes particuliers. Il s´agit de charger le greffon sur le serveur secondaire (le consommateur), de le configurer proprement, de s´assurer que l´identité utilisée sur le serveur principal (le fournisseur) permet la lecture de tout ce qui doit être répliqué. Le serveur secondaire va alors se mettre à jour de façon automatique. Cependant, quelques précisions peuvent se révéler utiles.

D´abord, il existe deux types de synchronisation : intermittente ou persistante. Dans le premier cas, (refreshOnly), le consommateur envoie au fournisseur une requête de synchronisation à intervalle régulier. Dans le second cas (refreshAndPersist), cette requête persiste au sein du fournisseur, et tout changement sur celui-ci provoque automatiquement leur transmission au consommateur. Dans l´exemple suivant, c´est le mode persistant qui est utilisé, comme déterminé par le paramètre type :

syncrepl rid=123

provider=ldaps://ldap.domain.com

type=refreshAndPersist

searchbase="dc=domain,dc=tld"

scope=sub

schemachecking=off

bindmethod=simple

binddn="cn=syncrepl,ou=roles,dc=domain,dc=tld

credentials=s3cr3t

Ceci ne doit pas être confondu avec la synchronisation entière ou différentielle (delta syncrepl). La première, la plus simple à mettre en œuvre transmet au consommateur l´intégralité d´une entrée lorsque celle-ci est modifiée. Ce qui dans le cas des annuaires importants peut générer un trafic important. À l´opposé, la seconde ne transmet que les modifications effectives, ce qui est censé être plus économe en termes de bande passante. Elle nécessite par contre la mise en place de journaux d´audit, comme détaillé dans la recette 7.1. Il suffit alors de rajouter les paramètres logbase, logfilter et syncdata à l´exemple précédent :

syncrepl rid=123

provider=ldaps://ldap.domain.com

type=refreshAndPersist

logbase="cn=log"

logfilter="(&(objectClass=auditWriteObject)(reqResult=0))"

syncdata=accesslog

searchbase="dc=domain,dc=tld"

scope=sub

schemachecking=off

bindmethod=simple

binddn="cn=syncrepl,ou=roles,dc=domain,dc=tld"

credentials=s3cr3t

Au niveau des permissions, sur le consommateur, c´est le rootdn qui est utilisé. Il n´y a donc besoin d´aucune ACL particulière. Par contre, sur le fournisseur, il est important de vérifier que l´utilisateur utilisé possède toutes les autorisations nécessaires, et n´est pas bridé par de quelconques limites. Ceci à la fois pour la base à répliquer d´une part, et sur la base de logs également dans le cas de la réplication différentielle. Une solution radicale est de passer par une ACL dédiée, à placer en tête de liste :

# syncrepl: accès en lecture global

access to dn.subtree="dc=domain,dc=tld" by

dn="cn=syncrepl,ou=roles,dc=domain,dc=tld" read

by * break

limits dn.exact="cn=syncrepl,ou=roles,dc=domain,dc=tld"

size.soft=unlimited size.hard=unlimited size.unchecked=unlimited

time.soft=unlimited time.hard=unlimited

Enfin, le paramètre rid est un identifiant de réplication interne au consommateur. En d´autres termes, il n´y a pas besoin de s´assurer de son identité entre différents consommateurs.

Pour plus d´information, consultez :

- la page de manuel slapd.conf(5) ;

- la page réplication du Guide d´administration d´OpenLDAP.

4.2 Vérification

À la mise en place de la réplication, il est facile de s´assurer de son fonctionnement. D´abord, le répertoire hébergeant la base se remplit de nouveaux fichiers. Ensuite, il suffit d´interroger le serveur secondaire pour s´assurer qu´il contient bien les entrées répliquées. Mais, comment s´assurer que cette réplication persiste par la suite, et que les différents serveurs restent synchronisés ?

Nagios est un outil de supervision bien connu. Sur le site Nagios Exchange, on trouve un greffon de surveillance de l´état de synchronisation de deux serveurs OpenLDAP, check_syncrepl. Il est écrit en Python, et nécessite le paquetage python-ldap. Il est également disponible dans le paquetage nagios-check_syncrepl sous Mandriva. Une fois installé, il suffit de le lancer en lui donnant la base de rechercher, et les URI du fournisseur et du consommateur. Si les deux serveurs sont synchronisés, le résultat ressemble à ceci :

[guillaume@oberkampf ~]$ /usr/lib/nagios/plugins/check_syncrepl.py ldap://ldap1.domain.tld ldap://ldap2.domain.tld -b dc=domain,dc=com

2008-09-25 00:32:47,305 - check_syncrepl.py - INFO - Provider is: ldap1.domain.tld

2008-09-25 00:32:47,321 - check_syncrepl.py - INFO - Checking if consumer ldap2.domain.tld is in SYNCH with provider

2008-09-25 00:32:47,327 - check_syncrepl.py - INFO - Provider and consumer exactly in SYNCH

Si ce n´est pas le cas, par contre, le le résultat ressemble à cela :

[guillaume@oberkampf ~]$ /usr/lib/nagios/plugins/check_syncrepl.py ldap://ldap1.domain.tld ldap://ldap2.domain.tld -b dc=domain,dc=com

2008-09-25 00:32:47,305 - check_syncrepl.py - INFO - Provider is: ldap1.domain.tld

2008-09-25 00:32:47,305 - check_syncrepl.py - INFO - Provider is: ldap1.domain.tld

2008-09-25 00:32:47,321 - check_syncrepl.py - INFO - Checking if consumer ldap2.domain.tld is in SYNCH with provider

2008-09-25 00:32:47,327 - check_syncrepl.py - INFO - Consumer NOT in SYNCH

2008-09-25 00:32:47,327 - check_syncrepl.py - INFO - Delta is 84 days, 0:50:21

En général, il suffit de relancer le consommateur, éventuellement après avoir vidé sa base, pour résoudre la plupart des problèmes.

Attention, pour la mise en œuvre par Nagios, il faut rediriger (option -l) dans un répertoire adéquat, ou, mieux, supprimer (option -q) l´enregistrement du résultat, et rajouter l´option -n. J´ai également rencontré des soucis avec des serveurs donnant une précision temporelle à la microseconde pour le marqueur de synchronisation, entraînant un crash. Un patch est disponible sur la page d´origine du greffon.

Le greffon bb-openldap pour Hobbit, déjà présenté dans la recette 7.3, réalise également cette surveillance.

4.3 Redirection transparente des modifications

Un serveur esclave est par définition en lecture seulement, tous les changements se faisant exclusivement sur le maître. Or, dans certaines architectures, ce dernier n´est pas accessible aux utilisateurs. Comment faire dans ce cas pour leur permettre néanmoins d´effectuer les modifications les concernant, comme les changements de mot de passe ?

La solution présentée ici fait intervenir plusieurs fonctionnalités avancées de LDAP : les références LDAP (referrals), la délégation (proxy), et le greffon chain. Elle est donc assez complexe, mais parfaitement fonctionnelle.

Le début est trivial, puisqu´une simple directive updateref dans la configuration de l´esclave est suffisante :

updateref ldap://ldap1.domain.tld

Toute demande de modification se voit ainsi retournée une référence vers le serveur maître, comme ici :

[guillaume@oberkampf ~]$ ldappasswd -x -D uid=bar,ou=users,dc=domain,dc=tld -w password -s newpassword -H ldap2.domain.tld

Result: Referral (10)

Referral: ldaps://ldap1.domain.tld

Certains clients LDAP peuvent être configurés pour suivre automatiquement cette référence, avec tous les problèmes de sécurité qui s´ensuivent. Mais, de toute façon, le but poursuivi ici est de rendre la chose transparente pour l´utilisateur. Le greffon chain permet justement d´effectuer ce suivi automatiquement, au sein du serveur. Sur le serveur esclave, il faut donc rajouter la configuration suivante :

overlay chain

chain-uri "ldap://ldap1.domain.tld"

chain-idassert-bind bindmethod="simple"

binddn="cn=chain,ou=roles,dc=domain,dc=tld"

credentials="s3cr3t"

mode="self"

chain-idassert-authzFrom "*"

chain-tls start

chain-return-error TRUE

La directive chain-uri met en place le suivi pour toutes les références correspondant à sa valeur. La directive chain-idassert-bind définit l´identifiant, le mot de passe et la méthode utilisée pour s´authentifier sur le serveur maître : en effet, le serveur esclave utilise un identifiant particulier, à la place de l´identifiant de la requête initiale. Si cette authentification réussie, par contre, c´est bien l´identifiant de cette requête initiale qui sera utilisée pour vérifier ses autorisations. Cette dissociation d´identité entre authentification et autorisation est rendue nécessaire par le fait que le mot de passe de l´utilisateur initial n´est plus disponible à cette étape, et qu´il n´est pas possible de rejouer son authentification sur le serveur maître. La directive chain-idassert-authzFrom autorise tous les identifiants à utiliser ce mandataire. Enfin, une connexion chiffrée est utilisée, et, en cas d´échec, l´erreur est renvoyée au client à la place de la référence.

Enfin, il faut mettre en place le mandataire sur le maître. D´abord, il faut autoriser cette fonctionnalité, en définissant une politique de délégation :

# proxy authorization policy

authz-policy to

Cette directive, telle que définie ici, permet de s´authentifier auprès du serveur avec un identifiant donné, puis à utiliser un autre identifiant compatible avec la valeur de l´attribut authzTo de cette entrée pour la détermination des autorisations. Nous avons besoin que l´identité cn=chain,ou=roles,dc=domain,dc=tld, utilisée par l´esclave, puisse ainsi prendre le rôle de n´importe quelle autre identité. Il faut donc définir cette entrée de la façon suivante :

dn: cn=chain,ou=roles,dc=domain,dc=tld

objectClass: organizationalRole

objectClass: simpleSecurityObject

cn: chain

description: slave server proxy user

authzTo: dn:*

Une fois tout ceci en place, la tentative de modification précédente devrait réussir :

[guillaume@oberkampf ~]$ ldappasswd -x -D uid=bar,ou=users,dc=domain,dc=tld -w password -s newpassword -H ldap2.domain.tld

[guillaume@oberkampf ~]$

Attention, l´attribut authzTo est un attribut opérationnel présent pour toute entrée. Autrement dit, un utilisateur capable de modifier son propre attribut est capable d´usurper l´identité de n´importe quel autre utilisateur... D´une manière générale, l´accès aux attributs de l´entrée d´un utilisateur par lui-même doit être strictement contrôlé, et n´autoriser la modification que dans les cas pertinents :

# attributes

access to dn.subtree="ou=users,dc=domain,dc=tld" attrs=userPassword

by self ssf=56 write

by self peername.ip=127.0.0.1 write

by anonymous ssf=56 auth

by anonymous peername.ip=127.0.0.1 auth

by * none

access to dn.subtree="ou=users,dc=domain,dc=tld"

attrs=loginShell,gecos,displayName,description,telephoneNumber,mobile,mail,labeledURI,postalAddress,cn,sn,givenName,roomNumber,preferredLanguage

by self write

by * read

access to dn.subtree="ou=users,dc=domain,dc=tld"

by * read

D´autres politiques de délégation, moins sensibles, peuvent également être adoptées. Plutôt qu´autoriser le mandataire à prendre n´importe quel identifiant, il peut être restreint à la branche utilisateur :

dn: cn=chain,ou=roles,dc=domain,dc=tld

objectClass: organizationalRole

objectClass: simpleSecurityObject

cn: chain

description: slave server proxy user

authzTo: dn.children:ou=users,dc=domain,dc=tld

Ou encore, la politique peut être définie dans l´autre sens. Plutôt que de définir pour qui le mandataire peut se faire passer, on peut définir par qui un identifiant peut se faire représenter, en utilisant cette fois-ci l´attribut authzFrom, et en changeant la configuration de slapd :

# proxy authorization policy

authz-policy from

Ces utilisateurs peuvent ainsi se faire représenter :

dn: uid=foo,ou=users,dc=domain,dc=tld

authzFrom: dn: cn=chain,ou=roles,dc=domain,dc=tld

dn: uid=bar,ou=users,dc=domain,dc=tld

authzFrom: dn: cn=chain,ou=roles,dc=domain,dc=tld

Cette politique offre un meilleur contrôle, mais nécessite plus de modifications dans les données.

Pour plus d´information, consultez :

- la page de manuel slapd.conf ;

- la page de manuel slapo-chain(5) ;

- la page de manuel slapd-ldap(5) ;

- la page greffon du Guide d´administration d´OpenLDAP ;

- la page stockage du Guide d´administration d´OpenLDAP.

5. Kerberos

Le modèle d´authentification par défault d´OpenLDAP est simple à mettre en œuvre et à utiliser. Associé à une connexion chiffrée par SSL ou TLS, il permet également un niveau de sécurité satisfaisant. Mais, il existe un autre modèle, plus robuste mais également plus complexe à mettre en œuvre, basé sur SASL. Ce dernier est en fait un modèle générique, permettant d´utiliser différents mécanismes de façon transparente pour l´application. Parmi ceux-ci, on peut citer l´utilisation de certificats X.509 ou encore Kerberos. Ce dernier est un mécanisme d´authentification fort, qui nécessite la mise en place d´une infrastructure dédiée, et notamment un serveur de distributions de clés, le KDC (Key Distribution Center). Pour chaque application à laquelle il désire accéder, l´utilisateur obtient un ticket d´authentification auprès de ce KDC, et le présente à l´application. Si celle-ci arrive à le déchiffrer, elle a la garantie que l´utilisateur est bien celui qu´il prétend être.

Les recettes qui suivent montrent l´intégration d´un serveur OpenLDAP à cette infrastructure Kerberos, que l´on suppose déjà mise en place.

5.1 Authentification

La mise en œuvre de Kerberos au sein d´OpenLDAP est relativement simple. Il suffit de créer un principal de la forme ldap/host.domain.tld, en utilisant le nom canonique du serveur LDAP, c´est-à-dire celui retourné par une interrogation DNS sur son adresse IP, et pas un quelconque alias DNS. En effet, le client effectue une normalisation du nom de l´hôte avant de demander un ticket au KDC. Puis, il faut extraire la clé de ce principal dans le fichier keytab du serveur LDAP, par exemple de la façon suivante :

[guillaume@oberkampf ~]$ kadmin -p admin/admin@DOMAIN.TLD ext_keytab ldap/host.domain.tld

Il faut ensuite s´assurer que le fichier keytab est bien lisible par le processus slapd, puis installer SASL et son greffon GSSAPI, et enfin relancer le serveur. Si tout est correct, le serveur devrait alors déclarer GSSAPI parmi les mécanismes d´authentification disponibles lorsque l´on interroge la racine DSE :

[guillaume@oberkampf ~]$ ldapsearch -x -b "" -s base supportedSASLMechanisms -LL

version: 1

dn:

supportedSASLMechanisms: GSSAPI

Il ne reste plus qu´à installer également SASL sur le client, à obtenir un ticket Kerberos, puis à tester avec un client :

[guillaume@oberkampf ~]$ ldapsearch -Y GSSAPI -s base -LL

SASL/GSSAPI authentication started

SASL username: user@DOMAIN.TLD

SASL SSF: 56

SASL data security layer installed.

version: 1

dn: dc=domain,dc=tld

dc: domain

objectClass: top

objectClass: domain

Pour plus d´information, consultez :

- la page SASL du Guide d´administration d´OpenLDAP.

5.2 Autorisation

Une fois Kerberos en place, l´utilisateur peut s´authentifier en présentant un ticket au serveur LDAP, à la place du couple identifiant/mot de passe. Néanmoins, il est alors affecté d´un identifiant distinct de son identifiant habituel, comme le montre la commande ldapwhoami, ce qui le prive des permissions attribuées à celui-ci :

[guillaume@oberkampf ~]$ ldapwhoami -Y GSSAPI

SASL/GSSAPI authentication started

SASL username: user@DOMAIN.TLD

SASL SSF: 56

SASL data security layer installed.

dn:uid=user,cn=gssapi,cn=auth

Pour effectuer la traduction de cet identifiant SASL en identifiant canonique, il faut utiliser la directive authz-regexp dans la configuration de slapd :

authz-regexp "uid=([^,]*),cn=gssapi,cn=auth" "uid=$1,ou=users,dc=domain,dc=tld"

Une fois ceci effectué, tout devrait rentrer dans l´ordre :

[guillaume@oberkampf ~]$ ldapwhoami -Y GSSAPI

SASL/GSSAPI authentication started

SASL username: user@DOMAIN.TLD

SASL SSF: 56

SASL data security layer installed.

dn:uid=user,ou=users,dc=domain,dc=tld

Pour plus d´information, consultez :

- la page de manuel slapd.conf.

Conclusion

La conclusion du mois précédent s'applique toujours. Ce supplément/complément inespéré n'avait pu être intégré faute de place et il nous semblait toutefois important de ne pas faire l'impasse sur toutes ces bonnes choses.

OpenLDAP est déjà riche en fonctionnalités et en usages. Mais, il faut également savoir que le projet lui-même ne cesse d'évoluer. Qui sait ce qu'il sera possible de faire d'ici quelques mois, d'ici un an ou plus ? Les informations données ici sont bien entendu utilisables en tant que telles, mais c'est aussi à vous de pousser plus loin et de les compléter avec vos recettes « maison ». De quoi finir une longue série de soirées d'hiver en beauté, non ?

Remerciements

Pour finir, je tiens à remercier Buchan Milne et Andreas Hasenack, qui sont à l´origine d´une grande partie des informations présentées ici, ainsi que Mickaël Scherer pour sa relecture attentive.

Erratum sur la partie 3.5 de l’article précédent

La valeur de l’attribut userPassword quand elle est déléguée à SASL doit être de la forme {SASL}utilisateur@royaume, et non pas SASLutilisateur@royaume.


Par le même auteur

Kerberos, le SSO universel : 4- Relation de confiance entre royaumes

Magazine
Marque
GNU/Linux Magazine
Numéro
143
|
Mois de parution
novembre 2011
|
Résumé

Jusqu'ici, les exemples présentés concernaient l'accès à des ressources appartenant au même royaume Kerberos que l'utilisateur. Or, ceci n'est pas toujours le cas dans la réalité, lorsqu'il existe plusieurs royaumes Kerberos différents au sein d'une même entité. Cette situation peut être volontaire, les différents royaumes jouant une fonction de cloisonnement, ou non, typiquement lorsqu'un domaine Active Directory est mis en place pour gérer un parc de machines Windows et qu'il vient avec son propre royaume Kerberos.

Kerberos, le SSO universel : 2- Intégration des applications

Magazine
Marque
GNU/Linux Magazine
Numéro
143
|
Mois de parution
novembre 2011
|
Résumé

L'intégration des applications à une infrastructure Kerberos, ou kerberisation, peut se faire de deux manières différentes. La première, l'authentification Kerberos à proprement parler, consiste à utiliser le mécanisme décrit précédemment, à base de tickets, entre le client et le serveur (figure 1). Il faut bien évidemment un support explicite pour ce mécanisme au niveau du client et du serveur, mais également dans le protocole utilisé.

Kerberos, le SSO universel : 3- Subtilités diverses et mise en œuvre avancée

Magazine
Marque
GNU/Linux Magazine
Numéro
143
|
Mois de parution
novembre 2011
|
Résumé

La partie précédente a montré l'utilisation de Kerberos au sein de différentes applications, à partir de cas d'école. Néanmoins, les cas réels sont généralement plus complexes et certains détails viennent parfois compliquer la donne. Cette partie présente donc certains problèmes de mise en œuvre de Kerberos et la façon d'y remédier.

Kerberos, le SSO universel : 1- Présentation et déploiement

Magazine
Marque
GNU/Linux Magazine
Numéro
143
|
Mois de parution
novembre 2011
|
Résumé

Kerberos est un protocole d'authentification réseau, qui présente la particularité d'allier à la fois sécurité et confort d'utilisation, puisqu'il s'agit d'un système d'authentification unique (SSO, Single Sign On). À l'heure où fleurissent les articles vantant les mérites des systèmes de type CAS, assurant un service similaire, mais limité aux seules applications web, il paraît intéressant de présenter cette technologie quelque peu méconnue.

Développement de sondes Nagios

Magazine
Marque
GNU/Linux Magazine
Numéro
129
|
Mois de parution
juillet 2010
|
Résumé
Nagios est un logiciel de supervision, c'est-à-dire un logiciel de surveillance du fonctionnement d'un ensemble de ressources informatique. Cette surveillance s'effectue par le biais de programmes externes, appelés greffons (plugins) dans la nomenclature consacrée.

Perles de Mongueurs - Conversion de dates

Magazine
Marque
GNU/Linux Magazine
Numéro
123
|
Mois de parution
janvier 2010
|
Résumé
Depuis le numéro 59, les Mongueurs de Perl vous proposent tous les mois de découvrir les scripts jetables qu’ils ont pu coder ou découvrir dans leur utilisation quotidienne de Perl. Bref, des choses trop courtes pour en faire un article, mais suffisamment intéressantes pour mériter d’être publiées. Ce sont les perles de Mongueurs.