[couverture de Linux Magazine 66]

Fonctionnalités avancées de OpenLDAP

Article publié dans Linux Magazine 66, novembre 2004.

Copyright © 2004 - Jérôme Fenal.

[+ del.icio.us] [+ Developers Zone] [+ Bookmarks.fr] [Digg this] [+ My Yahoo!]

Chapeau de l'article

Après avoir vu les fondements du protocole LDAP, son organisation des données et son utilisation de base, intéressons-nous maintenant aux fonctionnalités avancées de sa mise en œuvre libre, OpenLDAP.

Nous verrons d'abord comment ajouter des données non standard par le biais d'un schéma qui nous est propre. Ensuite, la sécurisation du transport LDAP via SSL/TLS, et enfin la sécurisation du service via la réplication.

Écrire son propre schéma

Il peut être plus qu'intéressant d'écrire son propre schéma. En effet, cela vous permettra d'avoir la sémantique la plus appropriée pour les données que vous désirez intégrer dans votre annuaire.

Nous allons voir ici rapidement comment écrire un petit schéma auxiliaire. Je ne parlerai cependant pas (sauf dans cette phrase) des considérations amenant à cette décision : centralisation des données dans un annuaire, création de nouvelles données au meilleur endroit (i.e. dans l'annuaire et non dans une application), etc. De même, je ne vous parlerai pas des implications de la création du schéma : pas de limitation directe sur la taille d'un champ, instrumentation nécessaire des applications utilisant les données du nouveau schéma, etc.

Nous avons vu précédemment sur la description des schémas que ceux-ci s'appuyaient sur des types de données documentés par la RFC2252. Ces types se voient associés à des ordres de tri, de même qu'à des règles de correspondance, qui sont fonctions du type de la donnée (« collate order » en anglais). Par exemple, les règles de tri associées à des contenus UTF-8 ne seront pas les mêmes que pour des contenus ASCII simples.

Préalable à l'écriture du schéma

Avec OpenLDAP, la définition d'un schéma se fait dans le format des fichiers de configuration de slapd, alors que sur d'autres annuaires, le schéma est lui-même inscrit dans l'annuaire (de façon logique, d'une part - par le biais de qu'on pourrait appeler une vue - ou dans la base de données d'autre part). C'est ainsi que l'on trouve deux formats de définition de schéma, le format LDIF, et le format d'OpenLDAP, qui n'est qu'une partie du fichier de configuration de slapd (merci include !). Nous avons déjà vu ces deux formats.

Mais comment arriver à un schéma à partir de notre liste de courses ?

En fait, c'est relativement simple, il suffit essentiellement de savoir ce que l'on doit mettre, comment le mettre, et surtout créer une sémantique qui ne soit pas ambiguë. De plus, elle ne devra de préférence pas non plus reprendre une sémantique d'un des schémas standards. Je ne saurai donc trop vous conseiller de prendre le temps de lire d'abord les RFC et schémas fournis par OpenLDAP.

Ensuite, il va falloir nous créer deux espaces de nommage. Le premier espace va s'appliquer aux noms des classes d'objets et des attributs. En règle générale, un simple suffixe suffit, tel que le nom de l'entreprise ou un acronyme.

Le second concerne les OID, identifiants numériques dont nous avons parlé dans l'article précédent. Pour identifier nos classes d'objet et nos attributs, nous ne pouvons pas choisir au hasard la 1ère partie des OID, au risque d'une collision avec des objets existants ou lors d'une prochaine extension de l'annuaire. Mais, et cela tombe bien, une branche spéciale dans l'arbre des OID existe pour ça, la branche enterprises. Pour l'utiliser, deux solutions : soit choisir un numéro au hasard, soit tout simplement s'enregistrer auprès de l'IANA qui les gère et les attribue de façon séquentielle. Pour des raisons de commodité, je prendrai le numéro 99999.

Mais cela ne nous donne que la première partie de l'espace de nommage des OID.

La seconde partie est de savoir comment bien utiliser notre sous-branche, créée sous la branche enterprise (au singulier) de l'IANA. Pourquoi ? Parce que nous pourrons être amenés un jour à créer des agents SNMP (comme cela a été illustré dans GLMF n°43). Nous allons donc créer deux branches à notre arbre : SNMP (branche 1, normal, car ce protocole est plus ancien) et LDAP (branche 2). Puis la branche LDAP sera elle-même découpée en deux autres branches : les classes d'objets (branche 1) et les attributs (branche 2). Cela nous donne la configuration suivante pour OpenLDAP :

  objectIdentifier jroot 1.3.6.1.4.1.99999


  # pas de :1 car c'est pour SNMP
  objectIdentifier j jroot:2
  objectIdentifier jObjectClass j:1
  objectIdentifier jAttributeType j:2

Notez la notation abrégée que nous permet OpenLDAP par le biais du mot-clé « objectIdentifier ». Nous allons la réutiliser un peu plus loin.

Écriture du schéma

Nous allons créer un petit schéma auxiliaire de Person, dans lequel nous allons vouloir stocker les données suivantes : un trigramme (une contraction en trois lettres du prénom et du nom de la personne), la liste de ses amis (sous forme de liens dans l'annuaire, et donc de DN), un booléen pour savoir si la personne est droitière ou non, la photo de son chien (si elle en a un, syntaxe JPEG), et enfin un attribut avec son nom en chinois, puisque, c'est bien connu, nos personnes le parlent couramment. Ce dernier attribut est d'importance, car il va nous commander de choisir un encodage UTF-8, i.e. applicable à autre chose qu'un alphabet latin, en deux mots : Directory String.

Pendant que nous y sommes, donnons-nous aussi un raccourci pour écrire les syntaxes, ce qui nous donne :

  objectIdentifier ldapAttributeTypes 1.3.6.1.4.1.1466.115.121.1

  attributetype ( jAttributeType:1
      NAME 'jTrigramme'
      DESC 'Trigramme de la personne'
      EQUALITY caseIgnoreIA5Match
      SUBSTR caseIgnoreIA5SubstringsMatch
      SYNTAX ldapAttributeTypes:26{3} SINGLE-VALUE )

Le trigramme est en IA5 String (:26, soit US-ASCII pour les intimes) car nous n'admettrons pas d'accent. De plus, le trigramme est une conception essentiellement anglo-saxonne, donc applicable uniquement aux noms écrits ou transcrits en caractères latins, quelle que soit leur origine. On demande aussi à OpenLDAP de garder à l'esprit quelque part qu'il ne fait que trois caractères.

  attributetype ( jAttributeType:2
      NAME 'jAmis'
      DESC 'Amis de la personne'
      EQUALITY distinguishedNameMatch
      SYNTAX ldapAttributeTypes:12 )

Ici, la syntaxe est donc DN (:12), car nous y mettrons les DN des autres personnes présentes dans l'annuaire amies d'une personne considérée.

  attributetype ( jAttributeType:3
      NAME 'jPhotoDuChien'
      DESC 'Photo du chien de la personne'
      SYNTAX ldapAttributeTypes:28 )

  attributetype ( jAttributeType:4
      NAME 'jDroitier'
      DESC 'La personne est droitière ?'
      SYNTAX ldapAttributeTypes:7 SINGLE-VALUE)

Rien de particulier, sinon les syntaxes JPEG (:28) et Boolean (:7). Notez quand même que la latéralisation est précisée comme SINGLE-VALUE (une seule valeur stockable dans l'annuaire à un instant donné), mais que l'on s'autorise plusieurs chiens (champ multi-valué), chiens que nous aurions pu compter, d'ailleurs.

Enfin, voici le nom en chinois :

  attributetype ( jAttributeType:5
      NAME 'jNomEnChinois'
      EQUALITY caseIgnoreMatch
      SUBSTR caseIgnoreSubstringsMatch
      DESC 'Le nom en chinois de la personne'
      SYNTAX ldapAttributeTypes:15 SINGLE-VALUE)

Je ne sais pas s'il existe une notion de majuscule et minuscule en chinois, mais nous sommes néanmoins parés à la présence de caractères le permettant, tant dans les recherches d'égalité pure que dans les recherches partielles.

Maintenant que nous avons les attributs, il nous faut les réunir dans une classe d'objets :

  objectclass ( jObjectClass:1
      NAME 'jPersonne'
      SUP 'top' AUXILIARY
      DESC 'Quelques données sur les personnes de mon annuaire'
      MUST ( cn $ uid $ jTrigramme $ jDroitier)
      MAY ( jAmis $ jPhotoDuChien $ jNomEnChinois ) )

Cette classe va obliger un objet qui en fait partie à avoir au moins les attributs jTrigramme et jDroitier, de même que cn et uid. Les autres sont optionnels et pourront ne pas être renseignés.

Mettez toutes ces définitions dans un fichier, que nous pourrons appeler jPersonne.schema, et incluez-le dans slapd.conf via l'instruction include.

Redémarrez OpenLDAP, et vous pouvez maintenant commencer à peupler votre annuaire avec ldapadd et ce qui suit :

  dn: uid=toto,ou=Paris,ou=People, dc=example,dc=com
  objectClass: top
  objectClass: Person
  objectClass: inetOrgPerson
  objectClass: posixAccount
  objectClass: shadowAccount
  userPassword: $BAbgoFfMV4LM
  uid: toto
  uidNumber: 1100
  cn: toto
  loginShell: /bin/bash
  gidNumber: 513
  gecos: Charlie toto
  description: System User
  homeDirectory: /home/toto
  sn: toto
  jTrigramme: TOT
  jDroitier: TRUE
  jPhotoDuChien::< file:///chemin/vers/medor.jpeg

Ne sachant (ni ne pouvant pour des raisons techniques) écrire en caractères chinois dans ces colonnes, je vous passerai la transcription de Toto dans cette écriture.

Problème possible

Pensez bien à votre schéma avant de l'intégrer à votre annuaire. En effet, il vous sera difficile de renommer ou de déplacer (via un changement d'OID) un attribut. Donc lors de la conception, prévoyez bien vos attributs, et si nécessaire, ne réutilisez pas les OID s'il y a déjà eu des insertions sur les attributs que vous aimeriez détruire.

Encore une fois, ce travail de conception demande de la planification. Mais une fois ce travail fait, la transcription est relativement rapide. Il faut simplement faire attention aux règles de mise en correspondance (matching).

Enfin, n'oubliez pas que d'autres annuaires ne permettent pas les raccourcis syntaxiques que nous avons pris avec OpenLDAP. Il vous faudra donc dans ce cas procéder comme indiqué dans les RFC ou comme dans les schémas fournis avec votre OpenLDAP, voire en format LDIF. Reportez-vous à votre documentation pour plus de détails. Un peu de Googlage peut aussi vous permettre de trouver des exemples.

Configuration de la partie SSL/TLS

Puisque nous allons un peu plus loin mettre en œuvre deux serveurs LDAP répliqués, nous allons devoir générer des certificats de serveur SSL/TLS certifiés par une autorité. Si vous avez déjà une autorité de certification, pas de souci, utilisez-la. Si vous avez lu le MISC de mai 2004, mettez en œuvre la PKI de votre choix. Dans le cas contraire, voici la marche à suivre pour générer l'autorité de certification (CA pour « certification authority » en anglais), puis les certificats des serveurs LDAP signés par cette autorité.

Le plus simple pour éviter un maximum de saisie est d'avoir son propre fichier de configuration pour OpenSSL.

Pour des raisons de concision, voici uniquement les lignes que j'ai modifiées par rapport au fichier openssl.cnf fourni en standard dans la distribution :

  HOME                          = /home/jfenal           # pour le fichier ~/.rnd
  dir                           = /home/jfenal/CA/demoCA # Where everything is kept
  countryName_default           = FR
  stateOrProvinceName_default   = Ile de France
  localityName_default          = Paris
  0.organizationName_default    = Jerome Fenal en personne
  emailAddress_default          = jerome.fenal@example.com

Ce fichier est stocké sur mon compte, dans le répertoire ~/CA. On définit ensuite la variable d'environnement OPENSSL_CONF pour la faire pointer sur ce fichier :

  export OPENSSL_CONF=~/CA/jfenal.cnf

Il nous reste maintenant à créer la CA, à créer ensuite un certificat, et à signer ce certificat avec le certificat de l'autorité pour avoir le certificat définitif et valide.

Création de l'autorité de certification

Nous allons utiliser le script Perl founi avec OpenSSL pour toutes les opérations. De votre côté, n'hésitez pas à reprendre ce script pour, par exemple, augmenter la durée de validité (365 jours par défaut) des certificats générés.

  $ /usr/lib/ssl/misc/CA.pl -newca
  CA certificate filename (or enter to create) ¶

Appuyez sur entrée de façon à prendre le nom par défaut pour nous éviter de vous perdre, puis répondez ensuite aux questions qui vous sont posées, en évitant les accents :

  Making CA certificate ...
  Generating a 1024 bit RSA private key
  ..++++++
  ....................................++++++
  writing new private key to './demoCA/private/cakey.pem'
  Enter PEM pass phrase:**********************************¶
  Verifying - Enter PEM pass phrase:**********************************¶
  -----
  You are about to be asked to enter information that will be incorporated
  into your certificate request.
  What you are about to enter is what is called a Distinguished Name or a DN.
  There are quite a few fields but you can leave some blank
  For some fields there will be a default value,
  If you enter '.', the field will be left blank.
  -----
  Country Name (2 letter code) [FR]:¶
  State or Province Name (full name) [Ile de France]:¶
  Locality Name (eg, city) [Paris]:¶
  Organization Name (eg, company) [Jerome Fenal en personne]:¶
  Organizational Unit Name (eg, section) []:Autorite de certification primaire¶
  Common Name (eg, YOUR name) []:Autorite de certification primaire¶
  Email Address [jerome.fenal@example.com]:¶

(Les « ¶ » indiquent les retours-chariot lors de la saisie.)

Notre autorité de certification est prête. Le certificat racine de l'autorité est dans ~/CA/demoCA/cacert.pem. La clé privée du certificat racine est dans ~/CA/demoCA/private/cakey.pem. Nous créons ensuite le certificat pour notre serveur LDAP : cela passe par la création de la requête, du certificat, puis la signature du certificat par la CA.

Création d'un certificat

L'utilisation du script CA.pl impliquerait la création d'un certificat avec une phrase de passe. Or nous voulons pouvoir redémarrer le service LDAP sans devoir taper cette phrase de passe au démarrage. Nous allons devoir réaliser les opérations à la main.

Il nous faut d'abord générer un couple clé privée/clé publique pour le certificat :

  $ openssl genrsa -out ldap1.example.com.key 1024
  Generating RSA private key, 1024 bit long modulus
  ...........++++++
  ..............................++++++
  e is 65537 (0x10001)

Création de la demande de certificat :

  $ openssl req -new -key ldap1.example.com.key -out ldap1.example.com.req
  You are about to be asked to enter information that will be incorporated
  into your certificate request.
  What you are about to enter is what is called a Distinguished Name or a DN.
  There are quite a few fields but you can leave some blank
  For some fields there will be a default value,
  If you enter '.', the field will be left blank.
  -----
  Country Name (2 letter code) [FR]:¶
  State or Province Name (full name) [Ile de France]:¶
  Locality Name (eg, city) [Paris]:¶
  Organization Name (eg, company) [Jerome Fenal en personne]:¶
  Organizational Unit Name (eg, section) []:¶
  Common Name (eg, YOUR name) []:ldap1.example.com¶
  Email Address [jerome.fenal@example.com]:¶

  Please enter the following 'extra' attributes
  to be sent with your certificate request
  A challenge password []:¶
  An optional company name []:¶

La demande est donc maintenant dans ldap1.example.com.req. Pour information, c'est ce que vous envoyez à une autorité de certification qui vous le signera en retour. Reste donc à certifier cette demande pour en faire un certificat valide.

Signature du certificat par la CA pour certification

  $ openssl ca -in ldap1.example.com.req -extensions v3_ca -out ldap1.example.com.pem -days 3000
  Using configuration from /home/jfenal/MyCA/myca.cnf
  Enter pass phrase for /home/jfenal/MyCA/demoCA/private/cakey.pem:
  Check that the request matches the signature
  Signature ok
  Certificate Details:
          Serial Number: 1 (0x1)
          Validity
              Not Before: May 29 20:39:49 2004 GMT
              Not After : Aug 15 20:39:49 2012 GMT
          Subject:
              countryName               = FR
              stateOrProvinceName       = Ile de France
              organizationName          = Jerome Fenal en personne
              commonName                = ldap1.example.com
              emailAddress              = jerome.fenal@example.com
          X509v3 extensions:
              X509v3 Subject Key Identifier:
                  BD:3F:C0:4C:75:14:D5:00:E6:B3:E6:1D:71:45:19:4D:61:D4:2D:C3
              X509v3 Authority Key Identifier:
                  keyid:AC:2F:66:7A:8A:D0:69:F9:2E:48:DC:3A:E8:6C:AC:E7:B0:1C:61:08
                  DirName:/C=FR/ST=Ile de France/L=Paris/O=Jerome Fenal en \
                    personne/OU=Autorite de certification primaire/CN=Autorite \
                    de certification primaire/emailAddress=jerome.fenal@example.com
                  serial:00
  
              X509v3 Basic Constraints:
                  CA:TRUE
  Certificate is to be certified until Aug 15 20:39:49 2012 GMT (3000 days)
  Sign the certificate? [y/n]:y
  
  
  1 out of 1 certificate requests certified, commit? [y/n]y
  Write out database with 1 new entries
  Data Base Updated

Remarquez que nous voulons éviter de devoir changer tous les ans le certificat, la période de validité de la certification est donc étendue à 3000 jours, soit plus de 8 ans... de quoi voir venir.

Nous avons donc notre certificat pour notre serveur LDAP (qui doit donc s'appeler ldap1.example.com).

Configuration dans OpenLDAP

Copiez le certificat de notre autorité de certification (~/MyCA/demoCA/cacert.pem) dans le répertoire /etc/ssl/.

Copiez aussi les deux fichiers ldap1.example.com.pem et ldap1.example.com.key dans /etc/ssl/openldap ou où bon vous semble, pourvu que vous vous y retrouviez.

Modifiez le fichier slapd.conf afin d'avoir les lignes suivantes :

  TLSCertificateFile      /etc/ssl/openldap/ldap1.example.com.pem
  TLSCertificateKeyFile   /etc/ssl/openldap/ldap1.example.com.key
  TLSCACertificateFile    /etc/ssl/MyCAcert.pem

Vérification du bon fonctionnement

Redémarrez ensuite le service ldap :

  # service ldap restart

ou

  # /etc/init.d/ldap restart

Le script devrait vous répondre OK ou ne pas vous sortir d'erreur.

Pour vérifier que l'on peut bien joindre l'annuaire via le protocole LDAP sur SSL, essayez de lancer une requête :

  $ ldapsearch -b dc=example,dc=com -s sub -x -w secret -D cn=Manager,dc=example,dc=com -H ldaps://localhost/

Si aucune réponse ne vient, ou si une erreur survient, vérifiez bien que OpenLDAP écoute bien en TLS, et que ldaps:/// est bien spécifié sur la ligne de commande de slapd. Par exemple, l'erreur suivante :

  ldap_bind: Can't contact LDAP server (81)
          additional info: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

signifie que le serveur a bien été contacté, mais que son certificat n'a pu être validé au regard du certificat racine de l'autorité dont il dépend. Il nous faut donc configurer le client LDAP (via la bibliothèque), et pour ce faire, créer un fichier de configuration pour le compte d'où le client est lancé (~/.ldaprc ou ~/ldaprc). Le fichier doit contenir :

  BASE    dc=example, dc=com
  URI     ldaps://ldap1.example.com/
  TLS_CACERT      /etc/ssl/MyCAcert.pem
  TLS_REQCERT     demand

Cela nous permet d'indiquer la base de recherche par défaut, la connexion au serveur par défaut, ainsi que le protocole (LDAP ou LDAP/SSL), et pour la partie SSL/TLS, le certificat de l'autorité de certification dont dépend le serveur (si nous sommes sur la même machine, autant spécifier le même fichier), et le type de comportement face au certificat SSL (on le vérifie absolument, ou pas). Pour plus d'informations, référez-vous à la page de manuel ldap.conf(5).

Réplication LDAP via slurpd

Pour monter un serveur LDAP répliqué, commencez d'abord par monter un second serveur LDAP similaire au premier.

La configuration spécifique à la réplication est relativement simple : il faut spécifier au maître de faire rejouer les transactions sur le ou les esclaves, et il faut que le serveur esclave redirige les clients voulant faire des modifications vers le serveur maître, lui seul ayant la possibilité de recevoir des écritures.

Dans ce cas, un client sera bien déconnecté du serveur sur lequel il était pour aller interroger et renseigner le serveur maître. Ce n'est en fait pas le client qui le fait, mais la bibliothèque LDAP qui s'en charge pour lui.

Je partirai du principe que le serveur maître est ldap1.example.com, le serveur esclave ldap2.example.com. N'oubliez pas de générer un certificat avec le bon nom de serveur (Common Name dans la description du certificat), dérivant de la même autorité de certification (pour simplifier les choses).

La réplication n'est pas très difficile, voire plutôt très simple à mettre en œuvre, pourvu que l'on ait la bonne information au départ. Alors je l'écris en gras : une configuration de réplication s'applique à une base de données LDAP et non à un annuaire. Ça va mieux en le disant, d'autant que je n'ai trouvé cette information qu'après une lecture assidue des lidies (listes de distribution) sur gmane.org (pub !). En effet, un annuaire OpenLDAP peut gérer plusieurs bases, avec plusieurs suffixes différents. Or il est tout à fait possible de vouloir répliquer une base et pas une autre. Ce point de détail bloquant m'a valu une certaine perte de temps avant de comprendre que je ne pouvais pas mettre la configuration de réplication où je voulais dans /etc/openldap/slapd.conf.

C'est pourtant explicitement dit dans la page de manuel de slapd.conf(5) : Specify a replication site for this database, mais que voulez-vous... Une directive de réplication se met donc dans la section de description database afférente.

Principe

Le serveur qui doit répliquer sa base de données sur un autre serveur met à disposition les modifications de ces données dans un fichier, sous une forme proche de LDIF. C'est le journal des modifications.

Ce fichier est lu ensuite par un processus frère, slurpd, qui est démarré en même temps que slapd quand on veut faire de la réplication. Son boulot est de vérifier le journal que lui passe slapd, et de faire passer les modifications sur les tous les serveurs configurés. Les modifications sont communiquées par slurpd aux autres slapd par le protocole que vous utilisez, LDAP. Il va sans dire qu'il faudra donc spécifier l'utilisateur qu'utilisera slurpd pour se connecter sur les esclaves. Cet utilisateur, que nous nommerons le réplicateur et qu'il nous faudra définir d'abord dans l'annuaire maître, sera le seul avoir le droit en écriture sur les serveurs esclaves.

Pour éviter les modifications par tout autre que le réplicateur sur les serveurs esclaves, nous allons utiliser une particularité de LDAPv3 : l'utilisation des renvois (Referrals). Ces renvois, notifiés de façon transparente au client, feront que ce client LDAP se connectera de manière tout aussi transparente au serveur spécifié dans le renvoi afin de mener à bien les modifications demandées. Tous les clients actuels (basés sur les bibliothèques clientes OpenLDAP) supportent ainsi LDAPv3, et donc les renvois.

Mise en œuvre

On modifie la configuration des serveurs maître et esclave de la façon suivante :

Maître

On ajoute à la suite de la définition de la base de données (database) les lignes :

  replica host=ldap2.example.com:389
        tls=yes
        bindmethod=simple
        binddn="cn=Replicator,dc=example,dc=com"
        credentials=ksdhjdlk

On a donc défini un serveur ldap2, accessible via TLS, s'authentifiant via un mot de passe (et non via SASL) avec le compte cn=Replicator,dc=example,dc=com et le mot de passe : ksdhjdlk.

N'oubliez pas d'insérer la définition LDIF du compte réplicateur dans l'annuaire :

  dn: cn=Replicator, dc=example,dc=com
  userPassword: {MD5}wTLNbidzOxJH8bbfhDL83A==
  objectClass: person
  objectClass: top
  sn: Replicator
  cn: Replicator

On notera que le réplicateur est défini à la racine du DIT (« Directory Information Tree », arbre contenant les données de l'annuaire). Cela évitera par la suite de le confondre avec un compte utilisateur.

Ce compte sera donc défini tant dans l'annuaire maître que dans l'annuaire esclave, car les deux sont identiques. On peut cependant imaginer avoir une base de données spécifique au serveur esclave, avec un autre suffixe. Mais pour un simple compte de réplication, cela en vaut-il la peine ?

Esclave (serveur ldap2)

De même que pour la base de données à répliquer de l'annuaire maître, on ajoute les lignes suivantes à la suite de la définition de la base de données répliquée sur le serveur esclave :

  updateref       ldap://ldap1.example.com:389/
  updatedn        "cn=Replicator,dc=example,dc=com"

La première ligne permet le renvoi vers l'annuaire maître pour les écritures, et spécifie le DN du réplicateur. Ajoutez et/ou modifiez les ACL par ce qui suit, pour autoriser le réplicateur à écrire dans l'annuaire répliqué :

  access to attr=userPassword
    by self write
    by anonymous auth
    by dn="cn=Replicator,dc=example,dc=com" write
    by * none

  access to *
    by dn="uid=Replicator,dc=example,dc=com" write
    by * read

Encore une fois, les lignes updateref et updatedn sont spécifiques à une database (me répété-je ?).

Synchronisation des annuaires

Pour la synchronisation, il vaut mieux placer l'annuaire en lecture seule le temps de récupérer son contenu, même si dans les versions récentes, avec Berkeley DB, il est possible de lancer la commande slapcat en étant assuré d'avoir un vidage cohérent du contenu de l'annuaire.

De plus, le fait de placer l'annuaire maître en lecture seule le temps de la réplication permet de s'assurer de l'absence de modification sur le maître pendant que le contenu est transféré du maître à l'esclave. Le retour en mode normal, autorisant des modifications sur l'annuaire maître, ne doit se faire qu'après le redémarrage de l'esclave, qui contiendra à ce moment-là exactement les données du maître.

Pour ce faire, nous allons rajouter une clause include au fichier de configuration de l'annuaire maître pour la base de données à répliquer. Cette clause pointera sur un fichier, situé à côté de slapd.conf, dans lequel nous pourrons spécifier readonly on ou readonly off. Cela nous permettra de passer la base dans un mode ou l'autre beaucoup plus facilement (et rapidement) qu'avec un appel à perl (du style perl -pi -e 's/readonly on/readonly off/g' /etc/openldap/slapd.conf), qui devra même être plus compliqué que ça dans le cas d'un annuaire avec plusieurs bases.

La ligne à ajouter dans slapd.conf, après la définition de la base dc=example,dc=com :

  include /etc/openldap/readonly-example.com.conf

Le script pour lancer la réplication sera donc :

  #!/bin/bash
  # Réplication d'annuaire OpenLDAP par dump ou par copie des bases
  #
  BACKUPDIR=/var/lib/ldap/backup
  BASE=example.com
  BASESUFFIX="dc=example,dc=com"
  REPL=base
  #REPL=dump
  #
  mkdir -p $BACKUPDIR
  chown root:root $BACKUPDIR
  chmod 700 $BACKUPDIR
  #
  echo "readonly on" > /etc/openldap/readonly-${BASE}.conf
  service ldap restart
  #
  slapcat -b "$BASESUFFIX" > $BACKUPDIR/slapcat.dump
  rc=$?
  if [ $rc -ne 0 ] ; then
    echo "Le dump de la base $BASESUFFIX a échoué"
    echo "Réplication impossible"
    exit 1
  fi
  #
  # Arret LDAP distant
  ssh ldap2.example.com "/etc/init.d/ldap stop"
  if [ "$REPL" == "base" ] ; then 
    scp $BACKUPDIR/slapcat.dump ldap2.example.com:$BACKUPDIR/slapcat.dump
    scp /var/lib/ldap/*.dbb ldap2.example.com:/var/lib/ldap
    ssh ldap2.example.com "chown ldap:ldap /var/lib/ldap/*.dbb"
  else
    cmd="/etc/init.d/ldap stop"
    cmd="$cmd ; rm -f /var/lib/ldap/*"
    cmd="$cmd ; su - ldap -c slapadd -c -l $BACKUPDIR/slapcat.dump"
    ssh ldap2.example.com "$cmd"
  fi
  ssh ldap2.example.com "/etc/init.d/ldap start"
  echo "readonly off" > /etc/openldap/readonly-${BASE}.conf
  service ldap restart

Ce script suppose que vous ayez soit une clé pour le compte root de ldap1 autorisée sur ldap2, soit que vous ayez « l'agent forwarding » pour votre clé personnelle activé (et votre clé autorisée sur le compte root de vos deux serveurs), soit que vous soyez prêt à taper un certain nombre de fois le mot de passe root de ldap2.

Ce script ne teste pas tous les cas de figures où peuvent survenir des problèmes, en particulier en cas d'erreur au cours du transfert des données. À ce titre, ne le lancez que manuellement et surveillez son déroulement. Sinon, modifiez-le afin de vérifier tous les codes retour de toutes les commandes et n'avancez dans le déroulement du script que si tout se passe bien. N'oubliez pas d'envoyer des avertissements pour vérifier à votre arrivée le matin le bon déroulement des transferts de nuit en automatique.

Liens

Les sites web

La prochaine fois...

... nous verrons comment authentifier un serveur Linux sur un annuaire LDAP, ce grâce aux PAM (sans jeu de mot, S.V.P.).

Les listes de diffusions

Je vous donne ici le nom de la liste, ainsi que sa localisation dans l'excellent moteur d'archivage et de présentation en NNTP de lidies, j'ai nommé Gmane.org, ainsi que le site Web du gestionnaire de liste.

Jérôme Fenal

<jfenal@free.fr> et <jerome.fenal@logicacmg.com>.

Jérôme Fenal est utilisateur de GNU/Linux depuis 1994, de divers Unix (Ultrix) ou Unix-like depuis un peu plus longtemps, membre de Paris.pm.

Merci aux Mongueurs Marseillais, Lyonnais, Parisiens et Grenoblois qui ont assuré la relecture avisée de cet article.

Cet article a été écrit sous Linux, avec [g]vim, en pod (Plain Old Documentation).

[IE7, par Dean Edwards] [Validation du HTML] [Validation du CSS]