Comment ajouter l'authentification à deux facteurs à SSH avec TOTP

Installez libpam-google-authenticator, lancez google-authenticator pour chaque utilisateur, ajoutez auth required pam_google_authenticator.so à /etc/pam.d/sshd, et définissez AuthenticationMethods publickey,keyboard-interactive et KbdInteractiveAuthentication yes dans la configuration du serveur SSH. Chaque connexion demande alors la clé et un code à six chiffres. Gardez une session ouverte pendant vos essais, car une erreur peut vous bloquer dehors.

Mis à jour le . Exécuté de bout en bout avec OpenSSH 10.0 et libpam-google-authenticator sur Debian 13, dans un conteneur jetable, avec de vraies connexions et des codes générés par oathtool : codes corrects, faux, réutilisés et anciens, codes de secours, utilisateurs sans fichier de secret et mode de fichier incorrect.

Avant de commencer

Restez connecté au serveur dans une fenêtre jusqu'à la fin. Vérifiez aussi que vous disposez d'une console chez votre hébergeur. Configurez d'abord la connexion par clé : voir comment générer une clé.

Étape 1 : installer le module

sudo apt update
sudo apt install libpam-google-authenticator

Malgré son nom, il n'exige aucun compte Google et fonctionne avec n'importe quelle app TOTP. Le serveur vérifie lui-même les codes.

Étape 2 : créer un secret pour chaque utilisateur

Lancez ceci en tant qu'utilisateur qui se connectera, pas avec sudo :

google-authenticator -t -d -f -r 3 -R 30 -w 3
OptionSignification
-tCodes basés sur le temps
-dUn code ne peut pas être utilisé deux fois. Nous l'avons vérifié : une seconde connexion avec le même code a échoué.
-fÉcrire le fichier sans demander
-r 3 -R 30Au plus 3 connexions toutes les 30 secondes. Pensez-y pendant vos tests : des essais rapides répétés échouent même avec un code correct.
-w 3Accepter le code actuel et ceux juste avant et après, pour tolérer un léger écart d'horloge

Scannez le QR code avec votre app et saisissez un code pour confirmer. Si vous ne pouvez pas le scanner, saisissez la clé secrète à la main dans l'app. L'outil affiche aussi un lien vers une page de graphique de Google et avertit que l'ouvrir expose le secret à Google : ne l'ouvrez donc pas. La commande affiche cinq codes de secours. Enregistrez-les ailleurs que sur ce serveur et sur votre téléphone. Chacun fonctionne une fois à la place d'un code.

Cela crée ~/.google_authenticator avec le mode 400. Gardez-le ainsi : dans notre test, avec le mode 644, les connexions échouaient même avec un code correct.

Étape 3 : configurer PAM

Modifiez /etc/pam.d/sshd. Commentez la ligne qui demande le mot de passe, et ajoutez deux lignes dessous :

# @include common-auth
auth required pam_google_authenticator.so nullok
auth required pam_permit.so
  • Pourquoi commenter common-auth : si vous la laissez, la connexion de notre test demandait le code puis aussi le mot de passe du système.
  • Ce que fait nullok : un utilisateur sans fichier de secret peut se connecter sans code, ce qui permet de déployer utilisateur par utilisateur. Retirez-le quand tout le monde est configuré.
  • Pourquoi pam_permit.so : nullok fait s'abstenir le module, et PAM a besoin d'au moins un module qui réussisse. Sans cette ligne, les utilisateurs sans fichier de secret étaient refusés dans notre test. Ceux qui ont un fichier ont toujours besoin du bon code.

Une fois nullok retiré, vous pouvez retirer aussi pam_permit.so. Les systèmes Red Hat utilisent une première ligne différente, et le paquet vient d'EPEL. Consultez la documentation de votre distribution.

Étape 4 : configurer sshd

# /etc/ssh/sshd_config.d/10-2fa.conf
AuthenticationMethods publickey,keyboard-interactive
KbdInteractiveAuthentication yes
UsePAM yes

La virgule signifie que les deux sont requis, dans cet ordre. Utilisez un fichier de ce dossier, car la première valeur lue l'emporte, et le sshd_config principal de Debian contient KbdInteractiveAuthentication no plus bas. Les anciens systèmes appelaient ce réglage ChallengeResponseAuthentication.

Vérifiez le fichier avant de recharger :

sudo sshd -t
sudo sshd -T | grep -iE '^(authenticationmethods|kbdinteractiveauthentication|usepam) '
sudo systemctl reload ssh

Si KbdInteractiveAuthentication est désactivé, sshd -t vous le dit, et vous ne devez pas recharger :

Disabled method "keyboard-interactive" in AuthenticationMethods list "publickey,keyboard-interactive"
AuthenticationMethods cannot be satisfied by enabled authentication methods

Étape 5 : tester depuis une nouvelle fenêtre

ssh user@server
(user@server) Verification code:

Ce que nous avons obtenu dans chaque cas :

EssaiRésultat
Clé et code correctConnecté. Le journal indique Accepted keyboard-interactive/pam for user.
Clé et code faux, réutilisé ou vieux de cinq minutesPermission denied (keyboard-interactive).
Pas de cléPermission denied (publickey). Aucun code n'est demandé, et aucun mot de passe n'est accepté.
Clé et code de secoursConnecté une fois. Le code est ensuite retiré du fichier et échoue s'il est réutilisé.

Si des codes corrects échouent, vérifiez d'abord l'horloge. Le serveur et le téléphone doivent être d'accord sur l'heure. Lancez timedatectl sur le serveur et regardez si l'horloge est synchronisée.

Gardez une porte de retour

Ayez au moins deux de ces éléments avant de fermer votre dernière session ouverte :

  • Vos codes de secours, rangés ailleurs que sur le téléphone. Si le téléphone est perdu, entrez avec l'un d'eux, puis relancez google-authenticator pour remplacer le secret.
  • La console de l'hébergeur. Elle n'utilise pas SSH. Depuis elle, supprimez 10-2fa.conf et rechargez.
  • Une exception par adresse, pour un réseau que vous seul pouvez atteindre, comme un VPN :
# At the end of 10-2fa.conf
Match Address 10.8.0.0/24
    AuthenticationMethods publickey
  • Un compte de secours séparé, avec une clé gardée hors ligne. Ajoutez le même bloc avec Match User breakglass. Cela convient aussi à une tâche automatisée, si vous limitez sa clé.

Vérifiez une exception avec -C, car un simple sshd -T n'affiche pas les blocs Match :

sudo sshd -T -C user=user,host=server,addr=10.8.0.5 | grep -i authenticationmethods

Chaque exception est un contournement du second facteur : gardez-les peu nombreuses et étroites.

Pour limiter ce que peut exécuter une clé de secours ou de tâche automatique, voir restreindre une clé SSH à une seule commande.

Saisir moins de codes

Avec le partage de connexion, les nouvelles sessions réutilisent une connexion ouverte et ne redemandent pas de code.

Questions

Cela affecte-t-il scp, sftp, rsync et les scripts ?

Oui. Ils se connectent tous de la même façon et demanderont un code : une tâche sans personne pour le saisir échoue. Exemptez son compte avec un bloc Match, et limitez ce que sa clé peut faire avec les options du guide sur la restriction des clés.

Cela protège-t-il sudo ?

Non. Cela ne modifie que /etc/pam.d/sshd. sudo a son propre fichier PAM et n'est pas concerné.

Sources

Autres guides : Sécuriser un serveur

Tous les guides