Activité Déploiement d'un serveur OpenLDAP

Consigne

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

  1. Être capable d’installer et configurer un serveur OpenLDAP.
  2. Connaître les principaux composants de OpenLDAP (slapd, fichier de configuration, etc.)
  3. Connaître l’importance du protocole ldaps pour l’utilisation de l’authentification simple.
  4. 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.
  5. Connaître l’existence des commandes de maintenance de OpenLDAP (slapcat, slapadd, slapmodify, slapindex, slapschema, etc.)
  6. Être capable d’utiliser les commandes usuelles de OpenLDAP (ldapsearch, ldapadd, ldapmodify, ldapdelete, ldapmodrdn, etc.)
  7. Être capable de décrire des objets dans le format LDIF (person, OU, etc.)
  8. Ê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:

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 du réseau sur deux ou trois chiffres, en initialisant une variable d’environnement NUM, les commandes peuvent être exécutées telles quelles).
    • le nom d’utilisateur·rice est admin
    • les informations d’indentification se trouvent sur la fiche que vous avez reçu par courriel.

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
Fig. 1 – Établire une connexion ssh

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
Fig. 2 – Générer une paire de clés (le commentaire apparaît dans la clé publique)

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 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
Fig. 3 – Installation des paquets

À 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
Fig. 4 – Afficher le contenu de la base de données avec la commande slapcat

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
Fig. 5 – Afficher le contenu de la base de données avec la commande ldapsearch

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...]
Fig. 6 – syntaxe simplifiée de ldapsearch

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 anonymousa 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
Fig. 7 – Afficher le contenu de la base de données avec la commande ldapsearch, 2e essai

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
Fig. 8 – Afficher le contenu de la base de données avec la commande ldapsearch, 3e essai

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
Fig. 9 – Afficher le contenu de la base de données avec la commande ldapsearch, 4e essai

À 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 dcObject 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
Fig. 10 – Afficher le root DSE

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)
Fig. 11 – Fichiers .schema fournis avec OpenLDAP

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" +
Fig. 12 – Récupérer le schéma de l'annuaire

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" '*' +
Fig. 13 – Opération bind avec ldapsearch

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
Fig. 14 – Description d'une personne au format LDIF (add-content.ldif)

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"
Fig. 15 – Ajouter des objets

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 ~/certs. 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. Inportant: 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
Fig. 16 – Certificat de clé publique et clé privée

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-ca-root.pem
-
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ldap/slapd_cert.pem
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ldap/slapd_key.pem
Fig. 17 – Configuer le certificat TLS (tls-cert-config.ldif)

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
Fig. 18 – Modifier la configuration de OpenLDAP avec ldapmodify

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
Fig. 19 – Vérifer la configuration avec ldapsearch

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://"
Fig. 20 – Modification du fichier /etc/default/slapd

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
Fig. 21 – Modifier la configuration de OpenLDAP avec ldapmodify

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
Fig. 22 – Installer le certificat racine

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:

  1. Quel est le CN du subject du certificat? À quoi est-ce que cela doit correspondre?
  2. Est-ce que l’émetteur du certificat est “EPAI ICT Lab Root CA”?
  3. Ouvrez le certificat avec un éditeur de texte ? Combien y a-t-il de certificats à l’intérieur?
  4. Copiez les certificats dans des fichiers séparés et utilisez la commande openssl x509 avec chacun d’eux et décrivez-les.
  5. Expliquez pourquoi vous devez ajouter le certificat racine dans la liste des certificats de confiance de l’OS de votre machine.
  6. 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
Exemple de script et de fichier csv

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
Créer un fichier ldif avec le filtre csv2ldif

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
Utiliser la sortie du filtre comme entrée de la commande ldapadd

Questions:

  1. Quel séparateur doit être utilisé dans le fichier CSV pour le script ci-dessus.
  2. Comment sont séparés les objets dans un fichier LDIF ?
  3. Créez un fichier CSV et aidez-vous du script pour le convertir en fichier LDIF.
  4. Créez un script similaire pour créer les groupes.
  5. Ajouter les groupes et les utilisateur·rice·s dans l’annuaire.
  6. 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éfinitions dans le schéma de 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 ))
Schéma openssh-lpk au format LDIF

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
Schéma openssh-lpk au format 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 ))
Schéma sudoers au format LDIF

Questions:

  1. À quoi voit-on que l’attribut sshPublicKey est multivalué?
  2. Qu’est-ce qu’un classe auxiliaire et en quoi se distingue-t-elle d’une classe structurelle?
  3. Pourquoi utilise-t-on le protocole ldapi:// et l’option -Y EXTERNAL pour ajouter le schéma?
  4. Ajouter le schéma sudo de la même manière que vous avez ajouté le schéma openssh-lpk.
  5. 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:

  1. Quelle est la description de la classe d’objets inetOrgPerson ?
  2. Quels sont les attributs que vous utiliseriez ?
  3. Comment décrit-on l’ajout d’un attribut dans une entrée avec LDIF?
  4. Comment décrit-on la modification d’un attribut dans une entrée avec LDIF?
  5. Quel caractère permet de séparer plusieurs modifications d’un même objet dans un fichier LDIF ?
  6. À chaque membre du groupe it-service, ajoutez la classe ldapPublicKey et initialisez l’attribut sshPublicKey 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)
Exemple de script pour modifier les utilisateurs

Supprimer des objets

La commande ldapdelete permet de supprimer un objet en spécifiant son DN.

Questions:

  1. Peut-on supprimer un objet sans authentification?
  2. Essayez de supprimer l’OU People (ou=People,dc=lab,dc=epai-ict,dc=ch). Que se passe-t-il et Pourquoi?
  3. Essayez de supprimer le compte d’utilisatrice lovelacea.
  4. Peut-on supprimer plusieurs objets à la fois? Si oui, comment?
  5. Le script ci-après permet d’obtenir une liste de DN. Analysez-le et expliquez son fonctionnement en détail.
  6. Modifiez le script pour obtenir une liste des DN des membres du groupe commercial?
  7. 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'
Afficher la liste des DN des objets de type inetOrgPerson à l'exception de lovelacea

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:

  1. Activier le firewall ufw pour n’autoriser les connexions entrantes ssh, ldap et ldaps.
  2. Désactiver l’authentification par mot de passe et s’assurer que l’authentification de root n’est pas autorisée.

Questions:

  1. Quels sont les ports qu’il faut ouvrir sur le firewall pour permettre les connexions ssh, ldap et ldaps?
  2. Quelles commandes doit-on exécuter pour autoriser le trafic entrant sur ces ports avec ufw?
  3. Quelle commande doit-on exécuter pour activer ufw?
  4. Comment s’appelle le daemon (service) qui prend en charge les connexions ssh sur le serveur?
  5. Quel fichier de configuration doit-on modifier pour changer la configuration de ce daemon?
  6. Quel paramètre doit-on changer dans ce fichier pour interdire l’authentification par mot de passe?
  7. Quel paramètre doit-on vérifier pour s’assurer que la connexion de l’utilisateur·rice root n’est pas autorisée?