Aller au contenu
Authentification : PAM, NSS et annuaires

Authentification : PAM, NSS et annuaires

200 Compagnon ⏱ 1 h 30 linuxubuntudebianpamsssdopenssh

À la fin, vous saurez

  • Distinguer le rôle de NSS (qui existe) de celui de PAM (qui prouve son identité et a le droit d'entrer)
  • Lire une pile PAM ligne par ligne, y compris les contrôles entre crochets et les sauts, et prédire son résultat
  • Modifier la configuration PAM d'Ubuntu et de Debian avec pam-auth-update sans se couper l'accès
  • Mettre en place le verrouillage après échecs (pam_faillock) et une politique de robustesse des mots de passe (pam_pwquality) conformes aux recommandations de l'ANSSI
  • Restreindre les connexions à un groupe et à un réseau avec pam_access
  • Expliquer le hachage yescrypt, lire et régler l'expiration d'un compte avec chage, et argumenter pour ou contre l'expiration périodique
  • Relier un serveur à un annuaire avec SSSD et vérifier la résolution et l'authentification d'un compte d'annuaire

Prérequis

Testé avec debian 13 libpam 1.5.3 (Ubuntu) / 1.7.0 (Debian) libpam-pwquality 1.4.5 openssh-server 9.6p1 (Ubuntu) / 10.0p1 (Debian) passwd 4.13 (Ubuntu) / 4.17.4 (Debian) sssd 2.9.4 (Ubuntu) / 2.10.1 (Debian) sudo 1.9.15p5 (Ubuntu) / 1.9.16p2 (Debian) ubuntu 24.04 , vérifié le 7 octobre 2026

Pourquoi

Camille est partie vendredi. Lundi matin, la responsable sécurité de Lyneko vous pose une question simple : « Pouvez-vous me garantir que Camille ne peut plus se connecter à aucune machine de Signalements ? »

La réponse honnête demande du travail. Sur sig-app-1, sig-app-2 et sig-outils, Camille avait un compte local, un mot de passe pour sudo, une clé dans ~/.ssh/authorized_keys, et sa clé publique figure aussi dans les clés SSH du projet Scaleway, que le script scw-fetch-ssh-keys réécrit dans /root/.ssh/authorized_keys à chaque démarrage. La leçon Utilisateurs, groupes et sudo vous a appris à verrouiller un compte avec usermod --lock --expiredate 1. Mais pourquoi cette commande bloque-t-elle aussi une connexion par clé SSH, alors que la clé n'a rien à voir avec le mot de passe ? Qui, exactement, consulte la date d'expiration ? Et que se passera-t-il le jour où l'équipe comptera vingt personnes et quinze serveurs ?

Derrière chaque ouverture de session, deux mécanismes travaillent en silence. NSS répond à la question « ce compte existe-t-il, et quel est son UID ? ». PAM répond à « cette personne prouve-t-elle qui elle est, a-t-elle le droit d'entrer maintenant, et que faut-il préparer pour sa session ? ». Tant qu'on ne les a pas lus, on administre l'authentification par superstition : on copie une ligne trouvée sur un forum dans /etc/pam.d/common-auth, et on découvre une heure plus tard que plus personne ne peut faire sudo.

Cette leçon ouvre ces deux boîtes. Vous apprendrez à lire une pile PAM comme un programme, à la modifier sans vous enfermer dehors, à ajouter un verrouillage après échecs et une politique de mots de passe défendable devant un auditeur, puis à franchir l'étape qui rend la question de la responsable sécurité triviale : un annuaire central, où désactiver Camille se fait une fois, pour toutes les machines.

Les concepts

Deux questions, deux mécanismes

QuestionMécanismeConfigurationExemple d'appel
Ce nom existe-t-il ? Quel est son UID, son groupe, son répertoire, son shell ?NSS (Name Service Switch)/etc/nsswitch.confgetpwnam("camille")
Cette personne est-elle bien Camille ? A-t-elle le droit d'entrer ? Que faut-il faire à l'ouverture et à la fermeture de sa session ?PAM (Pluggable Authentication Modules)/etc/pam.d/<service>pam_authenticate(), pam_acct_mgmt()

Les deux se rejoignent souvent : le module pam_unix vérifie un mot de passe dans /etc/shadow, et le module pam_sss en vérifie un auprès d'un annuaire, tandis que NSS rend le même compte visible par ls -l ou id. Mais ils restent indépendants : un compte peut exister pour NSS (il a un UID, ses fichiers portent son nom) tout en étant refusé par PAM (compte expiré, hors du groupe autorisé, verrouillé après trop d'échecs).

PAM : une bibliothèque entre l'application et la vérification

Avant PAM, chaque programme (login, su, ftpd...) lisait lui-même /etc/passwd puis /etc/shadow. Changer de méthode d'authentification, par exemple passer à Kerberos, imposait de recompiler tous ces programmes. PAM, conçu chez Sun au milieu des années 1990 puis repris sous Linux par le projet Linux-PAM, inverse la logique : l'application ne sait plus comment on authentifie, elle demande seulement si c'est réussi. La méthode est décrite dans un fichier de configuration et exécutée par des modules chargés à la volée.

    flowchart LR
  A["sshd, sudo, login, su,<br/>cron, passwd..."] -->|"pam_start(sshd)"| B["libpam"]
  B -->|lit| C["/etc/pam.d/sshd"]
  C -->|"@include"| D["/etc/pam.d/common-*"]
  B -->|charge| E["pam_unix.so<br/>pam_faillock.so<br/>pam_sss.so ..."]
  E --> F["/etc/shadow<br/>/var/run/faillock<br/>annuaire LDAP"]
  

Le nom passé à pam_start() est le nom de service : sshd, sudo, login, su, cron, passwd, chpasswd... libpam cherche le fichier du même nom dans /etc/pam.d/. S'il n'existe pas, elle se rabat sur /etc/pam.d/other, qui sur Debian et Ubuntu inclut les fichiers communs. Les modules sont des bibliothèques partagées installées dans /usr/lib/x86_64-linux-gnu/security/ sur Ubuntu et Debian (/usr/lib64/security/ sur Red Hat).

Les quatre types de modules

Une application ne fait pas une seule demande à PAM, mais plusieurs, chacune servie par une pile différente. La page pam.conf(5) en définit quatre :

TypeQuestion poséeFonction appelée par l'applicationExemples de modules
authProuvez qui vous êtes (mot de passe, jeton, empreinte).pam_authenticate(), puis pam_setcred()pam_unix, pam_faillock, pam_sss, pam_u2f
accountIndépendamment de la preuve, avez-vous le droit d'entrer maintenant ? Compte expiré, heure interdite, origine refusée...pam_acct_mgmt()pam_unix, pam_nologin, pam_access, pam_time
passwordChanger le secret (le nouveau mot de passe est-il acceptable, comment le stocker ?).pam_chauthtok()pam_pwquality, pam_unix
sessionPréparer et défaire l'environnement de la session : journaliser, limites, variables, répertoire personnel.pam_open_session(), pam_close_session()pam_systemd, pam_limits, pam_env, pam_loginuid, pam_mkhomedir, pam_motd

La distinction entre auth et account est celle qui répond à la question de Camille : nous y reviendrons dans « Sous le capot ».

Anatomie d'une ligne

Chaque ligne d'un fichier de /etc/pam.d/ a la forme :

type   contrôle   module   [arguments...]
auth   [success=1 default=ignore]   pam_unix.so nullok
  • type : l'une des quatre piles. Précédé d'un tiret (-session), il indique que l'absence du module n'est pas journalisée : on l'utilise pour un module optionnel qui peut ne pas être installé.
  • contrôle : ce que PAM fait du résultat du module (ci-dessous).
  • module : le nom du fichier .so, relatif au répertoire des modules, ou un chemin absolu.
  • arguments : propres au module, documentés dans sa page de manuel (man pam_unix).

Deux formes particulières : @include common-auth (extension Debian, ou le contrôle include partout ailleurs) insère les lignes d'un autre fichier, et substack fait de même en isolant l'effet des actions done et die à la sous-pile.

Les contrôles simples

PAM exécute les modules d'une pile de haut en bas et calcule un résultat global. Les quatre mots-clés historiques, tels que pam.conf(5) les définit :

ContrôleSi le module réussitSi le module échoue
requiredon continuel'échec est mémorisé, on continue quand même, et la pile échouera à la fin
requisiteon continueon s'arrête immédiatement sur un échec
sufficientsi aucun required n'a échoué avant, on s'arrête immédiatement sur un succèsl'échec est ignoré, on continue
optionalne compte que si c'est le seul module de la pileidem

Pourquoi required continue-t-il après un échec ? Pour ne pas renseigner un attaquant : si la pile s'arrêtait dès que le nom d'utilisateur est inconnu, la différence de comportement (ou de durée) révélerait quels comptes existent. requisite fait le choix inverse, au prix de cette fuite d'information.

La syntaxe entre crochets

Les quatre mots-clés sont des raccourcis. La forme complète associe à chaque code de retour d'un module une action :

[valeur=action valeur=action ... default=action]

Les valeurs sont les codes de retour de PAM : success, auth_err, user_unknown, new_authtok_reqd, acct_expired, perm_denied, ignore... et default pour tous ceux qui ne sont pas cités. Les actions :

ActionEffet
okle code contribue au résultat ; si la pile n'avait pas encore échoué, elle devient « réussie pour l'instant »
donecomme ok, puis arrêt immédiat de la pile (sauf échec antérieur)
badle code est un échec ; si c'est le premier, c'est lui que la pile renverra
diecomme bad, puis arrêt immédiat
ignorele code ne compte pas
resetoublie l'état accumulé et repart de zéro avec le module suivant
un entier Nsaute les N modules suivants de la pile

La page de manuel donne les équivalences exactes :

required    = [success=ok new_authtok_reqd=ok ignore=ignore default=bad]
requisite   = [success=ok new_authtok_reqd=ok ignore=ignore default=die]
sufficient  = [success=done new_authtok_reqd=done default=ignore]
optional    = [success=ok new_authtok_reqd=ok default=ignore]

Le saut (success=1) est l'outil favori de Debian et d'Ubuntu : il permet d'écrire « si ce module réussit, saute par-dessus le module qui refuse ». Une pile se lit alors comme un petit programme avec des goto, et c'est exactement ce que vous allez faire sur sig-app-1.

NSS, au-delà de l'introduction

Le premier cours a montré les lignes passwd: files systemd de /etc/nsswitch.conf. Deux précisions sont utiles pour la suite.

Chaque source correspond à une bibliothèque libnss_<source>.so.2 que la glibc charge à la demande : files lit /etc/passwd, systemd interroge systemd pour les comptes dynamiques, sss interroge le démon SSSD. Installer le paquet libnss-sss ajoute la source sss aux lignes passwd, group, shadow...

Entre deux sources, on peut placer une action conditionnelle de la forme [STATUT=action], où le statut vaut success, notfound, unavail ou tryagain, et l'action return ou continue. Par exemple :

passwd:  files [NOTFOUND=return] sss

signifierait : « si files répond que le compte n'existe pas, arrêtez là ». On ne l'écrit presque jamais pour les comptes, mais il faut savoir le lire. Enfin, getent -s <source> interroge une seule source, ce qui permet de savoir d'où vient un compte :

$ getent -s files passwd camille
$ getent -s sss passwd camille
camille:*:1450001:1450001:Camille Martin:/home/camille:/bin/bash

En pratique

Tout ce qui suit se fait sur sig-app-1, Ubuntu 24.04, puis se transpose à sig-outils (Debian 13) ; les différences sont signalées.

La règle d'or : une session root ouverte

Caution

Avant de toucher un seul fichier de /etc/pam.d/, ouvrez deux sessions SSH sur la machine, et passez root dans l'une d'elles (sudo -i). Ne la fermez pas avant d'avoir vérifié, depuis une troisième connexion, que l'ouverture de session et sudo fonctionnent encore. Une session déjà ouverte n'est pas affectée par une pile cassée ; une nouvelle connexion, si. Sans cette précaution, une faute de frappe vous renvoie à la console série et au mode de secours de Scaleway (leçon 5).

Sauvegardez aussi l'état initial :

$ sudo cp -a /etc/pam.d /root/pam.d.$(date +%F)

L'option -a conserve propriétaires, droits et dates.

Inventorier les services

$ ls /etc/pam.d/

Sortie typique sur une instance Ubuntu 24.04 minimale :

chfn       common-account   common-session                  login     passwd     sshd  sudo
chpasswd   common-auth      common-session-noninteractive   newusers  runuser    su    sudo-i
chsh       common-password  cron                            other     runuser-l  su-l  systemd-user

Chaque fichier porte le nom d'un service ; les fichiers common-* sont des morceaux partagés, inclus par les autres. Deux sont réservés : other (service inconnu) et systemd-user, utilisé par user@.service quand systemd démarre le gestionnaire de services d'un utilisateur.

Lire common-auth ligne par ligne

$ grep -v '^#' /etc/pam.d/common-auth | grep -v '^$'

Sortie typique sur une installation par défaut :

auth	[success=1 default=ignore]	pam_unix.so nullok
auth	requisite			pam_deny.so
auth	required			pam_permit.so

Selon les paquets installés, d'autres lignes peuvent apparaître après pam_permit.so (par exemple pam_cap.so si libpam-cap est présent). Suivons deux scénarios.

Mot de passe correct. pam_unix renvoie success ; l'action associée est 1 : PAM saute le module suivant (pam_deny) et arrive sur pam_permit, qui réussit toujours. Résultat : succès.

Mot de passe faux. pam_unix renvoie auth_err, couvert par default=ignore : ce résultat ne compte pas. PAM passe à pam_deny, qui échoue toujours, avec le contrôle requisite : arrêt immédiat, échec.

Pourquoi ce détour au lieu d'un simple auth required pam_unix.so ? Parce que la structure accepte plusieurs méthodes alternatives. Avec SSSD installé, le bloc devient :

auth	[success=2 default=ignore]	pam_unix.so nullok
auth	[success=1 default=ignore]	pam_sss.so use_first_pass
auth	requisite			pam_deny.so
auth	required			pam_permit.so

Le compte local réussit ? On saute deux modules, jusqu'à pam_permit. Sinon, on tente l'annuaire avec le mot de passe déjà saisi (use_first_pass) ; s'il réussit, on saute pam_deny. Si les deux échouent, pam_deny tranche. Le commentaire du fichier livré par Debian résume la logique : pam_deny est le « fallback if no module succeeds », et pam_permit amorce la pile avec une valeur positive parce que les modules précédents « just jump around ».

L'option nullok de pam_unix autorise un mot de passe vide quand le champ de /etc/shadow est vide. Elle est là par défaut pour ne pas enfermer dehors une installation sans mot de passe ; sur un serveur, aucun compte ne doit avoir ce champ vide (vérification dans les exercices).

Lire le fichier de sshd

$ grep -v '^#' /etc/pam.d/sshd | grep -v '^$'

La sortie ressemble à ceci (version Debian 13, presque identique sur Ubuntu 24.04) :

@include common-auth
account    required     pam_nologin.so
@include common-account
session [success=ok ignore=ignore module_unknown=ignore default=bad]        pam_selinux.so close
session    required     pam_loginuid.so
session    optional     pam_keyinit.so force revoke
@include common-session
session    optional     pam_motd.so  motd=/run/motd.dynamic
session    optional     pam_motd.so noupdate
session    optional     pam_mail.so standard noenv
session    required     pam_limits.so
session    required     pam_env.so
session    required     pam_env.so envfile=/etc/default/locale
session [success=ok ignore=ignore module_unknown=ignore default=bad]        pam_selinux.so open
@include common-password

Ce que chaque ligne apporte :

  • pam_nologin refuse toute connexion non root si /etc/nologin (ou /run/nologin) existe. shutdown crée ce fichier quelques minutes avant un arrêt planifié : c'est pour cela qu'on ne peut plus se connecter juste avant un redémarrage.
  • pam_selinux est sans effet sur Ubuntu et Debian, où SELinux n'est pas actif : module_unknown=ignore évite l'échec si le module est absent.
  • pam_loginuid écrit l'UID de connexion dans /proc/self/loginuid. Cette valeur, l'auid, survit aux sudo et su ; c'est elle qui permettra au sous-système d'audit d'attribuer une commande à Camille même après un sudo -i (leçon 15).
  • pam_keyinit crée un trousseau de clés du noyau propre à la session.
  • pam_motd affiche le message du jour. Sur Ubuntu, la première ligne exécute, en root, les scripts de /etc/update-motd.d/ pour composer /run/motd.dynamic ; la seconde affiche /etc/motd.
  • pam_limits applique /etc/security/limits.conf aux sessions interactives, et seulement à elles : les services lancés par systemd n'y passent pas (leçon 11).
  • pam_env lit /etc/environment et /etc/default/locale.

Le lien entre sshd et PAM est réglé par UsePAM yes dans sshd_config (valeur par défaut des paquets Debian et Ubuntu). La page sshd_config(5) précise ce que cela implique : PAM sert à l'authentification par mot de passe et par KbdInteractiveAuthentication, et les piles account et session sont exécutées « for all authentication types », clé publique comprise. Retenez cette phrase : elle explique la moitié des surprises de cette leçon.

sudo et su

$ cat /etc/pam.d/sudo

Sortie typique (identique sur les deux distributions) :

#%PAM-1.0

# Set up user limits from /etc/security/limits.conf.
session    required   pam_limits.so

@include common-auth
@include common-account
@include common-session-noninteractive

sudo utilise donc la même pile auth que SSH et la console. Tout ce que vous ajouterez à common-auth (verrouillage, second facteur) s'appliquera aussi à sudo, ce qui est souvent voulu et parfois très gênant.

Dans /etc/pam.d/su, une ligne commentée mérite l'attention :

# auth       required   pam_wheel.so

Décommentée, elle réserve su aux membres d'un groupe (wheel par défaut, ou group=sudo). Le guide BP-028 de l'ANSSI donne l'exemple auth required pam_wheel.so use_uid root_only. Sur un serveur où tout passe par sudo, c'est une mesure peu coûteuse.

pam-auth-update : ne pas éditer common-* à la main

Sur Debian et Ubuntu, les fichiers common-* sont générés par pam-auth-update à partir de profils déposés par les paquets dans /usr/share/pam-configs/. Celui de pam_unix, livré par libpam-runtime :

Name: Unix authentication
Default: yes
Priority: 256
Auth-Type: Primary
Auth:
	[success=end default=ignore]	pam_unix.so nullok try_first_pass
Auth-Initial:
	[success=end default=ignore]	pam_unix.so nullok
...
Password-Type: Primary
Password:
	[success=end default=ignore]	pam_unix.so obscure use_authtok try_first_pass yescrypt

Les points à comprendre :

  • Priority ordonne les profils : plus la valeur est haute, plus la ligne est placée tôt.
  • Un bloc Primary est une méthode alternative (un compte local ou l'annuaire) ; un bloc Additional s'ajoute après la décision (pam_systemd, pam_mkhomedir).
  • success=end est converti par l'outil en un saut chiffré vers la fin du bloc primaire : c'est lui qui calcule le 1 ou le 2 que vous avez lus plus haut.
  • Les variantes -Initial servent au premier module du bloc, qui ne peut pas réutiliser un mot de passe déjà saisi (try_first_pass).

Lister et activer des profils :

$ ls /usr/share/pam-configs/
$ sudo pam-auth-update --enable mkhomedir

Sur une instance minimale, la liste ressemble à mkhomedir systemd unix : le profil unix vient de libpam-runtime, systemd de libpam-systemd, mkhomedir de libpam-modules. Chaque paquet de module PAM installé (libpam-pwquality, libpam-sss, libpam-cap...) y ajoute le sien.

Lancé sans argument, pam-auth-update affiche une liste à cocher. --enable et --disable agissent sans question, ce qui convient aux scripts et à Ansible. Sa page de manuel précise deux comportements à connaître : les modifications d'options faites à la main dans la partie gérée sont préservées, mais l'ajout d'un module dans cette partie fait considérer le fichier comme modifié localement, et l'outil cesse alors de le mettre à jour (sauf --force, qui écrase la configuration locale après l'avoir sauvegardée en .pam-old). Une modification manuelle peut donc passer inaperçue pendant des mois, puis empêcher un paquet comme libpam-sss de s'intégrer correctement. Préférez toujours un profil.

Note

Sur Red Hat, AlmaLinux et Rocky Linux, le rôle de pam-auth-update est tenu par authselect, qui génère /etc/pam.d/system-auth et password-auth à partir de profils (sssd, minimal...) et de fonctionnalités (authselect enable-feature with-faillock).

Verrouiller après des échecs : pam_faillock

L'ANSSI recommande de « limiter dans le temps le nombre de tentatives d'authentification » (recommandation R10 du guide sur les mots de passe). Le module historique pam_tally2 a été retiré de Linux-PAM en version 1.5.0 ; son successeur est pam_faillock, présent dans libpam-modules sur Ubuntu 24.04 comme sur Debian 13, avec la commande faillock dans libpam-modules-bin. Aucun profil pam-auth-update n'est livré pour lui : il faut l'écrire.

La difficulté est de placer le module à trois endroits :

  1. preauth, avant pam_unix : refuser tout de suite un compte déjà verrouillé ;
  2. authfail, après un échec de pam_unix : compter l'échec ;
  3. dans la pile account : remettre le compteur à zéro après une authentification réussie.

Le CIS Benchmark d'Ubuntu 24.04 propose deux profils. Le premier, /usr/share/pam-configs/faillock, place le comptage après pam_unix grâce à une priorité plus faible :

Name: Enable pam_faillock to deny access
Default: yes
Priority: 0
Auth-Type: Primary
Auth:
	[default=die]	pam_faillock.so authfail

Le second, /usr/share/pam-configs/faillock_notify, place la vérification avant, avec une priorité plus forte, et ajoute la pile account :

Name: Notify of failed login attempts and reset count upon success
Default: yes
Priority: 1024
Auth-Type: Primary
Auth:
	requisite	pam_faillock.so preauth
Account-Type: Primary
Account:
	required	pam_faillock.so

On active les deux :

$ sudo pam-auth-update --enable faillock faillock_notify

Le bloc auth de common-auth devient :

auth	requisite			pam_faillock.so preauth
auth	[success=2 default=ignore]	pam_unix.so nullok
auth	[default=die]			pam_faillock.so authfail
auth	requisite			pam_deny.so
auth	required			pam_permit.so

Relisez-le avec la méthode précédente. Compte verrouillé : preauth échoue, requisite arrête tout. Bon mot de passe : pam_unix saute deux modules (authfail et pam_deny). Mauvais mot de passe : authfail enregistre l'échec et die arrête la pile.

Les seuils se règlent dans /etc/security/faillock.conf. Les valeurs par défaut (faillock.conf(5)) sont deny = 3 échecs, comptés sur fail_interval = 900 secondes, avec un déverrouillage automatique après unlock_time = 600 secondes. Pour Signalements :

# /etc/security/faillock.conf (extrait)
deny = 5
fail_interval = 900
unlock_time = 900
silent
  • deny = 5 : verrouillage au cinquième échec consécutif dans la fenêtre.
  • unlock_time = 900 : quinze minutes, suffisant pour rendre une attaque en ligne inutile, assez court pour ne pas transformer chaque faute de frappe en ticket. La valeur 0 (ou never) impose un déverrouillage manuel : c'est offrir à n'importe qui le moyen de bloquer un compte à distance.
  • silent : n'affiche pas de message à l'utilisateur pendant preauth. La page pam_faillock(8) avertit que, sans cette option, le module révèle quels comptes existent.
  • root n'est pas verrouillé par défaut ; even_deny_root change cela, avec le risque évident.

Consulter et remettre à zéro :

$ sudo faillock --user camille

La sortie ressemble à ceci :

camille:
When                Type  Source                                           Valid
2026-10-07 09:12:41 TTY   /dev/pts/1                                           V
2026-10-07 09:12:47 TTY   /dev/pts/1                                           V

Type vaut TTY, RHOST (adresse distante) ou SVC (service) selon l'information disponible ; V signifie que l'entrée compte encore dans la fenêtre. Pour lever un verrouillage : sudo faillock --user camille --reset. Dans le journal, le verrouillage se lit ainsi (messages tirés du code source de pam_faillock) :

sudo[4121]: pam_faillock(sudo:auth): Consecutive login failures for user camille account temporarily locked
sudo[4130]: pam_faillock(sudo:auth): User camille is temporarily locked out due to 5 consecutive failed login attempts

Exiger des mots de passe robustes : pam_pwquality

$ sudo apt install libpam-pwquality

Le paquet active de lui-même son profil (Priority: 1024, Conflicts: cracklib), qui ajoute en tête de common-password :

password	requisite			pam_pwquality.so retry=3
password	[success=1 default=ignore]	pam_unix.so obscure use_authtok try_first_pass yescrypt

pam_pwquality demande le nouveau mot de passe, le contrôle, et le passe à pam_unix qui le stocke (use_authtok : ne pas le redemander). retry=3 laisse trois essais.

Quelle politique ? Le guide de l'ANSSI sur les mots de passe raisonne en entropie : pour un jeu d'environ 90 caractères, il propose une longueur minimale de 9 à 11 caractères pour une sensibilité faible à moyenne, de 12 à 14 pour moyenne à forte, et au moins 15 au-delà (tableau 3), recommande de ne pas imposer de longueur maximale (R22) et de contrôler les mots de passe contre des dictionnaires (R27). Il note aussi que les règles de complexité sont contournées par des substitutions prévisibles (P@ssw0rd), mais restent utiles contre les attaques en ligne couplées à une limitation des essais. Le NIST, dans la révision 4 de SP 800-63B (2025), va plus loin : 15 caractères minimum pour un mot de passe utilisé seul, et aucune règle de composition.

Pour des comptes d'administration de serveurs exposés, on retiendra la fourchette haute. Plutôt que de modifier /etc/security/pwquality.conf, déposez un fichier dans le répertoire prévu, lu par ordre alphabétique :

# /etc/security/pwquality.conf.d/50-lyneko.conf
minlen = 15
minclass = 0
dictcheck = 1
usercheck = 1
difok = 5
enforce_for_root
  • minlen = 15 : longueur minimale. Attention au système de crédits : avec les valeurs par défaut (dcredit = 0, etc.), il n'y en a pas, et 15 signifie 15 caractères. Un crédit positif permettrait à chaque chiffre de compter double ; une valeur négative (dcredit = -1) impose au moins un chiffre.
  • minclass = 0 : pas d'obligation de classes de caractères, en accord avec le NIST ; l'ANSSI donne dans BP-028 un exemple plus strict (minlen=12 minclass=3), que vous pouvez préférer si votre politique l'exige.
  • dictcheck = 1 : refus des mots du dictionnaire de cracklib (par défaut).
  • usercheck = 1 : refus d'un mot de passe contenant le nom du compte (par défaut).
  • difok = 5 : au moins cinq caractères différents de l'ancien mot de passe (par défaut 1).
  • enforce_for_root : sans cette option, root reçoit l'avertissement mais peut imposer un mot de passe faible.

Un mot de passe refusé produit l'un des messages de libpwquality, préfixé par BAD PASSWORD: :

BAD PASSWORD: The password is shorter than 15 characters
BAD PASSWORD: The password fails the dictionary check - it is based on a dictionary word

Important

pam_pwquality ne contrôle que les mots de passe changés après son installation et par une voie qui passe par PAM (passwd, chpasswd). Un mot de passe déjà en place, ou un hachage injecté par usermod -p ou par cloud-init, n'est jamais vérifié.

Restreindre qui peut entrer : pam_access

Sur sig-app-1, seuls les membres du groupe equipe-signalements doivent pouvoir se connecter, et uniquement depuis le réseau privé pn-signalements (par le bastion) ou la console. Le fichier de sshd contient déjà la ligne, commentée :

# account  required     pam_access.so

Décommentez-la dans /etc/pam.d/sshd (le fichier n'est pas géré par pam-auth-update, l'éditer est normal), puis décrivez la politique dans /etc/security/access.conf :

# /etc/security/access.conf
# permission : utilisateurs ou (groupes) : origines
+ : root : LOCAL
+ : (equipe-signalements) : 172.16.8.0/22 LOCAL
- : ALL : ALL

access.conf(5) décrit trois champs séparés par :. Les groupes s'écrivent entre parenthèses pour les distinguer des comptes. LOCAL désigne les connexions qui ne viennent pas du réseau (console, su) ; une origine réseau se donne par adresse, nom ou réseau. Surtout, la première ligne qui correspond l'emporte : la règle - : ALL : ALL doit rester la dernière. Un refus est journalisé par le module sous la forme :

sshd[5320]: pam_access(sshd:account): access denied for user `camille' from `203.0.113.50'

pam_access est en pile account : il s'applique donc aussi aux connexions par clé, contrairement à tout ce qui est dans auth.

Les autres modules à connaître

  • pam_systemd (profil systemd, bloc Additional, sessions interactives uniquement) enregistre la session auprès de systemd-logind : création de /run/user/<UID>, de la variable XDG_RUNTIME_DIR, de la tranche user-<UID>.slice et du gestionnaire user@<UID>.service. C'est grâce à lui que loginctl sait qui est connecté et que loginctl terminate-user camille ferme toutes ses sessions.
  • pam_mkhomedir (profil mkhomedir, désactivé par défaut) crée le répertoire personnel à la première connexion, à partir de /etc/skel. Indispensable avec un annuaire, où les comptes n'ont pas été créés par adduser.
  • pam_umask règle le masque des sessions. Debian 13 l'active avec la gestion des usergroups (annoncée dans les notes du paquet pam de Debian) : si le groupe principal porte le nom de l'utilisateur, les fichiers créés sont modifiables par le groupe ; l'option nousergroups désactive ce comportement.
  • pam_limits : voir la leçon 11. Les notes de Debian signalent qu'à partir de Linux-PAM 1.7.0, le module ne réinitialise plus toutes les limites des sessions : celles de systemd s'appliquent, sauf ce qui est écrit dans limits.conf (l'option set_all rétablit l'ancien comportement).

Mots de passe : hachage, login.defs et expiration

Le premier cours a montré le préfixe $y$ de yescrypt dans /etc/shadow. Qui le choisit ? Pour tout changement qui passe par PAM, c'est l'option yescrypt de pam_unix dans common-password. Le fichier /etc/login.defs contient aussi une directive ENCRYPT_METHOD, mais elle ne sert qu'aux outils de la suite shadow qui hachent eux-mêmes sans passer par PAM (comme chgpasswd, ou chpasswd quand on lui impose une méthode) ; et elle diffère entre nos deux distributions : SHA512 dans le login.defs d'Ubuntu 24.04, YESCRYPT dans celui de Debian 13. Le login.defs de Debian 13 recommande en commentaire une valeur cohérente avec la configuration PAM : sur sig-app-1, passez-la à YESCRYPT.

yescrypt est une fonction de dérivation dite memory-hard (coûteuse en calcul et en mémoire, ce qui pénalise les cartes graphiques des attaquants), exactement ce que recommande l'ANSSI (R29 du guide sur les mots de passe, repris par R68 de BP-028). BP-028 donne l'exemple pam_unix.so obscure yescrypt rounds=11 pour augmenter le coût ; mesurez l'effet sur une machine de test avant de le déployer, car chaque authentification devient plus lente et plus gourmande.

L'expiration est stockée dans /etc/shadow et se lit avec chage :

$ sudo chage -l camille

Sortie typique :

Last password change					: Sep 12, 2026
Password expires					: never
Password inactive					: never
Account expires						: Jan 02, 1970
Minimum number of days between password change		: 0
Maximum number of days between password change		: 99999
Number of days of warning before password expires	: 7

Account expires: Jan 02, 1970 est la trace du usermod --expiredate 1 de la procédure de départ. Les options utiles :

CommandeEffet
chage -d 0 dominiqueoblige à changer le mot de passe à la prochaine connexion (mot de passe initial)
chage -M 365 -W 14 adm-dominiqueexpiration du mot de passe après 365 jours, avertissement 14 jours avant
chage -I 30 adm-dominique30 jours après l'expiration du mot de passe, le compte devient inutilisable
chage -E 2027-03-31 prestataireexpiration du compte à une date : idéal pour un intervenant extérieur

PASS_MAX_DAYS 99999 dans login.defs, soit pas d'expiration, est le réglage par défaut des deux distributions, et il est cohérent avec l'état de l'art. L'ANSSI recommande de ne pas imposer par défaut d'expiration aux comptes non sensibles quand les mots de passe sont robustes (R24), et d'en imposer une, par exemple entre 1 et 3 ans, aux comptes à privilèges protégés par un simple mot de passe (R25). Le NIST interdit l'expiration périodique et n'impose le changement qu'en cas d'indice de compromission. Un changement tous les 90 jours fabrique des mots de passe en série (Signalements2026!, Signalements2027!) et n'apporte rien contre un vol de hachage récent. En revanche, chage -E sur les comptes temporaires est une excellente pratique : un compte de prestataire qui s'éteint seul ne dépend plus de la mémoire de quelqu'un.

Passer à un annuaire : SSSD

Faites le compte pour Signalements : trois machines aujourd'hui, et chaque arrivée ou départ demande trois fois adduser, usermod, la clé SSH, sudoers. Un oubli sur une seule machine, et la réponse à la responsable sécurité est fausse. Un annuaire centralise les comptes, les groupes, souvent les clés SSH et les règles sudo ; chaque machine l'interroge. Les familles courantes :

  • LDAP (OpenLDAP, 389 Directory Server) : le protocole et un serveur d'annuaire générique ;
  • FreeIPA (Red Hat IdM) : LDAP, Kerberos, autorité de certification, règles sudo et contrôle d'accès par hôte, intégrés ;
  • Active Directory : l'annuaire de Microsoft, très présent chez les collectivités clientes, auquel un Linux peut se joindre.

Côté machine, le client moderne est SSSD (System Security Services Daemon) : un démon qui interroge l'annuaire, met en cache les réponses (et, si on le demande, les empreintes des mots de passe pour fonctionner hors ligne), et se branche à la fois sur NSS (libnss-sss) et sur PAM (libpam-sss, pam_sss.so).

Supposons que Lyneko dispose d'un annuaire LDAP joignable en LDAPS sur annuaire.lyneko.internal (le domaine .internal est réservé aux usages privés). Installation :

$ sudo apt install sssd-ldap libnss-sss libpam-sss
$ sudo pam-auth-update --enable mkhomedir

Les paquets ajoutent sss à /etc/nsswitch.conf et le profil sss à PAM ; on active en plus la création du répertoire personnel. Configuration, dans /etc/sssd/sssd.conf :

[sssd]
services = nss, pam, ssh
domains = lyneko.internal

[domain/lyneko.internal]
id_provider = ldap
auth_provider = ldap
access_provider = simple
simple_allow_groups = equipe-signalements

ldap_uri = ldaps://annuaire.lyneko.internal
ldap_search_base = dc=lyneko,dc=internal
ldap_default_bind_dn = uid=sssd-sig,ou=services,dc=lyneko,dc=internal
ldap_default_authtok = <mot de passe du compte sssd-sig>
ldap_tls_reqcert = demand
ldap_tls_cacert = /usr/local/share/ca-certificates/lyneko-ca.crt

cache_credentials = true
  • services : les « répondeurs » à démarrer (NSS, PAM, et ssh pour les clés publiques stockées dans l'annuaire).
  • id_provider et auth_provider : d'où viennent l'identité et la vérification du mot de passe (ldap, ipa, ad, krb5...).
  • access_provider = simple avec simple_allow_groups : seuls les membres de ce groupe d'annuaire peuvent se connecter à cette machine. C'est le contrôle d'accès par hôte, côté account.
  • ldap_default_bind_dn : un compte de service dédié, en lecture seule. L'ANSSI demande de ne jamais utiliser un compte d'administration de l'annuaire pour ces requêtes (R70 de BP-028).
  • ldap_tls_reqcert = demand : le certificat du serveur est vérifié, sinon la connexion est refusée (R69 : authentifier le serveur et protéger le canal).
  • cache_credentials = true (faux par défaut) : permet de se connecter si l'annuaire est injoignable, avec le dernier mot de passe validé.

sssd.conf(5) exige que le fichier appartienne à root et ne soit lisible et modifiable que par lui :

$ sudo chmod 600 /etc/sssd/sssd.conf
$ sudo systemctl restart sssd

Vérifier, du plus simple au plus complet :

$ getent passwd camille
camille:*:1450001:1450001:Camille Martin:/home/camille:/bin/bash
$ id camille
uid=1450001(camille) gid=1450001(camille) groups=1450001(camille),1450010(equipe-signalements)
$ sudo sssctl domain-status lyneko.internal
$ sudo sssctl user-checks camille -s sshd -a acct

La sortie de getent montre * dans le champ mot de passe : le compte ne vient pas de /etc/passwd, et son mot de passe n'est pas sur la machine. sssctl user-checks exécute la pile PAM du service indiqué pour ce compte, sans ouvrir de session : c'est l'outil de diagnostic le plus direct. Pour les clés SSH stockées dans l'annuaire, sshd_config reçoit :

AuthorizedKeysCommand /usr/bin/sss_ssh_authorizedkeys
AuthorizedKeysCommandUser nobody

Désormais, le départ de Camille se traite dans l'annuaire : désactivation du compte, retrait du groupe equipe-signalements. Chaque machine le constate à l'expiration de son cache (entry_cache_timeout, 5 400 secondes par défaut), ou immédiatement après sudo sss_cache -u camille.

Pour Active Directory ou FreeIPA, l'outil realm (paquet realmd) automatise la découverte du domaine, l'inscription de la machine et la génération de sssd.conf : sudo realm join --user=admin-jointure ad.mairie.example. Le résultat est le même SSSD, avec id_provider = ad ou ipa.

L'alternative : des identités courtes plutôt que des comptes

Un annuaire résout la propagation, mais suppose un service critique de plus. Pour des serveurs d'infrastructure, une autre approche gagne du terrain : une autorité de certification SSH. Chaque serveur fait confiance à la clé de l'autorité (TrustedUserCAKeys dans sshd_config) ; une personne obtient, après authentification auprès d'un fournisseur d'identité, un certificat valable quelques heures, qui liste les comptes autorisés (principals). Il n'y a plus de authorized_keys à nettoyer : le départ de Camille se traite en coupant son accès à l'autorité, et ses certificats existants expirent d'eux-mêmes dans la journée. Le cours SSH détaillera la mise en place ; retenez qu'à ce stade PAM continue d'exécuter les piles account et session pour ces connexions.

Sous le capot

La conversation entre sshd et PAM

Quand Camille se connectait par clé à sig-app-1, voici les appels que sshd faisait, dans l'ordre :

pam_start("sshd", "camille", &conv, &pamh)
  (authentification par clé publique : faite par sshd lui-même, pam_authenticate() n'est PAS appelé)
pam_acct_mgmt(pamh)          -> pile account : pam_nologin, pam_access, pam_unix, pam_faillock
pam_setcred(pamh, ESTABLISH_CRED)
pam_open_session(pamh)       -> pile session : pam_loginuid, pam_systemd, pam_limits, pam_motd...
   ... session de Camille ...
pam_close_session(pamh)
pam_end(pamh)

Voilà la réponse à la question du début. usermod --lock modifie le hachage du mot de passe, que seule la pile auth consulte : une connexion par clé ne la traverse pas. usermod --expiredate 1 modifie la date d'expiration du compte, que pam_unix vérifie dans la pile account (la page pam_unix(8) cite les champs expire, last_change, max_change...), et cette pile est exécutée pour tous les types d'authentification. Le refus apparaît dans le journal avec le texte exact du module :

sshd[6012]: pam_unix(sshd:account): account camille has expired (account expired)

Le corollaire est important : tout ce qui est en pile auth est contourné par une connexion par clé. pam_faillock ne compte pas les échecs de clé, pam_pwquality ne sert à rien à qui n'a pas de mot de passe, et un second facteur ajouté dans common-auth ne protège que les connexions par mot de passe... et sudo. Pour appliquer une règle à tout le monde, il faut la mettre en account ou en session, ou la confier à sshd lui-même (AuthenticationMethods publickey,keyboard-interactive pour exiger clé et second facteur, sujet du cours SSH).

Comment libpam suit les sauts

À la lecture de la configuration, libpam construit, pour chaque type, un tableau de modules et, pour chacun, un tableau d'actions indexé par code de retour. À l'exécution, elle parcourt le tableau avec un indice ; une action entière N ajoute simplement N à cet indice. C'est pourquoi les sauts sont fragiles : insérer une ligne entre un module et sa cible décale le saut sans erreur ni avertissement. Ajouter pam_faillock.so authfail à la main entre pam_unix (success=1) et pam_deny fait atterrir le succès sur pam_deny... et plus personne n'entre. pam-auth-update recalcule les sauts à chaque génération ; une édition manuelle, non.

Si un module est introuvable, libpam l'enregistre comme « module défectueux » qui échoue à chaque appel, et journalise (texte de libpam/pam_handlers.c) :

sshd[7001]: PAM unable to dlopen(/usr/lib/x86_64-linux-gnu/security/pam_faillok.so): /usr/lib/x86_64-linux-gnu/security/pam_faillok.so: cannot open shared object file: No such file or directory
sshd[7001]: PAM adding faulty module: /usr/lib/x86_64-linux-gnu/security/pam_faillok.so

Avec le contrôle required, une faute de frappe dans un nom de module ferme donc la porte à tout le monde.

Où vivent les compteurs et les caches

pam_faillock écrit un fichier par compte dans /var/run/faillock/, c'est-à-dire /run/faillock/ puisque /var/run est un lien vers /run : un système de fichiers en mémoire (tmpfs, voir tmpfs) : un redémarrage remet tous les compteurs à zéro. C'est voulu (un compte verrouillé ne survit pas à un redémarrage), mais il faut le savoir avant de présenter ce mécanisme comme une trace d'audit ; la trace, c'est le journal.

SSSD se compose d'un processus de moniteur, de répondeurs (sssd_nss, sssd_pam, sssd_ssh), qui parlent aux bibliothèques NSS et PAM par des sockets locales, et d'un fournisseur par domaine (sssd_be), qui parle à l'annuaire. Les réponses sont stockées dans une base locale sous /var/lib/sss/db/, et un cache en mémoire partagée sous /var/lib/sss/mc/ permet à libnss_sss de répondre sans même contacter le démon. C'est ce double cache qui rend les machines résilientes à une coupure de l'annuaire, et qui explique qu'un changement fait dans l'annuaire ne soit pas visible instantanément.

Pourquoi un programme statique ne voit pas les comptes d'annuaire

NSS repose sur le chargement dynamique de libnss_sss.so.2 par la glibc. Un binaire lié statiquement (certains outils écrits en Go, des binaires compilés avec musl) ne peut pas charger ces modules : il ne lit que /etc/passwd et ignore l'annuaire. Si un outil affiche un UID numérique là où ls -l affiche un nom, c'est souvent la raison.

Pièges courants

Se couper l'accès en éditant common-auth. Le symptôme : sudo répond Sorry, try again. à un mot de passe correct, ou une nouvelle connexion SSH par mot de passe échoue. Diagnostic depuis la session root restée ouverte : journalctl -t sudo -t sshd -n 50 montre le module en cause. Corrigez, ou restaurez la copie /root/pam.d.<date>. Si plus aucune session n'est ouverte, il reste la console de Scaleway et le mode de secours (leçon 5).

Un saut décalé. Une ligne ajoutée à la main entre pam_unix et pam_deny sans mettre à jour success=1. Aucune erreur de syntaxe, mais plus personne n'entre, ou au contraire tout le monde entre si la cible devient pam_permit. Relisez toujours la pile en simulant un bon et un mauvais mot de passe, et testez avec pamtester (paquet du même nom) : pamtester sudo dominique authenticate acct_mgmt exécute les piles auth et account du service sudo pour ce compte.

pam-auth-update qui « ne fait plus rien ». Après une modification manuelle d'un common-*, l'outil considère le fichier comme modifié localement et le laisse en l'état : un nouveau profil, comme celui de libpam-sss, n'est pas intégré. Comparez avec un pam-auth-update sur une machine neuve, puis réintégrez vos modifications sous forme de profil.

Un administrateur verrouillé par sudo. pam_faillock est dans common-auth, donc dans sudo. Cinq fautes de frappe dans un script qui appelle sudo, et le compte est verrouillé quinze minutes, y compris pour SSH par mot de passe. faillock --user <nom> --reset depuis un autre compte d'administration, ou attendre. Prévoyez un compte de secours (voir « En production »).

« J'ai activé faillock et rien n'est compté. » Vous testez par clé SSH, qui ne traverse pas la pile auth. Testez par sudo ou par la console.

pam_access sans effet ou trop efficace. La première règle qui correspond gagne : un - : ALL : ALL placé en tête bloque tout le monde, y compris root depuis la console (si la ligne pam_access figure aussi dans /etc/pam.d/login). Un groupe écrit sans parenthèses est interprété comme un nom de compte et ne correspond à personne.

SSSD refuse de démarrer. Le plus souvent, les droits de /etc/sssd/sssd.conf : le fichier doit être à root, en mode 600. systemctl status sssd et journalctl -u sssd le disent explicitement ; sudo sssctl config-check vérifie la syntaxe et les droits.

Un compte d'annuaire désactivé qui entre encore. Le cache : SSSD répond avec des données qui peuvent dater de entry_cache_timeout, et cache_credentials = true permet une authentification hors ligne si l'annuaire est injoignable. sss_cache -E invalide tout le cache sur une machine ; pour un départ, désactivez dans l'annuaire et forcez l'invalidation sur les machines sensibles.

Les limites de session qui changent après une mise à jour vers Debian 13. Linux-PAM 1.7.0 ne réinitialise plus toutes les limites des sessions dans pam_limits : des valeurs qui venaient implicitement de PAM proviennent désormais de systemd. Si un outil lancé en SSH se plaint du nombre de fichiers ouverts, comparez ulimit -a avant et après, et écrivez explicitement les limites voulues.

Sécurité

Les fichiers PAM sont une cible. Un attaquant devenu root n'a pas besoin de garder une porte dérobée réseau : une ligne auth sufficient pam_permit.so en tête de common-auth, ou un pam_unix.so remplacé par une version qui accepte un mot de passe secret et note les autres, lui suffit. Ces modifications passent inaperçues à l'œil. D'où le contrôle d'intégrité de /etc/pam.d/ et de /usr/lib/x86_64-linux-gnu/security/ (AIDE, dpkg --verify libpam-modules) et la surveillance des écritures dans ces répertoires par auditd, traités à la leçon 15. Méfiez-vous de même de pam_exec, qui exécute un programme à chaque authentification et peut lui transmettre le mot de passe (expose_authtok).

Pas de mot de passe vide. nullok reste dans la configuration par défaut ; la seule garantie est qu'aucun compte n'ait un second champ vide dans /etc/shadow (sudo awk -F: '$2 == ""' /etc/shadow doit ne rien afficher).

Ne pas révéler l'existence des comptes. silent pour pam_faillock, required plutôt que requisite en tête de pile, et le même message d'échec pour un compte inconnu et un mauvais mot de passe.

Le canal vers l'annuaire. L'ANSSI demande que l'authentification distante par PAM utilise un protocole sécurisé (R67) et que NSS authentifie le serveur d'annuaire et protège le canal (R69). Concrètement : ldaps:// ou StartTLS, ldap_tls_reqcert = demand, jamais de LDAP en clair, même sur le réseau privé. Un faux annuaire qui répondrait aux requêtes de sig-app-1 pourrait créer un compte d'UID 0.

Le cache hors ligne est une copie des empreintes. Avec cache_credentials = true, les empreintes des mots de passe d'annuaire sont stockées sur la machine (dans /var/lib/sss/db/) : quiconque obtient root peut les copier pour les attaquer hors ligne. Activez-le là où la continuité l'exige, et limitez sa durée par offline_credentials_expiration dans la section [pam] (0, soit illimité, par défaut).

L'authentification multifacteur. C'est la première recommandation du guide de l'ANSSI sur l'authentification. Sous Linux, PAM la permet par pam_u2f (clé matérielle FIDO2, paquet libpam-u2f) ou par un code temporaire TOTP (paquet libpam-google-authenticator). Ajoutée à sudo, elle empêche qu'une session volée suffise à obtenir root. Sur des serveurs, on préfère souvent placer le second facteur avant la machine : au fournisseur d'identité qui délivre les certificats SSH, ou au bastion.

Les comptes de service n'ont pas de mot de passe. signalements a * ou ! dans /etc/shadow et /usr/sbin/nologin pour shell : il ne peut ni s'authentifier ni ouvrir de session. Il ne doit jamais apparaître dans l'annuaire des personnes.

RGPD. Les journaux d'authentification contiennent des identifiants et des adresses IP, donc des données personnelles. Leur conservation suit la politique définie à la leçon 12 ; un annuaire, lui, concentre les données des personnes : le minimiser (pas de date de naissance pour ouvrir un shell).

En production

Choisir son modèle d'identité. Trois serveurs et une équipe stable peuvent vivre avec des comptes locaux, à condition qu'ils soient déployés par un outil de gestion de configuration (le cours Ansible y viendra) et que la procédure de départ soit écrite et rejouée sur toutes les machines. Au-delà, ou dès qu'un client public exige de prouver la révocation des accès, un annuaire ou une autorité de certification SSH devient nécessaire. Beaucoup d'équipes combinent les deux : annuaire pour l'identité et les groupes, certificats courts pour SSH.

Un compte de secours, documenté. Quand l'authentification dépend d'un annuaire, d'un second facteur ou d'un verrouillage, il faut un accès qui ne dépend de rien de tout cela : un compte local d'urgence (break-glass), avec une clé ou un mot de passe long conservé sous enveloppe dans le coffre de l'équipe, dont chaque utilisation déclenche une alerte et une revue. Et la console série de Scaleway, protégée par l'IAM du projet.

La configuration PAM est du code. Profils pam-auth-update, faillock.conf, pwquality.conf.d/, access.conf et sssd.conf vivent dans un dépôt et se déploient par l'outil de configuration, testés d'abord sur une machine jetable avec pamtester et une vraie connexion. Une modification manuelle de PAM sur un serveur de production est une dette dans le sens de la leçon 1.

Surveiller les signaux. Un pic de authentication failure dans le journal, des verrouillages pam_faillock, un sss_cache qui ne se rafraîchit plus (sssctl domain-status en mode hors ligne) sont des alertes utiles, une fois les journaux centralisés sur sig-outils.

Revue des accès. Une fois par trimestre, comparez la liste des personnes autorisées (membres de equipe-signalements, clés SSH du projet Scaleway, comptes locaux restants avec getent passwd | awk -F: '$3 >= 1000 && $3 < 60000') à la liste des personnes réellement dans l'équipe. N'oubliez pas les clés du projet Scaleway : elles réapparaissent dans /root/.ssh/authorized_keys à chaque démarrage, et le départ de Camille n'est complet qu'une fois sa clé retirée de l'IAM.

Exercices

1. Lire une pile (niveau 100). Voici le bloc auth d'une machine héritée :

auth	sufficient	pam_unix.so nullok
auth	required	pam_sss.so use_first_pass

Un compte local saisit son bon mot de passe ; puis un compte d'annuaire saisit le sien ; puis quelqu'un saisit un mot de passe faux. Que se passe-t-il dans chaque cas ? Réécrivez ce bloc dans le style Debian, avec des sauts.

Solution

Compte local, bon mot de passe : pam_unix réussit, sufficient et aucun required n'a échoué avant : succès immédiat, pam_sss n'est pas appelé. Compte d'annuaire : pam_unix échoue (ignoré, puisque sufficient = default=ignore), pam_sss réussit avec le mot de passe déjà saisi : succès. Mot de passe faux : pam_unix échoue (ignoré), pam_sss échoue en required : échec.

Style Debian :

auth	[success=2 default=ignore]	pam_unix.so nullok
auth	[success=1 default=ignore]	pam_sss.so use_first_pass
auth	requisite			pam_deny.so
auth	required			pam_permit.so

L'intérêt de la seconde forme : ajouter une troisième méthode, ou un module Additional après pam_permit, ne change pas la logique, et pam-auth-update la régénère.

2. Le départ, vraiment (niveau 200). Sur sig-outils, Camille avait un compte local avec une clé SSH. Un collègue a exécuté seulement sudo passwd -l camille. Camille peut-elle encore se connecter ? Prouvez-le par le raisonnement PAM, puis donnez les commandes qui ferment réellement l'accès et celle qui vérifie le résultat.

Solution

Oui, par clé. passwd -l verrouille le hachage du mot de passe, que seule la pile auth consulte ; avec une clé, sshd authentifie lui-même et n'appelle pas pam_authenticate(). Il appelle en revanche pam_acct_mgmt() : pam_unix y vérifie l'expiration du compte. Donc :

$ sudo usermod --expiredate 1 camille
$ sudo pkill -KILL -u camille
$ sudo chage -l camille | grep 'Account expires'
Account expires						: Jan 02, 1970

Pour une défense en profondeur, retirez aussi la clé (/home/camille/.ssh/authorized_keys), ses règles sudo, sa clé du projet Scaleway (sinon elle reviendra dans /root/.ssh/authorized_keys au prochain démarrage), et restreignez SSH à un groupe avec pam_access ou AllowGroups. Une tentative de connexion produira dans le journal pam_unix(sshd:account): account camille has expired (account expired).

3. Verrouillage sans s'enfermer (niveau 200). Mettez en place pam_faillock sur une machine de test Ubuntu 24.04 : 5 échecs en 15 minutes, déverrouillage après 15 minutes, sans révéler l'existence des comptes. Décrivez la procédure complète, y compris les précautions et le test, et le contenu attendu de common-auth.

Solution
  1. Ouvrir deux sessions, une en root ; cp -a /etc/pam.d /root/pam.d.$(date +%F).
  2. Créer /usr/share/pam-configs/faillock (Priority 0, Auth: [default=die] pam_faillock.so authfail) et /usr/share/pam-configs/faillock_notify (Priority 1024, Auth: requisite pam_faillock.so preauth, Account: required pam_faillock.so).
  3. /etc/security/faillock.conf : deny = 5, fail_interval = 900, unlock_time = 900, silent.
  4. sudo pam-auth-update --enable faillock faillock_notify.
  5. Vérifier common-auth :
auth	requisite			pam_faillock.so preauth
auth	[success=2 default=ignore]	pam_unix.so nullok
auth	[default=die]			pam_faillock.so authfail
auth	requisite			pam_deny.so
auth	required			pam_permit.so
  1. Test depuis une troisième session, avec un compte de test : sudo -k; sudo true avec cinq mauvais mots de passe, puis le bon, qui doit être refusé ; sudo faillock --user test montre cinq entrées valides ; journalctl -t sudo montre Consecutive login failures for user test account temporarily locked. faillock --user test --reset débloque. Vérifier enfin qu'un bon mot de passe après quelques échecs (moins de cinq) remet le compteur à zéro. Ne fermer la session root qu'ensuite.

4. Concevoir l'identité de Signalements (niveau 300). Lyneko va confier à deux prestataires l'exploitation de nuit de Signalements, pour une mairie qui exige de pouvoir prouver, à tout moment, qui a accès aux serveurs. Proposez une architecture d'authentification pour sig-app-1, sig-app-2 et sig-outils : source des identités, contrôle d'accès par machine, SSH, sudo, comptes temporaires, secours, et ce que vous montreriez à l'auditeur. Discutez au moins deux arbitrages.

Solution

Une proposition défendable :

  • Identités dans un annuaire (FreeIPA, ou l'annuaire de Lyneko en LDAPS), chaque personne avec son compte nominatif ; les prestataires dans un groupe prestataires-signalements dont les comptes portent une date d'expiration (fin de contrat).
  • Machines reliées par SSSD, access_provider = simple (ou les règles HBAC de FreeIPA) limité à equipe-signalements et prestataires-signalements ; cache_credentials activé avec offline_credentials_expiration court.
  • SSH par clés publiées dans l'annuaire (sss_ssh_authorizedkeys) ou, mieux, par certificats courts délivrés après authentification multifacteur ; connexion seulement depuis le bastion (pam_access ou groupe de sécurité Scaleway) ; plus aucune clé de projet Scaleway sauf celle du compte de secours.
  • sudo : règles par groupe (stockées dans l'annuaire ou déployées par Ansible), droits limités pour les prestataires (redémarrer et consulter Signalements) ; second facteur pam_u2f sur sudo pour les droits larges.
  • Secours : un compte local, clé sous enveloppe, alerte à chaque usage ; la console Scaleway réservée à deux personnes dans l'IAM.
  • Preuves pour l'auditeur : export des membres des groupes à date, journal des modifications de l'annuaire, journaux d'authentification centralisés sur sig-outils, procédure de départ et compte rendu de la dernière revue trimestrielle.

Arbitrages : l'annuaire devient un point de défaillance (d'où le cache et le compte de secours, qui eux-mêmes affaiblissent la révocation immédiate) ; les certificats courts suppriment la gestion des clés mais demandent une autorité bien protégée ; le second facteur sur sudo freine les automatisations, qui doivent utiliser des comptes de service sans sudo interactif.

Récapitulatif

  • NSS dit qui existe (/etc/nsswitch.conf, getent, getent -s) ; PAM dit qui prouve son identité, qui a le droit d'entrer et comment préparer la session (/etc/pam.d/<service>).
  • PAM a quatre piles : auth (preuve), account (droit d'entrer maintenant), password (changement du secret), session (préparation). Une connexion SSH par clé saute la pile auth mais traverse account et session : c'est pourquoi --expiredate bloque une clé et passwd -l non.
  • Les contrôles required, requisite, sufficient, optional sont des raccourcis de la syntaxe [valeur=action] ; Debian et Ubuntu utilisent des sauts (success=1) entre pam_unix, pam_deny et pam_permit.
  • Sur Debian et Ubuntu, les common-* sont générés par pam-auth-update à partir de profils dans /usr/share/pam-configs/ ; une édition manuelle les fige. Sur Red Hat : authselect.
  • Toujours une session root ouverte et une sauvegarde de /etc/pam.d avant de modifier ; tester avec une nouvelle connexion et pamtester.
  • pam_faillock (preauth, authfail, account ; /etc/security/faillock.conf, faillock --reset) ; pam_pwquality (/etc/security/pwquality.conf.d/) ; pam_access (/etc/security/access.conf, première règle gagnante).
  • Mots de passe hachés en yescrypt par pam_unix ; expiration lue par chage -l ; pas d'expiration périodique pour les comptes ordinaires (ANSSI R24, NIST), une expiration pour les comptes temporaires (chage -E).
  • Un annuaire relié par SSSD centralise comptes, groupes et clés ; attention au cache, au canal TLS et au compte de lecture dédié. Les certificats SSH courts sont l'alternative moderne pour l'accès aux serveurs.

Pour aller plus loin

  • Les pages pam.conf(5), pam_unix(8), pam_faillock(8) et access.conf(5) : la syntaxe des contrôles y est décrite avec précision.
  • Le Linux-PAM System Administrators' Guide, livré dans le paquet libpam-doc, pour l'ensemble des modules.
  • Le guide de l'ANSSI Recommandations relatives à l'authentification multifacteur et aux mots de passe, et la section 7.2 du guide BP-028.
  • La documentation de SSSD (sssd.conf(5), sssd-ldap(5), sssd-ad(5), sssctl(8)) et celle de FreeIPA pour un annuaire complet.
  • Le cours SSH, pour l'authentification par certificats et l'exigence de plusieurs facteurs dans sshd.
  • La leçon suivante, Durcir un serveur.
+20 XP Carte du ciel →Mon cosmonaute →

Sources