Activité Déploiement d'un serveur OpenLDAP
Consigne
- Avant de commencer l’activité :
- Lire la mise en situation, prendre connaissance des objectifs, du résultat attendu, et des ressources à disposition.
- Lire une première fois en entier la section Mise en route.
- Effectuez le travail décrit dans la section Mise en route et effectuez les tâches demandées dans la section Tâches.
Mise en situation
Votre équipe est chargée de centraliser l’authentification des utilisateur·rice·s sur les serveurs Linux et les applications Web. Après une phase d’analyse, il a été décidé de mettre en place un système d’annuaire à haute disponibilité avec OpenLDAP. La tâche qui vous a été confiée est d’installer un premier serveur OpenLDAP en apportant un soin particulier aux aspects de sécurité.
Objectifs
À la fin de ce travail, en ayant accès au web, vous devez :
- Être capable d’installer et configurer un serveur OpenLDAP.
- Connaître les principaux composants de OpenLDAP (
slapd
, fichier de configuration, etc.) - Connaître l’importance du protocole ldaps pour l’utilisation de l’authentification simple.
- Connaître la notion de schéma et la manière dont les classes d’objets et les types d’attributs sont décrits dans un fichier LDIF.
- Connaître l’existence des commandes de maintenance de OpenLDAP (
slapcat
,slapadd
,slapmodify
,slapindex
,slapschema
, etc.) - Être capable d’utiliser les commandes usuelles de OpenLDAP (
ldapsearch
,ldapadd
,ldapmodify
,ldapdelete
,ldapmodrdn
, etc.) - Être capable de décrire des objets dans le format LDIF (person, OU, etc.)
- Être capable d’effectuer des recherches et d’écrire un filtre LDAP.
- Être capable de démarrer, redémarrer, stopper et interroger le statut d’un service (daemon) sous Linux avec systemd ou avec init.d (System V Unix init system).
Résultat attendu
- Un rapport qui comprend :
- Une marche à suivre permettant l’installation et la configuration de OpenLDAP.
- Les réponses aux questions
- Une machine virtuelle Linux (Ubuntu server 22.04) avec OpenLDAP configuré.
Ressources
Documents:
- LDAP installation
- LDAP and Transport Layer Security (TLS)
- How to write LDAP search filters
- LDAP Filters
- The String Representation of LDAP Search Filters (RFC 2254)
Matériel:
- Un serveur Linux avec Ubuntu server 22.04 :
- le nom complet est
svr-m159-$NUM-ldap-01.lab.epai-ict.ch
($NUM est le numéro de la machine, en initialisant une variable d’environnement NUM, les commandes peuvent être exécutées telles quelles). - le nom d’utilisateur·rice est
admin
- le numéro de la machine et le mot de passe sont les mêmes que pour l’activité précédente
- le nom complet est
Mise en route
Connexion à la machine Ubuntu server
La machine que vous devez configurer dans cette activité est serveur Ubuntu 22.04 situé dans un centre de données distant. Le système n’a pas d’interface utilisateur graphique, mais il a un serveur SSH (Secure Shell) configuré pour accepter l’authentification par mot de passe et par clé publique.
Pour vous connecter à la machine, vous avez besoin d’un client SSH sur votre ordinateur. En principe, dans les systèmes Unix-like tels que macOS et GNU/Linux, le ssh fait partie des outils de base. Sous Windows 10 et 11, le client OpenSSH est disponible dans les fonctionnalités facultatives. Lancez la commande ssh -V
dans une fenêtre de terminal pour vous assurer qu’il est correctement installé. Si cela ne fonctionne pas, corrigez le problème avant de poursuivre.
Pour établir une connexion SSH, on utilise la commande ssh
en spécifiant le nom de l’utilisateur·rice distant et le fully qualified name (FQDN) ou l’adresse IP de la machine séparée par une arobase, par exemple:
1
ssh admin@svr-m159-$NUM-ldap-01.lab.epai-ict.ch
Il existe plusieurs méthodes d’authentification. Les plus courantes sont : l’authentification par mot de passe et l’authentification par clé publique. Dans le premier cas, le client SSH demande à l’utilisateur·rice de saisir son mot de passe et l’utilise pour résoudre le challenge envoyé par le serveur. Dans le second cas, le client SSH essaie de résoudre le challenge en utilisant tour à tour chacune des clés publiques stockées dans le répertoire .ssh ou qui ont été chargées dans l’agent ssh (ssh-agent
) avec la commande (ssh-add
).
La méthode d’authentification par clé publique offre non seulement un niveau de sécurité plus élevé, mais aussi un plus grand confort d’utilisation en évitant d’avoir à saisir le mot de passe à chaque fois. Pour utiliser cette méthode, la première étape consiste à générer une paire de clés asymétriques avec la commande ssh-keygen
. Le type de clé est le plus souvent RSA (avec une longueur de 2048 bits ou plus) ou Ed25519. Si vous avez déjà une paire de clés, utilisez-la. Dans le cas contraire, exécutez la commande ci-dessous dans une fenêtre de terminal sans élévation de privilèges:
1
ssh-keygen -t rsa -b 2048
Durant le processus de création, la commande vous invite à spécifier le nom et le chemin des fichiers, et à saisir un mot de passe pour la clé privée. Il est recommandé d’accepter les réponses suggérées et, pour faciliter l’utilisation, de laisser le mot de passe vide. Par défaut, les clés générées sont stockées dans le répertoire ~/.ssh
avec les noms id_ed25519
et id_ed25519.pub
, ou id_rsa
et id_rsa.pub
. Sans mot de passe et avec les noms de fichiers par défaut, les clés sont utilisées par le client SSH sans intervention de l’utilisateur·rice.
Une fois que la paire de clés est générée, il est important de s’assurer que le fichier contenant la clé privée (id_ed25519
ou id_rsa
) ne peut être lu que par vous et personne d’autre. Si nécessaire, vous pouvez modifier les ACL ou les permissions du fichier pour restreindre l’accès.
Vous devez ensuite copier la clé publique dans le fichier ~/.ssh/authorized_keys
qui se trouve dans le home directory de l’utilisateur·rice distant. Si le répertoire ~/.ssh
ou le fichier authorized_keys
n’existe pas, vous devez les créer. Si le fichier authorized_keys
existe déjà, vous devez ajouter le contenu du fichier .pub
à la fin du fichier sur une nouvelle ligne. Sous macOS et Linux, vous pouvez effectuer ces opérations en utilisant la commande ssh-copy-id
.
Installation de OpenLDAP
Le serveur d’annuaire OpenLDAP fait partie de la distribution de Ubuntu et peut être facilement ajouté au système en installant le paquet slapd
avec gestionnaire de paquets APT.
Le paquet slapd
utilise le fully qualified name (FQDN) de la machine pour initialiser le naming contexte du serveur LDAP. Par conséquent, il est important de vérifier le FQDN de la machine avant de lancer l’installation. Le FQDN de la machine peut être obtenu avec la commande hostname --fqdn
. Votre machine devrait avoir un FQDN de la forme svr-m159-$NUM-ldap-01.lab.epai-ict.ch
, où $NUM
est le numéro de votre serveur. Si le FQDN n’est pas correct, vous devez changer le nom d’hôte du serveur en conséquence. Au besoin, effectuez une recherche sur le web pour apprendre à le faire ou demandez de l’aide.
Nous pouvons maintenant procéder à l’installation du paquet slapd
(/etc/ssl/certs/EPAI-ICT-Lab-Root-CA.pem
) et du paquet ldap-utils
qui contient des outils dont nous aurons besoin pour interroger et modifier les données de l’annuaire. Pour cela, lancez la commande suivante:
1
sudo apt update && sudo apt install -y slapd ldap-utils
À la fin de l’installation, le paquet crée automatiquement un administrateur pour l’annuaire dont le DN de l’administrateur est cn=admin,dc=lab,dc=epai-ict,dc=ch
et vous invite à saisir un mot de passe. Nous aurons besoin de ce DN et de ce mot de passe pour nous connecter au serveur d’annuaire.
Lorsque l’installation est terminée, nous pouvons utiliser la commande de maintenance slapcat
pour afficher les données utilisateur de l’annuaire. En exécutant la commande, vous devriez obtenir un résultat similaire.
1
sudo slapcat
1
2
3
4
5
6
7
8
9
10
11
12
13
dn: dc=lab,dc=epai-ict,dc=ch
objectClass: top
objectClass: dcObject
objectClass: organization
o: lab.epai-ict.ch
dc: lab
structuralObjectClass: organization
entryUUID: 7d684502-49f1-103d-8682-ff500e0c74bf
creatorsName: cn=admin,dc=lab,dc=epai-ict,dc=ch
createTimestamp: 20230226071819Z
entryCSN: 20230226071819.428113Z#000000#000#000000
modifiersName: cn=admin,dc=lab,dc=epai-ict,dc=ch
modifyTimestamp: 20230226071819Z
Utilitaires LDAP
La commande slapcat
est une commande de maintenance qui ne fonctionne qu’avec les privilèges root sur le système local. Cette commande accède directement aux données de l’annuaire sans passer par le protocole LDAP.
On peut obtenir le même résultat en utilisant la commande ldapsearch
qui fait partie des utilitaires LDAP installés par le paquet ldap-utils
. Commençons par exécuter la commande suivante.
1
2
ldapsearch -H ldap://svr-m159-$NUM-ldap-01.lab.epai-ict.ch -x \
-b "dc=lab,dc=epai-ict,dc=ch"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# extended LDIF
#
# LDAPv3
# base <dc=lab,dc=epai-ict,dc=ch> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#
# lab.epai-ict.ch
dn: dc=lab,dc=epai-ict,dc=ch
objectClass: top
objectClass: dcObject
objectClass: organization
o: lab.epai-ict.ch
dc: lab
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1
On remarque que la réponse est assez différente de la précédente. D’une part, elle est plus verbeuse, car elle contient des commentaires et des informations sur le résultat de la requête. Et d’autre part, elle ne contient que les attributs utilisateur de l’organisation et pas ses attributs opérationnels.
Une rapide recherche nous apprend qu’avec LDAPv3, on peut obtenir les attributs opérationnels avec le signe +
dans la liste des attributs à la fin de la commande. Pour comprendre ce que cela signifie, il faut examiner la syntaxe de la commande ldapsearch
. En simplifiant un peu, la syntaxe de ldapsearch
est la suivante:
1
ldapsearch [options] filter [attrs...]
Options (uniquement celles dont nous avons besoin)
- -Hspécifie l'URI du serveur, p. ex. ldap://svr-m159-$NUM-ldap-01.lab.epai-ict.ch
- -xutilise l'authentification simple au lieu de SASL
- -Dspécifie le DN de l'utilisateur·rice, p. ex. cn=admin,dc=lab,dc=epai-ict,dc=ch
- -wspécifie le mot de passe de l'utilisateur·rice pour l'authentification simple
- -Wdemande le mot de passe de l'utilisateur·rice
- -sspécifie l'étendue de la recherche:
- base la recherche porte uniquement sur la base de la recherche.
- one la recherche porte uniquement sur les enfants de la base.
- sub la recherche porte sur le sous-arbre dont la racine est la base.
- children la recherche porte uniquement sur les descendants de la base.
- -bspécifie le DN de la base de la recherche
Par défaut, la requête est anonyme, l’étendue de la recherche est sub
, le filtre est '(objectclass=*)'
et la liste d’attributs par défaut est *
. La requête que nous avons faite renvoie donc tous les attributs utilisateur de tous les objets du sous-arbre dont la racine est la base de recherche que l’utilisateur·rice anonymous
a le droit de lire. Essayons de spécifier le +
pour la liste des attributs :
1
2
ldapsearch -H ldap://svr-m159-$NUM-ldap-01.lab.epai-ict.ch -x \
-b "dc=lab,dc=epai-ict,dc=ch" +
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# extended LDIF
#
# LDAPv3
# base <dc=lab,dc=epai-ict,dc=ch> with scope subtree
# filter: (objectclass=*)
# requesting: +
#
# lab.epai-ict.ch
dn: dc=lab,dc=epai-ict,dc=ch
structuralObjectClass: organization
entryUUID: 7d684502-49f1-103d-8682-ff500e0c74bf
creatorsName: cn=admin,dc=lab,dc=epai-ict,dc=ch
createTimestamp: 20230226071819Z
entryCSN: 20230226071819.428113Z#000000#000#000000
modifiersName: cn=admin,dc=lab,dc=epai-ict,dc=ch
modifyTimestamp: 20230226071819Z
entryDN: dc=lab,dc=epai-ict,dc=ch
subschemaSubentry: cn=Subschema
hasSubordinates: FALSE
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1
Cette fois la réponse contient tous les attributs opérationnels, mais pas les attributs utilisateur. En effet, la liste d’attributs que nous avons spécifiée (+
) remplace la liste d’attributs par défaut (*
), pour obtenir la liste de tous les attributs utilisateur et opérationnels, nous devons ajouter à la fois *
et +
. Faisons un nouvel essai (les apostrophes autour du *
évitent qu’il soit interprété par le shell):
1
2
ldapsearch -H ldap://svr-m159-$NUM-ldap-01.lab.epai-ict.ch -x \
-b "dc=lab,dc=epai-ict,dc=ch" '*' +
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# extended LDIF
#
# LDAPv3
# base <dc=lab,dc=epai-ict,dc=ch> with scope subtree
# filter: (objectclass=*)
# requesting: + *
#
# lab.epai-ict.ch
dn: dc=lab,dc=epai-ict,dc=ch
objectClass: top
objectClass: dcObject
objectClass: organization
o: lab.epai-ict.ch
dc: lab
structuralObjectClass: organization
entryUUID: 7d684502-49f1-103d-8682-ff500e0c74bf
creatorsName: cn=admin,dc=lab,dc=epai-ict,dc=ch
createTimestamp: 20230226071819Z
entryCSN: 20230226071819.428113Z#000000#000#000000
modifiersName: cn=admin,dc=lab,dc=epai-ict,dc=ch
modifyTimestamp: 20230226071819Z
entryDN: dc=lab,dc=epai-ict,dc=ch
subschemaSubentry: cn=Subschema
hasSubordinates: FALSE
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1
Nous avons cette fois tous les attributs, mais la réponse est toujours plus verbeuse que la celle de la commande slapd
. On peut l’une des options -L
, -LL
et -LLL
pour contrôler le format de la réponse. Essayons avec l’option -LLL
:
1
2
ldapsearch -H ldap://svr-m159-$NUM-ldap-01.lab.epai-ict.ch -x \
-LLL -b "dc=lab,dc=epai-ict,dc=ch" '*' +
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
dn: dc=lab,dc=epai-ict,dc=ch
objectClass: top
objectClass: dcObject
objectClass: organization
o: lab.epai-ict.ch
dc: lab
structuralObjectClass: organization
entryUUID: 7d684502-49f1-103d-8682-ff500e0c74bf
creatorsName: cn=admin,dc=lab,dc=epai-ict,dc=ch
createTimestamp: 20230226071819Z
entryCSN: 20230226071819.428113Z#000000#000#000000
modifiersName: cn=admin,dc=lab,dc=epai-ict,dc=ch
modifyTimestamp: 20230226071819Z
entryDN: dc=lab,dc=epai-ict,dc=ch
subschemaSubentry: cn=Subschema
hasSubordinates: FALSE
À l’exception des trois dernières lignes, la réponse est cette fois sensiblement la même que celle de la commande slapd
. Toutefois, la commande ldapsearch
présente deux avantages majeurs. Le premier est de pouvoir être utilisée depuis un poste client, et le second est d’utiliser un protocole standard. En fait, ldapsearch
n’est qu’un client LDAP parmi d’autres et n’est donc pas spécifique à OpenLDAP. On peut interroger l’annuaire OpenLDAP avec n’importe quel outil OpenLDAP et utiliser la commande ldapsearch
pour interroger n’importe quel serveur d’annuaire LDAP.
On remarquera encore que l’entrée que nous renvoie OpenLDAP n’est pas le root DSE, mais un objet avec les classes d’objets organization et dcObject. La classe cdObject est une classe auxiliaire qui fait de cet objet un domain component. Pour obtenir le root DSE de notre annuaire, on peut utiliser la requête suivante :
1
2
ldapsearch -H ldap://svr-m159-$NUM-ldap-01.lab.epai-ict.ch -x \
-LLL -s base -b "" +
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
dn:
objectClass: top
objectClass: OpenLDAProotDSE
structuralObjectClass: OpenLDAProotDSE
configContext: cn=config
namingContexts: dc=lab,dc=epai-ict,dc=ch
supportedControl: 2.16.840.1.113730.3.4.18
supportedControl: 2.16.840.1.113730.3.4.2
supportedControl: 1.3.6.1.4.1.4203.1.10.1
supportedControl: 1.3.6.1.1.22
supportedControl: 1.2.840.113556.1.4.319
supportedControl: 1.2.826.0.1.3344810.2.3
supportedControl: 1.3.6.1.1.13.2
supportedControl: 1.3.6.1.1.13.1
supportedControl: 1.3.6.1.1.12
supportedExtension: 1.3.6.1.4.1.4203.1.11.1
supportedExtension: 1.3.6.1.4.1.4203.1.11.3
supportedExtension: 1.3.6.1.1.8
supportedExtension: 1.3.6.1.1.21.3
supportedExtension: 1.3.6.1.1.21.1
supportedFeatures: 1.3.6.1.1.14
supportedFeatures: 1.3.6.1.4.1.4203.1.5.1
supportedFeatures: 1.3.6.1.4.1.4203.1.5.2
supportedFeatures: 1.3.6.1.4.1.4203.1.5.3
supportedFeatures: 1.3.6.1.4.1.4203.1.5.4
supportedFeatures: 1.3.6.1.4.1.4203.1.5.5
supportedLDAPVersion: 3
supportedSASLMechanisms: DIGEST-MD5
supportedSASLMechanisms: NTLM
supportedSASLMechanisms: CRAM-MD5
entryDN:
subschemaSubentry: cn=Subschema
Enfin, ldapsearch
peut également être utilisé pour récupérer le schéma de l’annuaire. Le schéma de l’annuaire est contient toutes les descriptions des types d’attribut, des règles et des classes d’objets qui peuvent être utilisés dans l’annuaire. Dans OpenLDAP ces données sont stockées dans une sous-entrée (subentry) dont le DN est cn=subschema
.
Beaucoup de descriptions de types d’attribut, de règles et de classes d’objets sont spécifiées par un standard (LDAP, X.500, etc.), mais il n’y a pas de standard pour le schéma d’un annuaire. La distribution de OpenLDAP contient un grand nombre de ces descriptions regroupées dans six fichiers «.schema».
File | Description |
---|---|
core.schema | OpenLDAP core (requis) |
cosine.schema | Cosine and Internet X.500 (util) |
inetorgperson.schema | InetOrgPerson (util) |
misc.schema | Assorted (expérimental) |
nis.schema | Network Information Services (FYI) |
openldap.schema | OpenLDAP Project (experimental) |
Par défaut, le paquet slapd
de Ubuntu installe les fichiers core, cosine, nis et inetorgperson.
Lancez la commande suivante pour afficher le schéma de l’annuaire :
1
2
ldapsearch -H ldap://svr-m159-$NUM-ldap-01.lab.epai-ict.ch -x \
-LLL -s base -b "cn=subschema" +
Authentification
Par défaut, OpenLDAP accepte les requêtes anonymes. Ces requêtes permettent d’interroger l’annuaire, mais pas d’effectuer des changements dans la base de données.
Si l’on ajouter, supprimer ou modifier des objets, il est nécessaire d’authentifier un·e utilisateur·rice en utilisation l’opération bind du protocole LDAP. Les commandes du paquet ldap-utils
effectuent l’opération bind si l’option -D est renseignée avec le DN d’un·e utilisateur·rice et que l’option -x (simple authentication) est présente. Le mot de passe peut être fourni sur la ligne de commande avec l’option -w, ou saisi lorsque la commande le demande avec l’option -W.
Par défaut, le paquet slapd
crée un compte d’administration dont le DN est cn=admin,dc=lab,dc=epai,dc=ch
. Le mot de passe est celui que vous avez été invité à saisir durant l’installation. Essayons d’effectuer une opération bind avec ldapsearch
:
1
2
3
ldapsearch -H ldap://svr-m159-$NUM-ldap-01.lab.epai-ict.ch -x \
-D "cn=admin,dc=lab,dc=epai-ict,dc=ch" -w '<admin password>' \
-LLL -b "dc=lab,dc=epai-ict,dc=ch" '*' +
Vous pouvez vous assurer que l’opération bind fonctionne en donnant un mauvais mot de passe.
Nous pouvons maintenant essayer d’ajouter des objets dans l’annuaire. Pour cela, on commence par créer un fichier LDIF qui contient la description de ces objets.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Contenu du fichier add-content.ldif
dn: ou=People,dc=lab,dc=epai-ict,dc=ch
objectClass: organizationalUnit
ou: People
dn: ou=Groups,dc=lab,dc=epai-ict,dc=ch
objectClass: organizationalUnit
ou: Groups
dn: cn=prog,ou=Groups,dc=lab,dc=epai-ict,dc=ch
objectClass: posixGroup
cn: prog
gidNumber: 5000
dn: uid=lovelacea,ou=People,dc=lab,dc=epai-ict,dc=ch
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: lovelacea
sn: Lovelace
givenName: Ada
cn: Ada Lovelace
displayName: Ada Lovelace
uidNumber: 10000
gidNumber: 5000
userPassword: {SSHA}e6fAV3muZa8qZAqY1LcH5tOWkyHKJRan
gecos: Ada Lovelace
loginShell: /bin/bash
homeDirectory: /home/lovelacea
Attention: Dans le format LDIF, une ligne vide marque la fin de la description d’un objet. Il est important que cette ligne soit effectivement vide et qu’elle ne contienne pas de caractères blancs (espace ou tabulation). Il est également important que le fichier se termine par une ligne vide.
Pour ajouter les objets décrits dans le fichier, on utilise la commande ldapadd
avec l’option -f pour indiquer le nom et l’emplacement du fichier. Par exemple, si le fichier s’appelle add-content.ldif
, on exécute la commande suivante:
1
2
3
ldapadd -H ldap://svr-m159-$NUM-ldap-01.lab.epai-ict.ch \
-x -D "cn=admin,dc=lab,dc=epai-ict,dc=ch" -W \
-f ./add-content.ldif
1
2
3
4
5
6
7
8
Enter LDAP Password:
adding new entry "ou=People,dc=lab,dc=epai-ict,dc=ch"
adding new entry "ou=Groups,dc=lab,dc=epai-ict,dc=ch"
adding new entry "cn=prog,ou=Groups,dc=lab,dc=epai-ict,dc=ch"
adding new entry "uid=lovelacea,ou=People,dc=lab,dc=epai-ict,dc=ch"
Assurez-vous que l’annuaire contient bien les objets décrits en affichant son contenu à l’aide de la commande ldapsearch
.
Installation d’un certificat TLS
Lorsque l’on utilise l’authentification simple, le mot de passe est transmis en clair au serveur. Il est donc crucial de sécuriser le canal de communication en utilisant le TLS (Transport Layer Security) pour chiffrer le canal TCP, tout comme avec le protocole HTTP.
Le TLS repose sur l’authentification du serveur à l’aide d’un certificat de clé publique émis par une autorité de certification (CA) de confiance. Pour garantir l’authenticité d’un certificat, il est nécessaire de posséder les certificats racines des différentes autorités de certification. Heureusement, ces certificats sont préinstallés dans les systèmes d’exploitation et sont régulièrement mis à jour. Toutefois, dans un environnement intranet, il peut être nécessaire d’obtenir un grand nombre de certificats pour différents usages. Dans ces cas-là, il est courant d’utiliser une autorité de certification interne et le certificat racine doit alors être ajouté à la liste des certificats racines de confiance pour assurer une communication sécurisée.
Le certificat racine de la CA que nous avons mis en place dans le cadre de nos laboratoires peut être téléchargé ici et est déjà installé dans vos serveurs Ubuntu.
Nous allons maintenant configurer notre serveur d’annuaire pour utiliser le TLS. Le certificat et la clé privée dont vous avez besoin se trouvent le répertoire ~/tls-cert
. Commencez par copier le certificat et la clé privée dans le répertoire /etc/ldap
, puis renommer le fichier du certificat en slapd_cert.pem
et le fichier de la clé en slapd_key.pem
. Changer le propriétaire et le group propriétaire de ces deux fichiers, et ajuster leurs permissions pour obtenir un résultat semblable la figure ci-après sInportant: le groupe propriétaire du certificat et de la clé doit être openldap
.
1
2
3
4
5
6
7
8
9
total 32
drwxr-xr-x 5 root root 4096 fév 26 16:49 .
drwxr-xr-x 99 root root 4096 fév 24 21:35 ..
-rw-r--r-- 1 root root 334 fév 16 2022 ldap.conf
drwxr-xr-x 2 root root 4096 aoû 5 2022 sasl2
drwxr-xr-x 2 root root 4096 fév 24 18:51 schema
-rw-r----- 1 root openldap 2096 fév 25 15:53 slapd_cert.pem
drwxr-xr-x 3 openldap openldap 4096 fév 26 08:18 slapd.d
-rw-r----- 1 root openldap 1679 fév 25 15:53 slapd_key.pem
La configuration de OpenLDAP est stockée dans l’annuaire et se gère avec les commandes du paquet ldap-utils
. Pour modifier la configuration, nous allons donc utiliser la commande ldapmodify
. Comme pour la commande ldapadd
, nous commençons par créer un fichier au format LDIF qui contient la description des modifications à effectuer.
1
2
3
4
5
6
7
8
9
10
# Contenu du fichier tls-cert-config.ldif
dn: cn=config
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ssl/certs/EPAI-ICT-Lab-Root-CA.pem
-
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ldap/slapd_cert.pem
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ldap/slapd_key.pem
Si l’on essaie maintenant d’exécuter ldapmodify
de la même manière dont nous avons exécuté ldapadd
, avec l’utilisateur·rice cn=admin,dc=lab,dc=epai-ict,dc=ch
, nous obtenons une erreur qui nous informe que nous n’avons pas les droits nécessaires pour cette opération. En effet, la configuration du serveur ne peut être modifiée que par l’utilisateur·rice root
du système. La solution réside dans le protocole ldapi: utilise un unix socket au lieu du réseau et qui permet de déléguer l’authentification au système Linux. Essayons d’effectuer cette modification avec la commande ci-après. Il est important de noter que cette fois, on exécute la commande avec sudo.
1
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f tls-cert-config.ldif
Si tout c’est bien passé, nous pouvons vérifier que la configuration est correcte en exécutant la commande suivante:
1
sudo ldapsearch -Y EXTERNAL -H ldapi:/// -LLL -s base -b "cn=config"
1
2
3
4
5
6
7
8
9
10
11
12
13
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
dn: cn=config
objectClass: olcGlobal
cn: config
olcArgsFile: /var/run/slapd/slapd.args
olcLogLevel: none
olcPidFile: /var/run/slapd/slapd.pid
olcTLSCACertificateFile: /etc/ssl/certs/epai-ict_lab_root_ca.pem
olcTLSCertificateFile: /etc/ldap/slapd_cert.pem
olcTLSCertificateKeyFile: /etc/ldap/slapd_key.pem
olcToolThreads: 1
Il reste encore à dire au service slapd
qu’il doit écouter sur le port 636 pour le protocole ldaps://
. Pour cela, nous devons modifier le fichier /etc/default/slapd
et ajouter “ldaps://” à la valeur du paramètre SLAPD_SERVICES
.
1
SLAPD_SERVICES="ldap:/// ldapi:/// ldaps://"
Redémarrez le service slapd.service
avec la commande sudo systemctl restart slapd.service
et testez le TLS avec les commandes suivantes:
1
2
ldapwhoami -x -ZZ -H ldap://svr-m159-$NUM-ldap-01.lab.epai-ict.ch
ldapwhoami -x -H ldaps://svr-m159-$NUM-ldap-01.lab.epai-ict.ch
Si la première commande renvoie «anonymous», cela signifie que le certificat est correctement configuré. Si la seconde commande fonctionne, cela signifie que le protocole ldaps://
est correctement configuré.
Tâches
Effectuez les tâches décrites ci-après à l’aide des outils d’administration de votre choix.
Installation des outils ldap
Installez les commandes ldapsearch
, ldapadd
, ldapmodify
, etc. sur votre ordinateur.
- Sous Windows, utiliser la machine de développement, un container Ubuntu ou le WSL2.
- Sous macOS, installer le paquet openldap avec Homebrew :
brew install openldap
Pour utiliser le ldaps://
, vous devez également récupérer le certificat racine de la CA et l’ajouter au certificat racine de confiance de votre système (machine phyique ou virtuelle).
Dans tous les cas, commencez par télécharger le fichier epai-ict-lab-root-ca.zip
et décompressez-le dans un répertoire. Il contient le certificat au format PEM (.pem) et au format DER (.crt). Ouvrez une fenêtre de terminal et déplacez-vous dans ce répertoire.
1
2
3
4
5
6
7
8
9
10
11
12
# Sous macOS
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain EPAI-ICT-Lab-Root-CA.crt`
# Sous Ubuntu
sudo cp EPAI-ICT-Lab-Root-CA.pem \
/usr/local/share/ca-certificates/EPAI_ICT_Lab_Root_CA.crt
sudo update-ca-certificates
# Sous Windows (pour information)
Import-Certificate -FilePath ".\EPAI-ICT-Lab-Root-CA.crt" `
-CertStoreLocation Cert:\LocalMachine\Root
Utilisez la commande ldapwhoami
comme précédemment pour vérifier que tout fonctionne, puis afficher le contenu de l’annuaire avec ldapsearch
. Assurez-vous d’utiliser le protocole ldaps://
.
Utilisez la commande openssl x509 -in <fichier> -text
pour lire toutes les informations qui se trouvent dans le certificat TLS (slapd_cert.pem
) et répondez au questions.
Questions:
- Quel est le CN du subjectif du certificat? À quoi est-ce que cela doit correspondre?
- Est-ce que l’émetteur du certificat est “EPAI ICT Lab Root CA”?
- Ouvrez le certificat avec un éditeur de texte ? Combien y a-t-il de certificats à l’intérieur?
- Copiez les certificats dans des fichiers séparés et utilisez la commande
openssl x509
avec chacun d’eux et décrivez-les. - Expliquez pourquoi vous devez ajouter le certificat racine dans la liste des certificats de confiance de l’OS de votre machine.
- Dans notre cas, qui du serveur ou du client valide le certificat TLS que vous avez installé? Expliquez le processus de validation.
Créer et modifier des utilisateur·rice·s
Utilisez des fichiers LDIF et les ldapadd
pour créer les objectifs ci-après sur le modèle de ceux que vous avez créés précédemment. Tous les groupes doivent être créés dans l’OU Groups et toutes les personnes dans l’OU People. Le uid des personnes se compose du nom de famille suivi de l’initiale du prénom, le tout en minuscule et sans accent.
- Les groupes:
- it-service (gidNumber: 6000)
- commercial (gidNumber: 6001),
- secretariat (gidNumber: 6002)
- Les personnes:
- Jacques Bornet, Directeur informatique (uidNumber: 6000)
- Huguette Maret, Directrice commerciale. (uidNumber: 6001)
- Georges Miauton, Secrétair de Direction. (uidNumber: …)
- Tifani Brelat, responsable informatique du site de Lausanne.
- Marie Michellot, responsable commerciale du site de Lausanne.
- Gabriel Berset, Secrétair de Direction.
- Andrea Schweizer, responsable informatique du site de Berne.
- Fritz Staub, responsable commercial du site de Berne.
- Irene Schaer, Secrétaire de Direction
- Juliette Praz, responsable informatique du site de Sion.
- Laure Fournier, responsable commerciale du site de Sion.
- Sarah Largey, Secrétaire de Direction.
- <vous-même>, administrateur du domaine
Remarque: La création d’un fichier LDIF à partir d’une liste de noms est une tâche fastidieuse et sujette à des erreurs. Il est donc judicieux d’automatiser la tâche, par exemple, avec bash. Le script ci-après écrit dans stdout une description des utilisateur·rice·s au format LDIF à partir d’un fichier CSV. Copiez le code dans un fichier csv2ldif
et modifiez les permission pour le rendre exécutable.
Astuce: Excel est un outil puissant pour créer et éditer des fichiers CSV. Toutefois, comme c’est un produit de Microsoft, les retours à la ligne sont généralement codés avec les caractères CRLF alors que sous Linux, ils sont codés avec un seul caractère LF. Pour convertir un fichier «Windows» en fichier «Linux», vous pouvez utiliser l’utilitaire dos2unix
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#! /bin/bash
# csv2lfif
# Lit un fichier csv en UTF-8 dont le séprateur est un ";" et les champs sont,
# dans l'ordre:
# username;firstname;lastname;uidNumber;gidNumber;password
#
while IFS=";" read -r username firstname lastname uidNumber gidNumber password
do
# Supprime le caractère CR si le fichier a été édité avec Windows.
password=$(echo -n $password | sed 's/\r//')
# Utilise le username comme sel pour le hashage
salt=$username
hash="$(printf "%s%s" "$password" "$salt" | openssl dgst -binary -sha1)"
cat <<-EOF
dn: uid=$username,ou=People,dc=lab,dc=epai-ict,dc=ch
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: $username
sn: $lastname
givenName: $firstname
cn: $firstname $lastname
displayName: $firstname $lastname
uidNumber: $uidNumber
gidNumber: $gidNumber
userPassword: $(printf "{SSHA}%s" "$(printf "%s%s" "$hash" "$salt" | base64)")
gecos: $(printf "%s %s" "$firstname" "$lastname" | iconv -f utf8 -t ascii//TRANSLIT//IGNORE)
loginShell: /bin/bash
homeDirectory: /home/$username
EOF
# tail reçoit le contenu de l'entrée standard
done < <(tail -n +2)
1
2
3
4
5
6
7
8
9
10
11
12
13
username;firstname;lastname;uidNumber;gidNumber;password
bornetj;Jacques;Bornet;6000;6000;epai123
mareth;Huguette;Maret;6001;6001;epai123
miautong;Georges;Miauton;6002;6002;epai123
brelatt;Tifani;Brelat;6003;6000;epai123
michellotm;Marie;Michellot;6004;6001;epai123
bersetg;Gabriel;Berset;6005;6002;epai123
schweizera;Andrea;Schweizer;6006;6000;epai123
staubf;Fritz;Staub;6007;6001;epai123
schaeri;Irene;Schaer;6008;6002;epai123
prazj;Juliette;Praz;6009;6000;epai123
fournierl;Laure;Fournier;6010;6001;epai123
largeys;Sarah;Largey;6011;6002;epai123
Le script csv2ldif
est un filtre. On peut, par exemple, utiliser la commande cat
pour écrire le contenu du fichier csv dans stdout, un tube (|) pour connecter la sdtin de csv2ldif
à la stdout de cat
, puis rediriger la stdout de csv2ldif
dans un fichier.
1
cat users.csv | ./csv2ldif > users.ldif
On peut également utiliser un second tube pour connecter la stdin de ldapadd à la stdout de csv2ldif
.
1
2
3
cat user.csv | ./csv2ldif | \
ldapadd -x -H ldaps://svr-m159-$NUM-ldap-01.lab.epai-ict.ch \
-D cn=admin,dc=lab,dc=epai-ict,dc=ch -W
Questions:
- Comment sont séparés les objets dans un fichier LDIF ?
- Quel format doit avoir le fichier CSV pour le script ci-dessus.
- Créez un fichier CSV et aidez-vous du script pour le convertir en fichier LDIF.
- Créez un script similaire pour créer les groupes.
- Ajouter les groupes et les utilisateur·rice·s dans l’annuaire.
- Avec OpenLDAP, si une erreur survient au milieu du fichier, que se passe-t-il ? Pouvez-vous relancer la création avec le même fichier ? (Examinez l’option -c)
Ajouter des définition dans le schéma dans l’annuaire
Pour que Linux puisse authentifier les utilisateur·rice·s en utilisant les données de l’annuaire avec ssh ou lors d’une élévation de privilège, nous avons besoin d’ajouter un certain nombre de définitions dans le schéma de l’annuaire
Pour l’authentification SSH, nous avons besoin d’un type d’attribut sshPublicKey
et d’une classe auxiliaire ldapPublicKey
pour ajouter cet attribut aux utilisateur·rice·s. Comme un·e utilisateur·rice peut avoir plusieurs clés publiques, l’attribut sshPublicKey
est multivalué.
1
2
3
4
5
6
7
8
9
10
11
12
dn: cn=openssh-lpk,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: openssh-lpk
olcAttributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey'
DESC 'OpenSSH Public key'
EQUALITY octetStringMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
olcObjectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey'
SUP top
AUXILIARY
DESC 'OpenSSH LPK objectclass'
MAY ( sshPublicKey $ uid ))
Copiez le schéma openssh-lpk dans un fichier openssh-lpk
dans le répertoire /etc/ldap/schema
et lancez la commande suivante :
1
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/openssh-lpk.ldif
Pour la commande sudo, nous avons besoin de la classe structurelle sudoRole
qui est décrite dans le schéma ci-après. Cette classe nous permet de définir quels comptes peuvent utiliser la commande sudo
et avec quels restrictions s’il y en a.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
dn: cn=sudo,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: sudo
olcAttributeTypes: {0}( 1.3.6.1.4.1.15953.9.1.1 NAME 'sudoUser'
DESC 'User(s) who may run sudo'
EQUALITY caseExactIA5Match
SUBSTR caseExactIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {1}( 1.3.6.1.4.1.15953.9.1.2 NAME 'sudoHost'
DESC 'Host(s) who may run sudo'
EQUALITY caseExactIA5Match
SUBSTR caseExactIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {2}( 1.3.6.1.4.1.15953.9.1.3 NAME 'sudoCommand'
DESC 'Command(s) to be executed by sudo'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {3}( 1.3.6.1.4.1.15953.9.1.4 NAME 'sudoRunAs'
DESC 'User(s) impersonated by sudo (deprecated)'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {4}( 1.3.6.1.4.1.15953.9.1.5 NAME 'sudoOption'
DESC 'Options(s) followed by sudo'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {5}( 1.3.6.1.4.1.15953.9.1.6 NAME 'sudoRunAsUser'
DESC 'User(s) impersonated by sudo'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {6}( 1.3.6.1.4.1.15953.9.1.7 NAME 'sudoRunAsGroup'
DESC 'Group(s) impersonated by sudo'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {7}( 1.3.6.1.4.1.15953.9.1.8 NAME 'sudoNotBefore'
DESC 'Start of time interval for which the entry is valid'
EQUALITY generalizedTimeMatch
ORDERING generalizedTimeOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )
olcAttributeTypes: {8}( 1.3.6.1.4.1.15953.9.1.9 NAME 'sudoNotAfter'
DESC 'End of time interval for which the entry is valid'
EQUALITY generalizedTimeMatch
ORDERING generalizedTimeOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )
olcAttributeTypes: {9}( 1.3.6.1.4.1.15953.9.1.10 NAME 'sudoOrder'
DESC 'an integer to order the sudoRole entries'
EQUALITY integerMatch
ORDERING integerOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
olcObjectClasses: {0}( 1.3.6.1.4.1.15953.9.2.1 NAME 'sudoRole'
DESC 'Sudoer entries'
SUP top
STRUCTURAL
MUST cn
MAY ( sudoUser $ sudoHost $ sudoCommand $ sudoRunAs $ sudoRunAsUser $
sudoRunAsGroup $ sudoOption $ sudoOrder $ sudoNotBefore $
sudoNotAfter $ description ))
Questions:
- À quoi voit-on que l’attribut
sshPublicKey
est multivalué? - Qu’est-ce qu’un classe auxiliaire et en quoi se distingue-t-elle d’une classe structurelle?
- Pourquoi utilise-t-on le protocole
ldapi://
et l’option-Y EXTERNAL
pour ajouter le schéma? - Ajouter le schéma sudo de la même manière que vous avez ajouté le schéma openssh-lpk.
- Assurez-vous que les deux schémas ont bien été ajoutés.
Modifier un·e utilisateur·rice
On voudrait ajouter le numéro de téléphone mobile et l’adresse de courriel des utilisateur·rice·s dans l’annuaire. Récupérez le schéma de l’annuaire dans un fichier et cherchez dans la description de la classe inetOrgPerson
, le nom des attributs que l’on pourrait utiliser à cette fin.
Questions:
- Quelle est la description de la classe d’objets
inetOrgPerson
? - Quels sont les attributs que vous utiliseriez ?
- Comment décrit-on l’ajout d’un attribut dans une entrée avec LDIF?
- Comment décrit-on la modification d’un attribut dans une entrée avec LDIF?
- Quel caractère permet de séparer plusieurs modifications d’un même objet dans un fichier LDIF ?
- À chaque membre du groupe it-service, ajoutez la classe
ldapPublicKey
et initialisez l’attributsshPublicKey
avec votre clé publique.
Astuce: Pour ajouter la classe et la clé publique, vous pouvez créer un script sur la base de celui que vous avez utiliser pour créer les utilisateurs.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#! /bin/bash
while IFS=";" read -r username firstname lastname uidNumber gidNumber password
do
cat <<-EOF
dn: uid=$username,ou=People,dc=lab,dc=epai-ict,dc=ch
changetype: modify
add: objectClass
objectClass: ldapPublicKey
-
add: sshPublicKey
sshPublickey: $(head -n 1 ~/.ssh/authorized_keys)
EOF
# tail reçoit le contenu de l'entrée standard
done < <(tail -n +2)
Supprimer des objets
La commande ldapdelete
permet de supprimer un objet en spécifiant son DN.
Questions:
- Peut-on supprimer un objet sans authentification?
- Essayez de supprimer l’OU People (ou=People,dc=lab,dc=epai-ict,dc=ch). Que se passe-t-il et Pourquoi?
- Essayez de supprimer le compte d’utilisatrice lovelacea.
- Peut-on supprimer plusieurs objets à la fois? Si oui, comment?
- Le script ci-après permet d’obtenir une liste de DN. Analysez-le et expliquez son fonctionnement en détail.
- Modifiez le script pour obtenir une liste des DN des membres du groupe commercial?
- Supprimer tous les membres du groupe commercial. Décrivez en détail la ou les commandes que vous utilisez pour cela.
Astuce: Exemple de commande pour obtenir une liste de DN:
1
2
3
4
ldapsearch -H ldap://svr-m159-$NUM-ldap-01.lab.epai-ict.ch -x -LLL \
-b "dc=lab,dc=epai-ict,dc=ch" \
'(&(objectclass=inetOrgPerson)(!(uid=lovelacea)))' dn \
| sed 's/dn:\s*//' | sed '/^$/d'
Sécuriser le serveur
Le TLS est déjà configuré pour chiffrer le canal TCP, mais on peut encore améliorer la sécurité du serveur avec deux mesures supplémentaires:
- Activier le firewall
ufw
pour n’autoriser les connexions entrantes ssh, ldap et ldaps. - Désactiver l’authentification par mot de passe et s’assurer que l’authentification de root n’est pas autorisée.
Questions:
- Quels sont les ports qu’il faut ouvrir sur le firewall pour permettre les connexions ssh, ldap et ldaps?
- Quelles commandes doit-on exécuter pour autoriser le trafic entrant sur ces ports avec ufw?
- Quelle commande doit-on exécuter pour activer ufw?
- Comment s’appelle le daemon (service) qui prend en charge les connexions ssh sur le serveur?
- Quel fichier de configuration doit-on modifier pour changer la configuration de ce daemon?
- Quel paramètre doit-on changer dans ce fichier pour interdire l’authentification par mot de passe?
- Quel paramètre doit-on vérifier pour s’assurer que la connexion de l’utilisateur·rice root n’est pas autorisée?