Ceci est une ancienne révision du document !
Openldap
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.
Installation
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.
Initialiser LDAP
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
Avoir des logs plus bavards
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 :- -1 : enable all debugging
- 0 : no debugging
- 1 : trace function calls
- 2 : debug packet handling
- 4 : heavy trace debugging
- 8 : connection management
- 16 : print out packets sent and received
- 32 : search filter processing
- 64 : configuration file processing
- 128 : access control list processing
- 256 : stats log connections/operations/results
- 512 : stats log entries sent
- 1024 : print communication with shell backends
- 2048 : print entry parsing debugging
On va faire ceci :
- log.ldif
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.
- /etc/rsyslog.d/ldap.conf
local4.* /var/log/ldap.log
Puis on redémarre rsyslog :
sudo systemctl restart rsyslog
Overlays
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èsreadetwrite(donc aussisearch,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”valsort: ordonne les résultats. Par exemple renseigner certains groupes (genre les assos) avant d'autres (genre les sous-groupes d'assos).
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.
MemberOf
Cela permet de lister plus facilement de quels groupes une utilisatrice est membre.
Créer les documents suivants :
- memberof_act.ldif
dn: cn=module,cn=config cn:module objectclass: olcModuleList objectclass: top olcmoduleload: memberof.la olcmodulepath: /usr/lib/ldap
- memberof_conf.ldif
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 :
- root
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
Cohérence de l'annuaire lors de suppression d'entrées (refint)
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 :
- refint_act.ldif
dn: cn=module,cn=config cn: module objectclass: olcModuleList objectclass: top olcmoduleload: refint.la olcmodulepath: /usr/lib/ldap
- refint_conf.ldif
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"
Enregistrer les modifications de l'annuaire (auditlog)
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 :
- auditlog_act.ldif
dn: cn=module,cn=config cn: module objectclass: olcModuleList objectclass: top olcModuleLoad: auditlog.la olcmodulepath: /usr/lib/ldap
- auditlog_conf.ldif
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"
Unicité des attributs (unique)
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 :
- unique_act.ldif
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 :
- L'uid, qui doit absolument être unique… utilisé dans la base elle-même, c'est du “texte”
- L'uidNumber, qui est forcément un nombre, sert entre autre quand il y a une utilisatrice linux liée (identifiant numérique utilisé par le système)
- le mail (utilisé pour récupérer son mot de passe, mais à voir avec la possibilité d'avoir plusieurs “pseudo” pour une seule personne… Pour le moment, on reste en “unique”.
- La portée (
?sub) précise que cela s'applique aux sous-arbres.
- unique_conf.ldif
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"
Policy
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 :
- ppolicy_module.ldif
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.
Plus de doc
- https://blog.debugo.fr/openldap-serie/ ; je n'ai pas vu l'intérêt de trop redonder ses infos. Il détaille plus de choses sur le fonctionnement de l'annuaire.
- https://www.openldap.org/doc/ : la doc officielle.





