C'est LE backend de référence. Ça fait le café, d'après les chamanes des profondeurs les plus lointaines de la Crypte.
Ne suivez pas ce qui suit trop aveuglément, je me rends compte que j'ai rien compris à certains morceaux, qui sont datés, et donc que ça risque d'être crasseux.
Pour l'installer :
sudo apt install slapd ldap-utils
La configuration par défaut s'appuie sur le FQDN de la machine. Avant de tout bidouiller, vérifions…
sudo slapcat | grep dn:
Le résultat devrait être “dn: dc=example,dc=org” (ou autre domaine). Si c'est ce que vous voulez, parfait. Sinon, dpkg-reconfigure slapd, répondre “non” à “Omit OpenLDAP server configuration?” et ensuite adapter le reste. À la fin, à la question “Do you want the database to be removed when slapd is purged?”, répondre “Oui”.
On peut admirer la base de son annuaire avec la commande suivante :
ldapsearch -x -LLL -H ldap://localhost -b "dc=example,dc=org"
Dans mon cas, avec l'installation par défaut et un “domaine” appelé ldap.local :
# ldapsearch -x -LLL -H ldap://localhost -b "dc=ldap,dc=local" dn: dc=ldap,dc=local objectClass: top objectClass: dcObject objectClass: organization o: ldap.local dc: ldap
Concernant la structure et la configuration d'Openldap, cela reste assez difficile à lire au début mais c'est assez “logique” pour s'apprendre au fil des manipulations.
Un petit coup de tree /etc/ldap/ permet de voir qu'on a deux types de fichiers : .schema et .ldif. Et des noms de dossiers étranges, qui utilisent en fait la syntaxe de ldap. L'une des parties qu'on va bidouiller est ici :
└── slapd.d
├── cn=config
│ ├── cn=module{0}.ldif
│ ├── cn=schema
│ │ ├── cn={0}core.ldif
│ │ ├── cn={1}cosine.ldif
│ │ ├── cn={2}nis.ldif
│ │ └── cn={3}inetorgperson.ldif
│ ├── cn=schema.ldif
│ ├── olcDatabase={0}config.ldif
│ ├── olcDatabase={-1}frontend.ldif
│ └── olcDatabase={1}mdb.ldif
└── cn=config.ldif
Ce qui est dans le dossier cn=config concerne… la configuration. Autrefois il y avait un fichier “/etc/ldap/sldap.conf”, si je comprend bien ce n'est plus trop d'actualité. Mais on n'édite pas directement les ldif ici, il faut passer par l'utilitaire ldapmodify.
Dans la suite du document, nous allons donc écrire nos documents “ldif”, puis les injecter dans l'annuaire avec cette commande ldapmodify. Il est conseillé de bosser dans un répertoire dédié (par exemple /root/ldap/conf/) histoire de tout nettoyer à la fin avec un petit “rm -R”. Ou de garder trace des bidouilles effectuées.
La méthode officielle consiste à modifier ldap à chaud (sans le redémarrer), en intervenant directement dans sa base via des commandes comme ldapmodify et ldapadd. Et il faut impérativement utiliser cela, mais la syntaxe et le fonctionnement peuvent piquer les yeux (et c'est rien de le dire). Auparavant, on utilisait /etc/ldap/sldap.conf et on peut encore voir son écriture dans /usr/share/doc/slapd/examples/slapd.conf1). Et c'est tellement plus lisible…
En fait, LDAP étant un vieux dinosaure utilisé très largement, l'usage de “/etc/ldap/sldap.conf” reste possible telle quelle. C'est cependant déconseillé et considéré comme une pratique obsolète depuis la version 2.3. L'usage du fichier de configuration “sldap.conf” demande un redémarrage du service à chaque modification, alors que la modification de la configuration dans l'arbre ldap lui-même (via ces fichiers ldif dans “cn=config”) est immédiatement pris en compte.
Cela semble anodin lors des tests, mais un gros annuaire peut mettre beaucoup de temps à redémarrer… Alors autant prendre l'habitude des bonnes pratiques, même si la syntaxe pique les yeux.
On peut essayer de combiner le meilleur des deux mondes en paramétrant le fichier slapd.conf, puis en le transformant en ldif via la commande slaptest. Cette méthode permet avant tout de vérifier si la syntaxe de nos ldif est adaptée à notre version actuelle de ldap. C'est utile pour relire et apprendre. Mais il y aura certainement des ldif qui seront à copier tel quel ou presque. Dans ce cas, il restera alors à injecter ces ldif (via ldapadd ou ldapmodify) dans l'annuaire. Attention, autant ça devrait marcher sur un serveur vierge, autant ça semble plutôt risqué sur un serveur déjà rempli, et dans tout les cas, évitons d'y aller de façon trop bourrine.
Les commandes de barbare :
# Convertir slapd.conf en fichiers ldif slaptest -f slapd.conf -F /tmp/ldif/ # Injecter un fichier ldif dans l'annuaire ldapadd -Y EXTERNAL -H ldapi:/// -f /tmp/ldif/cn=config.ldif # etc pour chaque ldif dans l'arbre
On peut séparer les données (le peuplement de l'annuaire) de la configuration avec l'option -n.
# Générer les ldif de la configuration seulement slaptest -f slapd.conf -F /tmp/ldif_config/ -n 0 # Générer les ldif des données seulement slaptest -f slapd.conf -F /tmp/ldif_data/ -n 1
Pour avoir des logs plus complets, il va falloir l'activer. On commence par copier le truc actuel, qu'on va bidouiller avant de réinjecter :
# ldapsearch -Y external -H ldapi:/// -b cn=config "(objectClass=olcGlobal)" olcLogLevel -LLL > log.ldif
Ce fichier “log.ldif” est assez court par défaut, il contient deux lignes :
dn: cn=config : ça veut dire que c'est de la configuration (ne pas bidouiller ce nom !)olcLogLevel: none : “olcLogLevel” c'est le niveau de log, forcément “none” c'est pas bavard… Au choix : On va faire ceci :
dn: cn=config changetype: modify replace: olcLogLevel olcLogLevel: 256
On injecte avec la commande suivante :
# ldapmodify -Y EXTERNAL -H ldapi:/// -f log.ldif
On peut vérifier le résultat avec ça :
# ldapsearch -Y external -H ldapi:/// -b cn=config "(objectClass=olcGlobal)" olcLogLevel
Ou simplement en faisant une requête avec slapd (par exemple ldapsearch -Y external -H ldapi:/// -b dc=ldap,dc=local ) et en admirant le résultat avec tail -f /var/log/syslog.
On retrouve les logs propres à slapd aussi dans journalctl :
journalctl -eu slapd.service
J'ai des trucs dans /var/log/syslog parce que je demande à systemd de m'écrire ce genre de log.
Il y a moyen de paramétrer pour que ce soit dans un fichier de log propre à slapd. Les infos sont par défaut envoyés à “local4”2). Yep, y'a d'autres logiciels qui loguent de façon plus clair, mais visiblement local4 n'est pas trop utilisé par ailleurs et de toute façon sur le serveur avec openldap on ne devrait pas avoir trop d'autres services. On va donc dire à rsyslog d'envoyer le flux de local4 dans un fichier de log séparé via /etc/rsyslog.d/ldap.conf.
local4.* /var/log/ldap.log
Puis on redémarre rsyslog :
sudo systemctl restart rsyslog
Cette partie va être un gros brouillon pour le moment, je grappille dans les documentations. Mais autant noter ce que je trouve, je rangerais pus tard !
Pour faire des recherches efficaces dans l'annuaire, mieux vaut bien paramétrer l'indexation((Cf https://www.openldap.org/doc/admin26/tuning.html#Indexes. On va indiquer à OpenLDAP que certaines entrées sont à indexer (et comment) pour éviter qu'il scanne tout (vraiment tout) à chaque recherche.
J'ai trouvé ça dans un slapd.conf :
index objectClass eq index cn eq,sub index sn eq,sub index uid eq index mail eq index memberOf eq
Cela liste les attributs sur lequels faire les recherches, limiter ça aux plus pertinent. Trop d'index ralentit l'écriture. Par exemple, pas besoin de sn chez nous, cette info ne nous intéresse pas ; possible que le mail non plus (pas dans les recherches globales en tout cas). Les types d'index sont les suivants :
Les index doivent être créé avant de peupler l'annuaire, sinon il faut le reconstruire :
sudo systemctl stop slapd sudo slapindex sudo chown -R openldap:openldap /var/lib/ldap/ sudo systemctl start slapd
Et comme dit plus haut, redémarrer un ldap actif peut être très, très long suivant sa taille…
Cependant l'usage des index est à utiliser avec discernement. La construction des index prend de la place sur l'espace disque, et on peut quand même chercher dans les données non indexées (par exemple faire une recherche sur “ou”). On indexe quand il y a beaucoup d'entrées (par exemple quelques milliers d'UID ⇒ là ça devient vital), qu'on fait régulièrement des recherches dessus, voir qu'on utilise des filtres un peu compliqués dans les recherches. Pas besoin quand il y a moins de 1000 entrées ou sur les trucs qu'on cherche rarement.
La blague étant donc que je saurais réellement ce que je dois indexer une fois que mon annuaire sera utilisé et donc peuplé. Il reste donc à trouver les bons index avant que la base ne grossisse trop, afin d'éviter une opération de maintenance trop longue…
Les overlays sont des fonctionnalités supplémentaires qui se rajoutent. (Source)
La doc complète est sur https://www.openldap.org/doc/admin26/overlays.html ; je ne documente ici que ce qui nous est utile.
La configuration demande plusieurs fichiers : un pour l'activation (utile juste une fois), l'autre pour la configuration (donc pouvant changer avec le temps).
Généralement (mais pas toujours) nous avons un module à activer qui va fournir une fonctionnalité de base, puis l'overlay lui-même qui indique comment utiliser les fonctionnalités du module ou ajoute des fonctionnalités à LDAP. Les modules (extensions en .so et .la) sont des bibliothèques compilées pour Openldap. Ils sont généralement dans /usr/lib/ldap/
Certains overlay sont assez essentiels :
memberOf : liste les groupes dont une utilisatrice est membrerefint : cohérence de l'annuaire lors de suppression d'entrées, pour éviter les membres fantômes et les groupes vides par erreur.auditlog : journalisation des modifications. Loguer, c'est bien ! Celui-ci ne logue que les opérations affectant les données (add, delete, modify, modrdn). Ça suffit pour voir quand quelqu'un change son mot de passe.accesslog : journalise tous les accès read et write (donc aussi search, compare, bind,write), et les connexions (échouées ou réussis). Cela peut être utile mais ça va impacter fortement les performances. Donc… pas tout de suite. unique : s'assurer que certaines données sont uniques (UID par exemple, ou pseudo, ou mail).ppolicy : politique de mots de passe (complexité, expiration, verrouillage)constraint : ajoute des contraintes sur les attributs, pour éviter de rentrer n'importe quoi (et donc de mettre en pseudo un truc qui va faire cracher les cms). Cela peut être des contraintes du type “une membre doit renseigner un mail” ou “si Untelle est membre de sysadmin, alors elle doit avoir renseigner aussi son téléphone”Ceux où je ne sais pas encore quand ce sera utile (mais ça le sera très certainement à un moment)
collect : je n'en suis pas absolument sûre mais vu ce que je prévois comme architecture, ça devrait être utile. Cela permet d'aggréger les données, et donc quand on demande par exemple des infos sur “pseudo” on aie aussi plus facilement des données annexes sans multiplier les requêtes (par exemple, tous les groupes dont le pseudo fait partie ?)syncprov : pour répliquer les infos entre serveurs OpenLDAP (et oui, vu le côté critique de la bestiole, on va redonder).translucent : proxy stockant localement certains attributs. Cas d'usage : si un des serveurs a un usage intensif de LDAP, il y aura un ldap sur le serveur en question, avec un cache local, qui se synchronisera avec le reste “quand y'a le temps”. On est sur des questions de latence, bref on verra quand ça commencera à ramer ! Potentiellement le serveur de jeu.J'ai compris ça avec la doc et quelques exemples piochés sur le web et autant dire que je ne suis pas sûre de moi. Donc lisez aussi la doc, et corrigez si j'ai mal traduit/compris.
Pour voir les modules chargés :
ldapsearch -Q -Y EXTERNAL -H ldapi:/// -b cn=config | grep olcModuleLoad
Ou en mode plus verbeux
ldapsearch -Q -Y EXTERNAL -H ldapi:/// -b cn=config '(olcModuleLoad=*)' olcModuleLoad
Pour les overlays activés :
ldapsearch -Q -Y EXTERNAL -H ldapi:/// -b cn=config '(olcOverlay=*)' olcOverlay
On note que le retour peut ressemble à ceci :
olcModuleLoad: {0}back_mdb
olcModuleLoad: {1}ppolicy
olcModuleLoad: {0}memberof.la
Le {0} indique les modules chargés en premier, {1} ensuite, etc. Bref faut pas s'inquiéter des chiffres.
Cela permet de lister plus facilement de quels groupes une utilisatrice est membre.
Créer les documents suivants :
dn: cn=module,cn=config cn:module objectclass: olcModuleList objectclass: top olcmoduleload: memberof.la olcmodulepath: /usr/lib/ldap
dn: olcOverlay=memberof,olcDatabase={1}mdb,cn=config
changetype: add
objectClass: olcMemberOf
objectClass: olcOverlayConfig
objectClass: olcConfig
objectClass: top
olcOverlay: memberof
olcMemberOfDangling: ignore
olcMemberOfRefInt: TRUE
olcMemberOfGroupOC: groupOfNames
olcMemberOfMemberAD: member
olcMemberOfMemberOfAD: memberOf
Puis on injecte ces fichiers :
ldapadd -Y EXTERNAL -H ldapi:/// -f memberof_act.ldif ldapadd -Y EXTERNAL -H ldapi:/// -f memberof_conf.ldif
Pour vérifier la configuration, au choix, une de ces commandes dans le terminal :
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcModuleList" tree /etc/ldap/slapd.d/ ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcmemberOf"
Pour chercher tous les groupes dont Truc est membre :
ldapsearch -xLLL "(uid=truc)" memberof
Cela évitera d'avoir des membres fantômes qui ne sont plus liés à rien. Si une utilisatrice est supprimée, alors elle le sera aussi des groupes dans lesquels elle était. Si un groupe se retrouve vide, l'admin y est automatiquement ajouté (ce qui évitera des erreurs).
Créer les fichiers suivants :
dn: cn=module,cn=config cn: module objectclass: olcModuleList objectclass: top olcmoduleload: refint.la olcmodulepath: /usr/lib/ldap
dn: olcOverlay=refint,olcDatabase={1}mdb,cn=config
objectClass: olcConfig
objectClass: olcOverlayConfig
objectClass: olcRefintConfig
objectClass: top
olcOverlay: refint
olcRefintAttribute: memberof member manager owner
olcRefintNothing: cn=admin,dc=ldap,dc=local
Injection
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f refint_act.ldif ldapadd -Q -Y EXTERNAL -H ldapi:/// -f refint_conf.ldif
Vérification, et vérification de la configuration :
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcModuleList" ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcRefintConfig"
Ce qui pourra être utile pour savoir ce qui s'est passé.
On commence par créer le dossier où cela s'écrira (automatique suivant comment on a configuré ses logs par ailleurs…) :
mkdir -m 755 -p /var/log/openldap touch /var/log/openldap/auditlog.log chmod 755 /var/log/openldap/auditlog.log chown -R openldap:openldap /var/log/openldap
Puis on crée les fichiers :
dn: cn=module,cn=config cn: module objectclass: olcModuleList objectclass: top olcModuleLoad: auditlog.la olcmodulepath: /usr/lib/ldap
dn: olcOverlay=auditlog,olcDatabase={1}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcAuditLogConfig
olcOverlay: auditlog
olcAuditlogFile: /var/log/openldap/auditlog.log
Injection :
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f auditlog_act.ldif ldapadd -Q -Y EXTERNAL -H ldapi:/// -f auditlog_conf.ldif
Vérification :
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcModuleList" ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcAuditLogConfig"
C'est par exemple pour que les UID attribués à chacune soit bien uniques. Ou qu'une adresse mail ne soit liée qu'à une seule personne. Éviter les doublons sur les attributs évitera des erreurs, voir des exploits, et améliore les recherches.
Créer les fichiers suivants :
dn: cn=module,cn=config cn: module objectclass: olcModuleList objectclass: top olcModuleLoad: unique.la olcmodulepath: /usr/lib/ldap
Attention ici, remplacez dc=ldap,dc=local par votre DC. Notez aussi qu'on va forcer l'unicité uniquement sur quelques attributs :
?sub) précise que cela s'applique aux sous-arbres. dn: olcOverlay=unique,olcDatabase={1}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcUniqueConfig
olcOverlay: unique
olcUniqueUri: ldap:///ou=people,dc=ldap,dc=local?uid?sub
olcUniqueUri: ldap:///ou=people,dc=ldap,dc=local?mail?sub
olcUniqueUri: ldap:///ou=people,dc=ldap,dc=local?uidNumber?sub
Injection :
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f unique_act.ldif ldapadd -Q -Y EXTERNAL -H ldapi:/// -f unique_conf.ldif
Vérification :
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcModuleList" ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcUniqueConfig"
Je suis Debuguo MAIS je ne sais pas encore quels contraintes vont être amenées par Oauth et compagnie, et je ne suis pas encore certaine que ces paramètres sont “bons”. Mais après réflexion, ça me semble quand même mieux que rien pour mes tests.
Par ailleurs ma méthode est un peu différente, puisque j'active le module de ppolicy.
La “policy” est donc la politique de mots de passe, y compris : les utilisatrices peuvent-elles changer leurs mots de passe ? Combien d'essais de connexion avant de bloquer ? Etc. Je ne sais pas trop où on définit le type de hash (je vais chercher).
On commence par charger le module ppolicy. Et du coup on vérifie avant tout s'il existe (sinon, voir le tuto de Debuguo, mais chez moi “ça marchait pas”) :
find /usr/lib/ldap -name "*ppolicy*"
Type de retour attendu :
/usr/lib/ldap/ppolicy.so /usr/lib/ldap/ppolicy.la
Pour charger le module, on crée le fichier ldif suivant :
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: ppolicy
Et appliquer :
ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f ppolicy_module.ldif
Pour vérifier la liste des modules (ppolicy doit apparaitre dedans) :
ldapsearch -Q -Y EXTERNAL -H ldapi:/// -b cn=config | grep olcModuleLoad
TODO : c'est beau d'appliquer le module, mais faut quand même définir la politique… Or ça me demande quelques aller-retours entre les tutos compréhensibles (mais peut-être trop datés) et la doc d'Openldap.