TP4: installation LDAP
On continue toujours avec nos VMs serveur et client.
Pour l’instant on travaille seulement sur la VM serveur.
Exercice 1: Préparer le terrain
Avant d’installer et configurer le serveur de mail, préparons le terrain.
Configurer le DNS
Ajoutez un alias DNS ldap
pour votre serveur, qu’on utilisera pour indiquer le
serveur ldap à utiliser.
Avez-vous pensé à mettre à jour le serial ?
Vérifiez que l’alias fonctionne.
Firewall
Dans notre cas on va commencer par interroger en local, donc le firewall ne gênera pas au début.
Ajouter le dépôt EPEL
LDAP n’est pas fourni dans les packages de base d’AlmaLinux, il faut activer le dépôt EPEL et activer la partie powertools:
# dnf install epel-release
# dnf config-manager --set-enabled powertools
Essentiellement, cela dépose dans /etc/yum.repos.d
des bouts de configuration
pour ajouter l’adresse du dépôt EPEL, ainsi qu’une clé GPG pour vérifier les
signatures des packages récupérés depuis ce dépôt.
Exercice 2: openldap
Installation de base
Commençons par installer le serveur et les clients LDAP:
# dnf install openldap-servers openldap-clients
Et activons le serveur (qui s’appelle en fait slapd)
# systemctl enable --now slapd
La configuration d’openldap ne se fait plus dans des fichiers, il faut utiliser les outils ldap (vous trouverez peut-être encore des tutoriels sur Internet qui font modifier des fichiers de configuration de slapd, ce n’est plus valide)
Commençons par injecter les classes que l’on utilisera:
# ldapadd -H ldapi:/// -Y EXTERNAL -f /etc/openldap/schema/cosine.ldif
# ldapadd -H ldapi:/// -Y EXTERNAL -f /etc/openldap/schema/nis.ldif
# ldapadd -H ldapi:/// -Y EXTERNAL -f /etc/openldap/schema/inetorgperson.ldif
Configuration
Regardons la configuration actuelle, qui est placée dans une partie cn=config
qui n’est pas affichée par défaut:
# ldapsearch -H ldapi:/// -Y EXTERNAL -b 'olcDatabase={2}mdb,cn=config'
On peut y voir que le domaine utilisé est actuellement my-domain.com
, on
voudrait changer cela. Pour cela il faut modifier les attributs correspondant de
l’objet 'olcDatabase={2}mdb,cn=config'
. On pourrait taper tout en interactif,
mais le risque de typo est grand. Il vaut donc mieux préparer un fichier
modify.ldif
contenant les modifications voulues:
dn: olcDatabase={2}mdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=adsillh,dc=local
dn: olcDatabase={2}mdb,cn=config
changetype: modify
replace: olcRootDN
olcRootDN: cn=Manager,dc=adsillh,dc=local
Le format est un peu verbeux…
Avec dn
on indique l’objet à modifier.
Avec changetype
on indique si on veut modifier (modify
) un objet existant ou en ajouter (add
) ou en enlever (delete
). Ici on modifie un objet existant.
Avec replace
on indique qu’on veut remplacer un attribut existant (on pourrait aussi en ajouter (add
) ou enlever (delete
)).
Et enfin on donne la nouvelle valeur de l’attribut.
Ici on fait cela deux fois. On pourrait aussi faire une seule opération
contenant deux modifications du même objet en séparant avec -
plutôt qu’une
ligne vide:
dn: olcDatabase={2}mdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=adsillh,dc=local
-
replace: olcRootDN
olcRootDN: cn=Manager,dc=adsillh,dc=local
Une fois le fichier modif.ldif
préparé au propre, on peut l’appliquer à la base:
# ldapmodify -H ldapi:/// -Y EXTERNAL -f modif.ldif
On peut revérifier la configuration avec la commande ldapsearch
pour constater que cela a bien eu effet.
Un autre point intéressant à noter dans la configuration, c’est l’attribut
olcDbIndex
qui indique sur quels attributs les objets sont indexés,
c’est-à-dire que les requêtes de recherche seront très efficaces sur ces
attributs. De manière peu étonnante, on retrouve ce qui est typiquement
cherché: cn
et mail
.
Permissions
Pour se simplifier la vie, on va permettre à root de modifier la base, et aux autres de juste lire, sauf le mot de passe:
dn: olcDatabase={2}mdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: {0}to attrs=userPassword by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write by anonymous auth by * none
olcAccess: {1}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write by * read
La première règle ({0}
) précise d’abord les droits pour l’attribut
userPassword
: pour root (uid 0) il a droit d’écriture (et donc lecture
aussi), pour les clients non encore connectés il peut servir à s’authentifier,
et pour les autres ils ne peuvent rien en faire.
La deuxième règle ({1}
) préciser les droits pour les autres attributs: root
a le droit d’écriture, les autres ont le droit de lecture.
On peut injecter, et l’on peut vérifier que cela a bien été mis à jour.
Exercice 3: Créer une hiérarchie de base
Base de la hiérarchie
Maintenant que l’on peut écrire dans la base LDAP, créons une simple
hiérarchie dc=adsillh,dc=local
avec juste des utilisateurs dedans. On
prépare le fichier add.ldif
pour ajouter les objets conteneurs:
dn: dc=adsillh,dc=local
objectClass: top
objectClass: dcObject
objectclass: organization
o: LPro ADSILLH
dc: adsillh
dn: ou=Etudiants,dc=adsillh,dc=local
objectClass: organizationalUnit
ou: Etudiants
Ici on n’a pas mis de changetype
, car l’on injecte plutôt avec ldapadd
qui est fait pour créer des objets, la syntaxe de add.ldif
est différente du modif.ldif
:
# ldapadd -H ldapi:/// -Y EXTERNAL -f add.ldif
Un utilisateur
On peut ajouter un objet pour représenter un étudiant:
dn: cn=Toto Lapin,ou=Etudiants,dc=adsillh,dc=local
cn: Toto Lapin
givenName: Toto
sn: Lapin
uid: toto
uidNumber: 10000
gidNumber: 10000
homeDirectory: /home/toto
mail: toto@adsillh.local
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: posixAccount
On a donné à l’étudiant la classe posixAccount
pour remplir tous les champs
utiles à ce qu’il ait un compte unix, et la classe inetOrgPerson
pour remplir
le champ mail
(i.e. qu’il “existe” sur Internet (inet organization
)).
Retrouvez la définition de la classe inetOrgPerson
dans
/etc/openldap/schema/*.schema
, constatez que la classe permet d’inclure
beaucoup d’informations, mais n’en exige aucune :)
On peut vérifier l’ajout en regardant toute la base:
# ldapsearch -H ldapi:/// -Y EXTERNAL -b dc=adsillh,dc=local
On peut voir les opérations dans le log:
# journalctl -u slapd
Dans l’exercice suivant, on va vouloir retrouver les étudiants à partir de
leur uid
, ce sera donc intéressant d’indexer la base sur l’attribut uid
.
Retournez voir la configuration de LDAP, on y voit l’indexation activée actuellement:
olcDbIndex: objectClass eq,pres
olcDbIndex: ou,cn,mail,surname,givenname eq,pres,sub
Modifiez donc cet attribut olcDbIndex
pour ajouter uid
à côté de givenname
.
Exercice 4: authentification par LDAP
On veut maintenant utiliser LDAP pour gérer les utilisateurs Unix
RedHat utilise SSSD (System Security Services Daemon) pour gérer les utilisateurs. Il faut installer la partie ldap:
# dnf install sssd-ldap oddjob-mkhomedir
Et créer un fichier de configuration /etc/sssd/sssd.conf
pour configurer l’utilisation de LDAP (oui, les %2f
sont faits exprès) :
[domain/default]
id_provider = ldap
autofs_provider = ldap
auth_provider = ldap
chpass_provider = ldap
ldap_uri = ldapi://%2fvar%2frun%2fldapi
ldap_search_base = dc=adsillh,dc=local
[sssd]
services = nss, pam, autofs
domains = default
[nss]
homedir_substring = /home
Il faut protéger le fichier contre la lecture sinon sssd
n’en veut pas:
# chmod 600 /etc/sssd/sssd.conf
On peut démarrer sssd et activer l’authentification via sssd:
# systemctl restart sssd
# authselect select sssd with-mkhomedir --force
On peut vérifier que notre utilisateur est reconnu désormais, avec les uid/gid que l’on avait choisis:
# id toto
On peut lui donner un mot de passe
# ldappasswd -H ldapi:/// -Y EXTERNAL -S cn="Toto Lapin",ou=Etudiants,dc=adsillh,dc=local
Et l’on peut se logguer en tant que toto
! Sauf qu’il n’a pas encore de
/home/toto
, activons le service qui peut le créer à la volée:
# systemctl enable --now oddjobd
En se reloggant, cette fois on a bien un home !
L’utilisateur peut maintenant aussi lui-même se connecter à ldap pour lire des informations:
$ ldapsearch -H ldapi:/// -x -W -D cn="Toto Lapin",ou=Etudiants,dc=adsillh,dc=local -b dc=adsillh,dc=local
Ajoutez un autre utilisateur tata
dans LDAP, ayant le même gidNumber
mais
un uidNumber
différent (et avec le login et prénom corrigés), constatez que
son compte Unix tata
est disponible immédiatement.
Constatez que l’on peut récupérer l’un des deux utilisateurs seulement en utilisant un filtre:
# ldapsearch -H ldapi:/// -Y EXTERNAL -b dc=adsillh,dc=local uid=toto
Essayez un autre filtre, essayez de filtrer sur gidNumber
pour constater que
vous récupérez bien les deux.
Essayons de changer la fiche de l’utilisateur toto
: modifiez l’attribut
gidNumber pour y mettre 20000. Relancez id toto
, constatez que cela n’a pas
changé ! En effet sssd
utilise un cache pour éviter de solliciter le
serveur LDAP en permanence. On peut vider le cache avec sss_cache -E
et id
toto
devrait désormais donner la réponse mise à jour.
Appelez id tata
, puis supprimez l’objet LDAP de tata
, et appelez id tata
de nouveau. Essayez de vous logguer avec. Le cache ne voit pas la suppression
non plus, flushez-le pour forcer la disparition du compte unix. Mais son home
lui n’est pas supprimé (c’est rarement une bonne idée de supprimer des
données automatiquement de toutes façons)
Exercice 5 (bonus): TLS
On a utilisé une connexion locale ldapi
pour se simplifier la vie. Mais pour
que l’utilisateur puisse être reconnu sur la VM client aussi, il faut utiliser
une connexion TCP/IP, et donc chiffrer pour éviter que les mots de passe soient
communiqués en clair.
Configurer des clés
Contrairement à postfix/dovecot, les clés ne sont pas faites automatiquement.
Il faut les générer, les poser à un endroit, et s’assurer que slapd
peut
lire la clé privée.
Pour les questions que pose openssl
on peut laisser les valeurs par défaut
en validant tout avec juste entrée
, sauf pour la question
Common Name
qui doit être répondue avec ldap.adsillh.local
# cd
# openssl req -x509 -nodes -newkey rsa:2048 -keyout ldap.key -out ldap.crt -days 365
# mv ldap.key /etc/openldap/certs/
# mv ldap.crt /etc/openldap/certs/
# chown ldap:ldap /etc/openldap/certs/ldap.key
# restorecon /etc/openldap/certs/*
Et l’on peut modifier la configuration ldap:
dn: cn=config
changetype: modify
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/openldap/certs/ldap.crt
-
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/openldap/certs/ldap.key
-
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/certs/ldap.crt
Vérifiez bien que le ldapmodify
ne produit pas d’erreur.
Dans /etc/openldap/ldap.conf
, on configure la partie client LDAP. Sur votre VM serveur
mettez-le à jour pour pointer sur votre serveur ldap:
BASE dc=adsillh,dc=local
URI ldaps://ldap.adsillh.local
TLS_CACERT /etc/openldap/certs/ldap.crt
Tester
Vous devriez désormais pouvoir vous connecter en ldap://
avec l’utilisateur
toto
:
$ ldapsearch -H ldap://ldap.adsillh.local -x -W -D cn="Toto Lapin",ou=Etudiants,dc=adsillh,dc=local -b dc=adsillh,dc=local
Mais aussi en ldaps://
$ ldapsearch -H ldaps://ldap.adsillh.local -x -W -D cn="Toto Lapin",ou=Etudiants,dc=adsillh,dc=local -b dc=adsillh,dc=local
Il ne reste qu’à ouvrir le firewall:
# firewall-cmd --zone=work --add-service=ldaps --permanent
# firewall-cmd --reload
Passez sur la VM client,
- copiez-y la clé publique
ldap.crt
du serveur, - Lancez
restorecon /etc/openldap/certs/*
- configurez
ldap.conf
de la même façon que sur le serveur - et testez-y
ldapsearch
(en ldaps seulement).
Authentification par LDAP
Configurez l’authentification des utilisateurs Unix par LDAP sur la VM client aussi.
Cette fois pour la configuration sssd.conf
il faut utiliser l’url
ldaps://ldap.adsillh.local
Vérifiez id toto
et essayez de vous logguer en tant que toto
Un nouveau venu
Ajoutez un nouvel utilisateur dans LDAP. Vérifiez qu’immédiatement il obtient un compte Unix partout, et une adresse email.
Exercice 6: Dovecot + LDAP
We will now connect Dovecot to our LDAP server, so that users can have an email
address without necessarily having a Unix account (that’s the typical case of a
mail hosting service). In that case, we can configure postfix
to give mails to
dovecot
via virtual_transport
, and configure dovecot
to connect to LDAP
and translate LDAP attributes into mail configuration (where to put mails, which
password use for IMAP/POP3 authentication, etc.)
Create LDAP users for daemons
In general one prefers to create a separate LDAP object in a separate ou
, with
its own password, and add permissions for it to read the LDAP database.
Create a ou=daemons,dc=adsillh,dc=local
LDAP object (like you created ou=Etudiants
).
Create a uid=dovecot,ou=daemons,dc=adsillh,dc=local
LDAP object (like we
created Toto Lapin
), with only the inetOrgPerson
class (so that it can just
have a userPassword
field). You can use Dovecot
as cn
and Mailbox
as
sn
. Note that we have used uid=dovecot
and not cn=dovecot
, and
ou=daemons
and not ou=Etudiants
Give a password to this object (like we gave one to Toto Lapin
)
Note that this is a LDAP user, not a Unix user. To test that this LDAP user can indeed connect to the LDAP server, you can use
$ ldapsearch -H ldapi:/// -x -W -D uid=dovecot,ou=daemons,dc=adsillh,dc=local -b dc=adsillh,dc=local
You will notice that it cannot read the userPassword
field, it’s indeed a different LDAP user than the root user, with different privileges.
Create vmail hosting
Even if we don’t want to have a Unix account for each mail account, we need a
Unix account for hosting the mails, which we will call vmail
. Let’s create
this user and check that its home is completely protected:
# adduser --uid 5000 vmail
# ls -ld /home/vmail
Add virtual mail support in postfix
Let’s now tell postfix in main.cf
to pass mails to dovecot instead of storing
them itself.
Look for the definition of mydestination
, to enable the version that does
not include $mydomain
And add this at the end to tell postfix to just relay @adsillh.local
mails to dovecot:
## Virtual mailbox settings
virtual_mailbox_domains = adsillh.local
virtual_transport = lmtp:unix:/run/dovecot/lmtp
Add virtual mail support in dovecot
Let’s now explain dovecot how to check LDAP and where to store incoming mails.
In conf.d/10-auth.conf
we can simply enable including auth-ldap.conf.ext
Read that last file, to see that it refers to /etc/dovecot/dovecot-ldap.conf.ext
which we can now create, to tell the dovecot ldap driver how to access LDAP:
# Version to use
ldap_version = 3
uris = ldapi:/// # Which server to connect to, here local
# which LDAP object & password to use
dn = uid=dovecot,ou=daemons,dc=adsillh,dc=local
dnpass = toto
# which part of LDAP to look into
base = ou=Etudiants,dc=adsillh,dc=local
scope = subtree
# What to check and how to translate
user_filter = (&(objectClass=inetOrgPerson)(mail=%u))
user_attrs = =home=/home/vmail/%{ldap:mail}
pass_filter = (&(objectClass=inetOrgPerson)(mail=%u))
pass_attrs = =password=%{ldap:userPassword}
In the filters, we have told that we look for objects that have the MailUser
class, and (&
) that have the email address (mail
attribute) that is being looked
for. We then tell dovecot to use the mail
attribute from ldap
(%{ldap:mail}
), and prepend /home/vmail/
to obtain the home
for the
user, where mails will be put into. The password is directly taken from the
userPassword
LDAP attribute.
In conf.d/10-mail.conf
look for mail_location
, and uncomment to force using Maildir
:
mail_location = maildir:~/Maildir
also, set these to force using the vmail
user for the maildir:
mail_uid = 5000
mail_gid = 5000
For security we also prefer to restrict dovecot to read/writing mails from there:
valid_chroot_dirs = /home/vmail
Last but not least, we also need to make SELinux allow dovecot
to access
/var/run/ldapi
to connect to LDAP (we could also use ldaps, but ldapi is more efficient and does not require setting up TLS):
# setsebool -P authlogin_nsswitch_use_ldap 1
Test with a user with mail but no unix account
Create a cn=Titi Lapin,ou=Etudiants,dc=adsillh,dc=local
LDAP object similarly
to what we did for other students, but without the posixAccount
class, and
thus not the homeDirectory
, uid
, uidNumber
, gidNumber
attributes, but
give it titi@adsillh.local
as mail
attribute.
Try to send a mail to it, and look in /home/vmail/titi@adsillh.local/
If it is not there, you can check mailq
to see it in the queue,
journalctl -u postfix
to see if it’s postfix that has troubles,
and journalctl -u dovecot
to see if it’s dovecot that has troubles.
After fixing something in the config, remember to restart the corresponding
daemon, and use postqueue -f
to tell postfix to retry delivering the mail.
Reading mail (Bonus)
If you try to run
# ldapsearch -H ldapi:/// -x -W -D uid=dovecot,ou=daemons,dc=adsillh,dc=local
You will see that we do not get the userPassword
attributes. Indeed, we had
only permitted root to access them.
Modify the olcAccess
attribute of {2}mdb,cn=config
, to add
by dn.base=uid=dovecot,ou=daemons,dc=adsillh,dc=local read
Check ldapsearch
again, now you should see the userPassword
attributes.
Set a password for cn="Titi Lapin"
On the client VM, change .muttrc
to use the titi@adsillh.local
login instead
of admin
(yes, there are now two @
).
You should be able to read the mail you have sent.
Try to send a mail, that should work too!
Aliases (Bonus)
Some users may want to have several email addresses that all end up in the same mailbox. We can tell postfix to perform the rewriting through virtual mapping before giving the mail to dovecot.
Let’s first inject the definition for the inetLocalMailRecipient
class that we
can use for this:
# ldapadd -H ldapi:/// -Y EXTERNAL -f /etc/openldap/schema/misc.ldif
Add to cn=Titi Lapin
this:
objectClass: inetLocalMailRecipient
mailLocalAddress: tititi@adsillh.local
mailLocalAddress: titititi@adsillh.local
Create a uid=postfix,ou=daemons,dc=adsillh,dc=local
LDAP object and give it a password.
We can now make postfix look for these aliases in addition to what we already
defined in main.cf
:
virtual_alias_maps = hash:/etc/postfix/virtual, ldap:/etc/postfix/virtual_aliases.cf
And in virtual_aliases.cf
:
## Postfix-LDAP settings - aliases
version = 3
server_host = ldapi:///
bind = yes
bind_dn = uid=postfix,ou=daemons,dc=adsillh,dc=local
bind_pw = toto
search_base = ou=Etudiants,dc=adsillh,dc=local
scope = sub
query_filter = (&(objectClass=inetLocalMailRecipient)(mailLocalAddress=%s))
result_attribute = mail
The meanings of the fields should now be very familiar :)
Note that we do not need to give postfix access to user passwords, that’s even better for security.
Restart postfix, try to send a mail to tititi@adsillh.local
, you should see in
maillog
that it is first rewritten to titi@adsillh.local
before giving it to
dovecot for delivery. And titititi@adsillh.local
should be working at well.
Change the mail
attribute of cn=Titi Latin
to your personal address. Try
to send a mail to tititi@adsillh.local
, you should be receiving it :) That
allows to automatically forward mails to another email domain. You will however
of course need to add a mailLocalAddress: titi@adsillh.local
for that address
to become valid again.
Note: redondance
Il est possible d’installer un deuxième serveur LDAP. La mise en place de la synchronisation est un peu délicate car il y a des parties de configuration propres à chaque serveur, et le reste doit être répliqué. Une fois le deuxième serveur LDAP, on peut pointer les clients vers les deux serveurs LDAP pour obtenir la redondance.