Tutoriel révisé en décembre 2022 / Debian 11
Première partie du tutoriel complet sur l’installation d’un serveur mail. Nous allons y voir la mise en place d’un serveur LDAP destiné à stocker les informations nécessaires au bon fonctionnement de notre serveur de messagerie.
Jusqu’à présent, pour mon serveur de messagerie, je passais par un stockage des comptes dans une base SQL. Ayant mis en place un serveur LDAP, j’ai voulu greffer ma messagerie dessus.
Il faut savoir qu’il n’y a pas une façon unique de faire. Les infos peuvent être organisées autrement, par exemple, les comptes utilisateurs peuvent être dans l’OU mail et différents des comptes présents dans l’OU people. Ou au contraire, on peut rassembler les infos de chaque domaine dans son OU.
L’important au final étant que vos requêtes Ldap puissent sortir les infos, à savoir : les domaines gérés, les alias et les comptes utilisateurs.
Mon modèle n’est pas forcement le plus simple ou le plus adapté à votre cas, mais le changer ne demande que peu d’efforts, l’important étant surtout de comprendre la logique.
I – Installation
Pour le LDAP, deux solutions :
Vous pouvez suivre mon tutoriel complet : OpenLDAP : La série de Tutos, et venir ensuite vous rebrancher ici au chapitre III.
Ou alors, suivre les quelques étapes ci dessous, qui permettent de mettre en place rapidement un petit LDAP fonctionnel.
Pour le serveur LDAP, je pars sur une VM fraîchement installée puis une mise à jour et on installe le paquet slapd et le paquet ldap-utils contenant les outils pour modifier le Ldap.
# apt update && apt upgrade
# apt-get install slapd ldap-utils
On reconfigure le paquet :
# dpkg-reconfigure slapd
La, vous répondez comme cela :
Omit OpenLDAP server configuration?No
DNS domain name:debugo.fr
Organization name?Debugo
Administrator password:PASSWORD
Confirm password:PASSWORD
Database backend to use:MDB
Do you want the database to be removed when slapd is purged?YES
Ensuite, un petit tour dans le fichier /etc/ldap/ldap.conf pour le configurer comme il faut (utilisé par les outils ldap pour modifier le LDAP en ligne de commande) :
BASE dc=debugo,dc=fr URI ldap://IP.MACHINE/
On relance openldap :
# service slapd restart
On test avec :
# ldapsearch -xLLL
II – Rapide configuration
Avant tout, on va créer des répertoires pour stocker nos fichiers :
# mkdir /root/ldap # cd /root/ldap
A – Mdp Admin dans un fichier
Afin d’éviter d’avoir à toujours retaper le mot de passe admin lors des commandes, nous allons l’enregistrer dans un fichier.
On va créer un fichier /root/pwdldap et mettre le mot de passe dedans :
# echo -n "mdpadmin" > /root/pwdldap # chmod 600 /root/pwdldap
On test :
# ldapsearch -x -H ldap://localhost -D cn=admin,dc=debugo,dc=fr -y /root/pwdldap -b dc=debugo,dc=fr
B – Droits d’accès à la configuration du serveur
Par défaut, l’accès à la configuration n’est pas possible en passant par le socket réseau avec le compte admin (et on en aura besoin pour ajouter notre schéma).
Créez le fichier LDIF /root/ldap/acces-conf-admin.ldif, et insérez :
dn: olcDatabase={0}config,cn=config changeType: modify add: olcAccess olcAccess: to * by dn.exact=cn=admin,dc=debugo,dc=fr manage by * break
Injectez :
# cd /root/ldap # ldapmodify -Y external -H ldapi:/// -f acces-conf-admin.ldif
Et l’on peut voir que c’est bon avec :
# ldapsearch -xLLL -H ldap://localhost -D cn=admin,dc=debugo,dc=fr -y /root/pwdldap -b cn=config
C – Compte Viewer
On va ajouter notre compte système viewer dans un fichier viewer.ldif. Ce compte servira à Postfix et Dovecot, pour qu’ils puissent lire les infos du Ldap.
dn: cn=viewer,ou=system,dc=debugo,dc=fr objectClass: simpleSecurityObject objectClass: organizationalRole cn: viewer description: LDAP viewer userPassword: passview
Et on l’injecte :
# ladd -f viewer.ldif
Puis on va modifier les Acls afin de donner le droit au compte viewer de lire les passwords.
Fichier acl.ldif :
dn: olcDatabase={1}mdb,cn=config changetype: modify replace: olcAccess olcAccess: to attrs=userPassword by self write by anonymous auth by dn="cn=viewer,ou=system,dc=debugo,dc=fr" read by dn="cn=admin,dc=debugo,dc=fr" write by * none olcAccess: to dn.base="dc=debugo,dc=fr" by users read olcAccess: to * by self write by dn="cn=admin,dc=debugo,dc=fr" write by * read by anonymous none
On injecte :
# ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f acl.ldif
D – Des alias
Nous allons utiliser énormément les commande fournies par ldap-utils. Du coup, afin d’aller plus vite nous allons créer des alias.
Éditez le fichier /root/.bashrc pour y ajouter :
alias lmodif='ldapmodify -cxWD cn=admin,dc=debugo,dc=fr -y /root/pwdldap' alias lsearch='ldapsearch -xLLL -H ldap://localhost -D cn=admin,dc=debugo,dc=fr -y /root/pwdldap' alias ladd='ldapadd -cxWD cn=admin,dc=debugo,dc=fr -y /root/pwdldap' alias ldel='ldapdelete -cxWD cn=admin,dc=debugo,dc=fr -y /root/pwdldap'
Pour une prise en compte immédiate :
# source /root/.bashrc
Je ne fais qu’effleurer la configuration d’un serveur LDAP. Pour plus de détails, je vous invite fortement à consulter ma série d’articles sur le Ldap.
II – Peuplement de base
On va créer nos OUs de base dans un fichier ou.ldif :
dn: ou=people,dc=debugo,dc=fr ou: people objectClass: organizationalUnit dn: ou=system,dc=debugo,dc=fr ou: people objectClass: organizationalUnit
Et on l’injecte :
# ladd -f ou.ldif
Avant de passer à la création de nos utilisateurs, petit point sur le stockage des mots de passe dans LDAP.
Par défaut, ils sont stockés en clair. Dans la suite du tutoriel, cela pose soucis avec le bind de Dovecot. Puis stocker en clair, c’est pas top.
Il est possible de force le hash avec l’overlay Ppolicy. A vous de configurer cela si vous voulez.
Sinon, à la main :
# slappasswd -h {SSHA}
Renseignez le pass deux fois et vous obtiendrez le hash en SSHA, hash qu’il faudra mettre dans le champ userPassword.
Puis nous passons à un premier utilisateur dans un fichier usertoto.ldif :
dn: uid=toto,ou=people,dc=debugo,dc=fr objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson uid: toto sn: toto givenName: toto cn: toto displayName: toto userPassword: {SSHA}..... mail: toto@debugo.fr
et un second dans un fichier usertata.ldif :
dn: uid=tata,ou=people,dc=debugo,dc=fr objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson uid: tata sn: tata givenName: tata cn: tata displayName: tata userPassword: {SSHA}..... mail: tata@debugo.fr
Pour terminer en injectant les deux :
# ladd -f usertoto.ldif # ladd -f usertata.ldif
III – Explication de l’organisation LDAP
Jusque la, je stocke mes comptes utilisateur. Ceci dit, il me manque des attributs, que je rajouterais via un schéma personnel.
Au niveau domaine, j’en ai deux : debugo.fr et domaine2.fr.
Je gère deux compte mails toto@debugo.fr et tata@debugo.fr. Toutes les autres adresses de debugo.fr et domaine2.fr (par ex ccc@debugo.fr, aaa@domaine2.fr, bbb@domaine2.fr, etc.. seront renvoyés soit vers toto@debugo.fr soit vers tata@debugo.fr.
Pour la gestion domaines et alias, j’ai donc choisi de faire de la sorte :
dc=debugo,dc=fr ou=people,dc=debugo,dc=fr "Stockage de mes utilisateurs" ou=mail,dc=debugo,dc=fr ou=debugo.fr, cn=alias1@debugo.fr,ou=debugo.fr,ou=mail,dc=debugo,dc=fr attr: mailfrom: alias@debugo.fr attr: mailto: toto@debugo.fr etc... ou=domaine2.fr, etc...
Chaque domaine sera une OU dans une nouvelle OU (ou=mail,dc=debugo,dc=fr) créée pour l’occasion.
Et dans chaque OU, je crée des entrées correspondants aux alias, là aussi avec l’aide de schéma supplémentaire
Cette façon de faire est celle que j’ai choisie. Mes domaines auraient très bien pu être non pas des OU mais des entrées, au niveau de l’OU mail et les alias définies par des attributs soit dans ces entrées soit dans les entrées des users… Je vous le disais, on peut vraiment faire comme on veut.
IV – Mise en place
A – Le Schéma
On va créer un fichier schema.ldif :
dn: cn=maildebugo,cn=schema,cn=config objectClass: olcSchemaConfig cn: maildebugo olcAttributeTypes: ( 1.3.6.1.4.1.99999.2.2.20 NAME 'mailaccountquota' DESC 'Quota Mail' EQUALITY caseExactMatch SINGLE-VALUE SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) olcAttributeTypes: ( 1.3.6.1.4.1.99999.2.2.21 NAME 'mailaccountactif' DESC 'Mail Actif' EQUALITY caseExactMatch SINGLE-VALUE SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) olcAttributeTypes: ( 1.3.6.1.4.1.99999.2.2.40 NAME 'mailaliasfrom' DESC 'Mail From' EQUALITY caseExactMatch SINGLE-VALUE SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) olcAttributeTypes: ( 1.3.6.1.4.1.99999.2.2.41 NAME 'mailaliasto' DESC 'Mail To' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) olcAttributeTypes: ( 1.3.6.1.4.1.99999.2.2.42 NAME 'mailaliasactif' DESC 'Alias Actif' EQUALITY caseExactMatch SINGLE-VALUE SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) olcAttributeTypes: ( 1.3.6.1.4.1.99999.2.2.60 NAME 'maildomain' DESC 'Domaine' EQUALITY caseExactMatch SINGLE-VALUE SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) olcAttributeTypes: ( 1.3.6.1.4.1.99999.2.2.61 NAME 'maildomainactif' DESC 'Domaine Actif' EQUALITY caseExactMatch SINGLE-VALUE SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) olcObjectClasses: ( 1.3.6.1.4.1.99999.2.1.20 NAME 'mailaccountdebugo' SUP TOP AUXILIARY MUST ( mailaccountquota $ mailaccountactif)) olcObjectClasses: ( 1.3.6.1.4.1.99999.2.1.40 NAME 'mailaliasdebugo' SUP TOP STRUCTURAL MUST ( cn $ mailaliasfrom $ mailaliasto $ mailaliasactif)) olcObjectClasses: ( 1.3.6.1.4.1.99999.2.1.60 NAME 'maildomaindebugo' SUP TOP AUXILIARY MUST ( maildomain $ maildomainactif))
Vous voyez trois séries de chiffres dans le fichier.
L’OID 1.3.6.1.4.1.99999.2.2.x correspond à la hiérarchie de mes attributs (la branche 1.3.6.1.4.1 est la branche dédiée aux OID privés : voir ici).
Le 99999 devrait être dans l’idéal remplacé par le PEN que vous pouvez obtenir sur cette page. Si vous ne destinez pas votre schéma a être public, ça n’a pas trop d’importance, mais attention à ne pas prendre un numéro déjà existant si un jour vous importez un schéma avec ce numéro, bref, vous voyez le topo. J’ai fais la demande, j’ai eu mon PEN en 72h je crois…
L’OID 1.3.6.1.4.1.99999.2.1.x est sur le même modèle mais définit un objet.
L’OID 1.3.6.1.4.1.1466.115.121.1.15 correspond à la définition d’une directory string (chaîne de caractère), je fais simple et prend ce type de donnée pour mes nouveau attributs.
Ce schéma est au final assez simple, je rajoute trois nouvelles classe d’objets : mailaccountdebugo, maildomaindebugo et mailaliasdebugo (qui à la particularité d’être structural, c’est à dire que ce pourra être une entrée sans ajout d’autre classe (par ex, inetogperson, etc…).
Chaque classe possède des attributs obligatoire (MUST).
L’attribut mailaliasto est le seule à ne pas avoir SINGLE-VALUE, en effet, un alias peut renvoyer vers plusieurs bals.
Le puriste et fin connaisseur des schémas de base d’OpenLdap me dira : « Oui, on peut faire tout ça sans rajouter de nouveaux schémas, en utilisant des attributs qui existent déjà ! »
Oui, mais dans mon cas, cela ne me convenait pas (les alias dans l’entrée de l’user, pourquoi pas, mais pour ensuite dire s’ils sont actifs, etc.., c’est compliqué). Et puis c’est l’occasion de faire des manipulations sur le Ldap…
Bref, on va ajouter notre schéma :
# ladd -f schema.ldif
Les noms des attributs est libre, mais il faudra alors prendre soin de modifier les futurs fichiers d’appel que l’on va créer en conséquence.
B – Les OUs pour nos domaines
Ensuite, on va créer nos nouvelles OUs dans un fichier oumail.ldif :
dn: ou=mail,dc=debugo,dc=fr ou: mail objectClass: organizationalUnit dn: ou=debugo.fr,ou=mail,dc=debugo,dc=fr ou: debugo.fr objectClass: organizationalUnit objectClass: maildomaindebugo description: Domaine mail primaire maildomain: debugo.fr maildomainactif: YES dn: ou=domaine2.fr,ou=mail,dc=debugo,dc=fr ou: domaine2.fr objectClass: organizationalUnit objectClass: maildomaindebugo description: Domaine mail secondaire maildomain: domaine2.fr maildomainactif: YES
On injecte :
# ladd -f oumail.ldif
C – Les Alias
Puis on passe à la création des entrées pour les alias dans un fichier alias.ldif :
dn: cn=postmaster@debugo.fr,ou=debugo.fr,ou=mail,dc=debugo,dc=fr objectclass: mailaliasdebugo cn: postmaster@debugo.fr mailaliasfrom: postmaster@debugo.fr mailaliasto: toto@debugo.fr mailaliasactif: YES dn: cn=postmaster@domaine2.fr,ou=domaine2.fr,ou=mail,dc=debugo,dc=fr objectclass: mailaliasdebugo cn: postmaster@domaine2.fr mailaliasfrom: postmaster@domaine2.fr mailaliasto: toto@debugo.fr mailaliasactif: YES dn: cn=testalias@debugo.fr,ou=debugo.fr,ou=mail,dc=debugo,dc=fr objectclass: mailaliasdebugo cn: testalias@debugo.fr mailaliasfrom: testalias@debugo.fr mailaliasto: toto@debugo.fr mailaliasto: tata@debugo.fr mailaliasactif: YES
A vous bien sur de faire vos propres alias en fonction de vos besoins.
On injecte :
# ladd -f alias.ldif
D – Les nouveaux attributs des utilisateurs
Et pour finir, on va rajouter les attributs de la classe mailaccountdebugo à nos utilisateurs.
Fichier attrtoto.ldif :
dn: uid=toto,ou=people,dc=debugo,dc=fr changetype: modify add: objectclass objectclass: mailaccountdebugo - add: mailaccountquota mailaccountquota: 0 - add: mailaccountactif mailaccountactif: YES
Fichier attrtata.ldif :
dn: uid=tata,ou=people,dc=debugo,dc=fr changetype: modify add: objectclass objectclass: mailaccountdebugo - add: mailaccountquota mailaccountquota: 0 - add: mailaccountactif mailaccountactif: YES
On injecte ces deux fichiers :
# ladd -f atttoto.ldif # ladd -f attrtata.ldif
Par la suite, pour ajouter un nouvel utilisateur, on pourra bien évidemment tout faire en un bloc :
dn: uid=new,ou=people,dc=debugo,dc=fr objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson objectclass: mailaccountdebugo uid: new sn: new givenName: new cn: new displayName: new userPassword: {SSHA}..... mail: new@debugo.fr mailaccountquota: 0 mailaccountactif: YES
V – Conclusion
On peut déjà tester en listant par exemple les domaines gérés :
# lsearch -b "ou=mail,dc=debugo,dc=fr" "(&(objectClass=maildomaindebugo))" ou
Ou encore, pour savoir vers quelle bal renvoie l’alias postmaster@debugo.fr :
# lsearch -b "ou=mail,dc=debugo,dc=fr" "(&(objectClass=mailaliasdebugo)(mailaliasfrom=postmaster@debugo.fr))" mailaliasto
Voila qui termine la partie LDAP pour l’utilisation avec un serveur de messagerie. Je vous invite encore une fois à consulter mon tutoriel sur LDAP qui vous expliquera également comment modifier, supprimer des données dans l’annuaire (ainsi que plein d’autres choses ! )
Et sinon, on peut passer à la suite avec la mise en place de Postfix…
Hello,
Merci pour ce tuto complet, c’est vraiment riche en info et donne une « base » très complète pour monter sa propre installation.
Par contre, j’ai une question : quel est l’intérêt de ldap par rapport à une base mysql ? D’autant qu’on perd ici la puissance d’outils comme postfixadmin (qui fonctionne avec une base mysql).
Salutation,
Merci pour le compliment.
Pourquoi Ldap ?
D’une, car c’est assez peu documenté, et comme l’occasion fait le larron 😉
De deux, mon référentiel utilisateur est en LDAP depuis longtemps, coupler la messagerie était le truc qui me manquait.
Coucou, tout d’abord, merci pour ce tuto qui a du te prendre énormément de temps et c’est un pure délice de voir sur la toile des gens qui donnent une mine de renseignements…
Alors je suis en train de débuter l’install et j’ai déjà deux mots de passe différents : un pour l’admin lorsque l’on installe ldap, et ensuite un autre lorsque l’on procède à la reconfiguration avec la commande dpkg.
J’utilise des mots de passe super complexes incluant des caractères spéciaux, des majuscules, des minuscules, des chiffres et mes deux mots de passe en sont équipés…
au tout premier test, après avoir enregistré le pwdldap dans mon /root/, j’ai droit à l’erreur suivante :
ldap_bind: Invalid credentials (49)
J’a essayé avec les 2 mots de passe enregistrés mais rien à faire…
J’ai bien entendu remplacé debugo et fr par mes infos… j’ai sur mon serveur changé les hostname et hosts mais je n’ai pas encore le domaine qui est actuellement en procédure de transfert. D’autre part, en dehors des ports utiles pour que mon serveur fonctionne, tous les ports sont fermés
En espérant que tu pourras m’aider, j’aimerais bien continuer ton tuto, merci 😉
Salut et merci.
Bon, question bete, mais avec le mdp dans la commande, ca fonctionne ?
Si oui, ptet un probleme d’encodage dans le fichier
Alors je viens de galérer un bon petit moment à comprendre pourquoi le truc ne fonctionnait pas, il manque la commande : service slapd restart lorsque l’on touche à la config dans le premier test, sinon on a droit à une jolie erreur…
De plus j’ai rectifié le mot de passe sans caractères spéciaux et j’en ai utilisé un bien plus court… j’ai réessayé avec un mdp d’environ 20 caractères avec Maj, min et chiffres et ça ne passe pas, toujours cette erreur ldap_bind: Invalid credentials (49). Au niveau sécurité, c’est trop léger…
J’abandonne le tuto pour deux raisons, la première est qu’une install avec mysql et postfixadmin est tout simplement géniale car très pratique au niveau de la gestion des domaines ainsi que des utilisateurs, et au niveau sécurité ldap ne me semble pas un choix judicieux car manque d’informations globales au niveau des mots de passe que l’on peut utiliser et leur longueur. De plus, du plaintext en mot de passe dans les fichiers de conf, pour un serveur en prod, ça pose problème, mais pas que, car la longueur et la force des mots de passe est toute aussi importante, surtout lorsque l’on se connecte à un compte admin.
Enfin dernier sujet, cela concerne les ports qui sont utilisés avec LDAP, j’ai du aller chercher à l’extérieur du tuto pour savoir quel(s) port(s) ouvrir, ce sont ces petites choses qui manquent à ce tuto pour lui permettre de prendre son envol. Pour toucher un large publique, il est souvent nécessaire de « tenir les gens par la main » afin que tout ce qui est testé fonctionne du premier coup, la chose étant rendue compliquée par des MAJ logicielles relativement fréquentes j’en conviens.
Ceci dit, je reviendrai faire un tour par ici histoire de voir si le tuto a été étoffé, mais également pour échanger nos points de vue.
Bonne suite !
Resalut 😉
Oui, ça peut m’arriver de temps en temps d’oublier quelques commandes basiques à droite à gauche, merci du retour 😉
Pour la prod, bien évidement qu’on ne maintient pas le fichier avec le mdp !
Mais tu as raison, je rajouterais cette recommandation.
Pour plus d’infos sur ldap, j’ai toute une série de tuto disponibles.
En ce qui concerne les ports, et bien, la, je ne les ai pas mentionné car j’en parle déjà ailleurs et j’ai pensé que si une personne a besoin d’ouvrir des ports, elle saura trouver facilement ceux du ldap.
Mais bon, ces points à part, je t’invite à continuer a tester le ldap comme référentiel postfix, c’est bien plus souple au final que le mysql.
Après, niveau sécurité, le ldap est très robuste, RAS.
Et sinon, rassure toi, je tiens déjà beaucoup les gens en main, mais à un moment, il faut que j’arrive à faire la part des choses entre ce qui est sous entendu et ce qu’il faut dire.
Un peu plus loin dans le tuto (partie Dovevot), tu peux voir comment je prend les gens en main justement, dans le sens ou je montre pourquoi j’arrive a telle config, et je ne fais pas que de la donner sans explication.
Mais cette série à été hyper longue à rédiger, et oui, je pourrais sur chaque partie pour rajouter quelques petites choses…
Si tu as d’autres remarques, n’hésites surtout pas !
Cdt,
Niko