Comment utiliser sudo via SSH sans divulguer votre mot de passe

Utilisez ssh -t user@server 'sudo command' et tapez le mot de passe à l'invite : SSH le chiffre comme tout le reste de la session. La fuite vient du fait de mettre le mot de passe dans une commande, car ssh server 'echo pass | sudo -S command' l'affiche dans la liste des processus des deux machines et peut le laisser dans l'historique du shell. Pour l'automatisation, utilisez une règle NOPASSWD pour une commande exacte.

Mis à jour le . Exécuté avec sudo 1.9.16 et OpenSSH 10.0 sur Debian 13 dans un conteneur jetable, depuis un client OpenSSH 10.3 sur macOS.

Le mot de passe sudo est-il envoyé en clair ?

Pas sur le réseau. Tout ce qui passe dans une session SSH est chiffré, y compris ce que vous tapez à une invite sudo. Le mot de passe est déchiffré sur le serveur, comme il le faut pour que sudo le vérifie : un serveur compromis peut donc le capturer. C'est une raison de ne pas réutiliser ce mot de passe ailleurs.

La méthode normale : ssh -t

sudo a besoin d'un terminal pour demander un mot de passe, et une commande distante n'en reçoit aucun par défaut :

$ ssh user@server 'sudo whoami'
sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper
sudo: a password is required

L'erreur suggère -S, et c'est là que les gens se trompent. La solution simple est -t, qui demande un terminal :

ssh -t user@server 'sudo whoami'

Vous obtenez l'invite normale, rien n'est affiché à l'écran, et rien ne figure dans une ligne de commande.

Ce qui fuit avec sudo -S

Voici le schéma à éviter :

ssh user@server 'echo mypassword | sudo -S systemctl restart myapp'

Le mot de passe fait maintenant partie d'une commande, et apparaît donc à trois endroits :

  • La liste des processus du serveur. Pendant l'exécution de la commande, un autre compte sans privilèges du serveur de test voyait ceci avec un simple ps : user bash -c echo mypassword | sudo -S -p "" sleep 3.
  • La liste des processus de votre machine. Tout le texte est un argument de ssh, ps l'y affiche donc aussi.
  • L'historique de votre shell local, si vous l'avez tapé, et tout script ou journal de CI qui le contient.

Si vous devez vraiment alimenter sudo depuis un script

Lisez le mot de passe sans écho et envoyez-le sur l'entrée standard, pour qu'il ne devienne jamais un argument :

read -rs SUDO_PW
printf '%s\n' "$SUDO_PW" | ssh user@server 'sudo -S -p "" whoami'
unset SUDO_PW

printf est une commande interne du shell : elle ne lance pas de processus que ps pourrait afficher. Dans le même test, l'autre compte ne voyait que sudo -S -p sleep 3. -p "" retire l'invite de la sortie. sudo utilise ici l'entrée standard pour le mot de passe, la commande distante ne doit donc pas avoir besoin de sa propre entrée.

Pour l'automatisation : NOPASSWD pour une commande exacte

Un script ne devrait détenir aucun mot de passe. Autorisez seulement ce dont il a besoin :

# /tmp/deploy.sudoers
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp, /usr/bin/journalctl -u myapp -n 100 --no-pager

Vérifiez le fichier avant de l'installer, car un fichier sudoers cassé peut vous verrouiller hors de sudo :

sudo visudo -cf /tmp/deploy.sudoers
sudo install -m 0440 -o root -g root /tmp/deploy.sudoers /etc/sudoers.d/deploy
sudo visudo -c

Utilisez-le ensuite, avec -n pour qu'une non-correspondance échoue tout de suite au lieu de rester bloquée à attendre un mot de passe :

$ ssh deploy@server 'sudo -n /usr/bin/systemctl restart myapp'
$ ssh deploy@server 'sudo -n /usr/bin/systemctl stop myapp'
sudo: a password is required

Listez ce qu'un compte peut exécuter avec sudo -l. Quatre pièges :

  • Une commande sans arguments dans la règle autorise n'importe quels arguments. Avec /usr/bin/id seul dans la règle, sudo -n /usr/bin/id et sudo -n /usr/bin/id -u se sont exécutés dans notre test. Écrivez les arguments que vous voulez.
  • Les noms de fichiers contenant un point sont ignorés. /etc/sudoers.d/deploy.conf n'a eu aucun effet dans notre test, alors que le même fichier nommé deploy fonctionnait.
  • Certaines commandes peuvent lancer un shell. Les éditeurs, les paginateurs, find -exec et tar peuvent exécuter d'autres programmes en root : en autoriser un peut donc revenir à tout autoriser. C'est pourquoi la règle ci-dessus a --no-pager : sans cela, journalctl peut ouvrir un paginateur.
  • Les jokers correspondent aussi aux espaces. Le manuel de sudoers avertit que les jokers dans les arguments correspondent à n'importe quel caractère, espaces compris, et peuvent donc correspondre à plus d'arguments que prévu.

Combien de temps sudo se souvient de vous

Par défaut, sudo ne redemande pas pendant 5 minutes, et l'enregistrement est séparé pour chaque terminal. Dans le test, une session ssh -t a exécuté un second sudo -n whoami sans invite. Pour y mettre fin plus tôt, lancez sudo -k. Pour demander à chaque fois pour un compte, ajoutez ceci avec visudo :

Defaults:deploy timestamp_timeout=0

Un shell root resté ouvert est là où arrivent la plupart des erreurs. Voir comment éviter les erreurs sur les serveurs de production.

sudo -A et askpass

Avec -A, sudo exécute le programme nommé dans SUDO_ASKPASS et lit le mot de passe dans sa sortie :

SUDO_ASKPASS=$HOME/bin/askpass sudo -A whoami

Cela garde le mot de passe hors de la liste des processus, mais l'assistant doit le tirer de quelque part. Sur un serveur sans session graphique, c'est en général un fichier, comme dans notre test : le secret se retrouve donc sur le disque. Une règle NOPASSWD étroite est normalement la meilleure réponse.

Ansible

Utilisez --ask-become-pass (-K) pour être invité à le saisir, ou gardez ansible_become_password dans un fichier chiffré avec Ansible Vault. Ne le passez pas avec -e en ligne de commande, car cela le met dans la liste des processus et dans votre historique comme n'importe quel autre argument.

Une règle NOPASSWD pour quelques commandes ne fonctionne pas avec Ansible. Sa documentation dit que l'élévation de privilèges ne peut pas être limitée à certaines commandes, car les modules s'exécutent depuis un fichier temporaire dont le nom change à chaque fois. Elle suggère d'utiliser un compte séparé sans cette contrainte.

Questions

Le mot de passe sudo passe-t-il en clair sur SSH ?

Non. Tout ce qui passe dans une session SSH est chiffré, y compris ce que vous tapez à l'invite de sudo. Il est déchiffré sur le serveur, qui doit le vérifier.

Pourquoi ssh dit qu'un terminal est requis pour lire le mot de passe ?

Une commande distante n'a pas de terminal par défaut, et sudo en a besoin pour demander le mot de passe. Ajoutez -t, comme dans ssh -t user@server 'sudo command'.

NOPASSWD est-il sûr pour les scripts ?

C'est plus sûr qu'un mot de passe stocké si la règle désigne une seule commande précise avec ses arguments. Évitez les éditeurs, les pagers et les programmes capables de lancer un shell.

Sources

Autres guides : Sécuriser un serveur

Tous les guides