25 avril 2024

Tuto Messagerie, partie I : LDAP

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

6 réflexions sur « Tuto Messagerie, partie I : LDAP »

  1. 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).

    1. 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.

  2. 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 😉

    1. 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

  3. 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 !

    1. 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

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *