Comment restreindre une clé SSH à une commande ou à une adresse

Placez des options devant la clé dans authorized_keys : restrict,command="/usr/local/bin/backup",from="203.0.113.10" ssh-ed25519 AAAA.... Avec cette ligne, la clé exécute toujours cette seule commande, quoi que demande le client, uniquement depuis cette adresse, sans terminal ni redirection. Les limites ne valent que pour cette clé, pas pour les autres clés ni pour les mots de passe du compte.

Mis à jour le . Toutes les options, le script d'aiguillage, rrsync et SFTP ont été exécutés avec OpenSSH 10.0 sur Debian 13, dans un conteneur jetable.

Les options

OptionEffetCe que voit l'autre côté
restrictAucune redirection d'aucune sorte, pas de terminal, pas de ~/.ssh/rcPTY allocation request failed on channel 0
command="..."Exécute toujours cette commandeLa sortie de cette commande
from="..."Uniquement depuis ces adresses ou ces nomsPermission denied
permitopen="host:port"Limite ssh -L et -W à cette destinationstdio forwarding failed ou open failed
permitlisten="[host:]port"Limite ssh -R à ce portremote port forwarding failed

Commencez par restrict, puis ajoutez ce dont la clé a besoin. Cette option couvre aussi toute restriction qu'OpenSSH ajoutera plus tard, ce que ne fait pas une liste de no-pty,no-port-forwarding et autres. Les options tiennent sur une seule ligne, séparées par des virgules, sans espace entre elles.

Une seule commande

restrict,command="/usr/local/bin/backup" ssh-ed25519 AAAA... backup-job

Désormais, ssh user@server et ssh user@server rm -rf / lancent tous deux la sauvegarde et rien d'autre.

Quelques commandes autorisées, avec SSH_ORIGINAL_COMMAND

Ce que le client a demandé se trouve dans la variable SSH_ORIGINAL_COMMAND, et un petit script peut la vérifier :

#!/bin/sh
# /usr/local/bin/ssh-allow
case "$SSH_ORIGINAL_COMMAND" in
  "uptime"|"df -h")
    exec $SSH_ORIGINAL_COMMAND
    ;;
  *)
    echo "Not allowed: $SSH_ORIGINAL_COMMAND" >&2
    exit 1
    ;;
esac
restrict,command="/usr/local/bin/ssh-allow" ssh-ed25519 AAAA... monitoring

Dans notre essai, ssh user@server uptime a fonctionné, et ssh user@server "uptime; id" a répondu Not allowed: uptime; id. Comparez des chaînes entières, comme ci-dessus. Un motif comme uptime* laisse passer tout ce qui commence par ce mot, et ne transmettez jamais la variable à sh -c ou eval.

Choisissez aussi les commandes autorisées avec soin. Une commande capable d'écrire des fichiers ou de lancer un shell, comme un éditeur, annule la restriction.

Limiter par adresse

from="203.0.113.10,198.51.100.0/24" ssh-ed25519 AAAA... office

Les motifs et les plages CIDR fonctionnent tous deux, et ! exclut une adresse. Utilisez des adresses IP plutôt que des noms d'hôte, car un nom d'hôte dépend du DNS inverse. Quand l'adresse ne correspond pas, le client voit Permission denied et le journal du serveur donne l'explication :

Authentication tried for user with correct key but not from a permitted host (host=198.51.100.7, ip=198.51.100.7, required=203.0.113.10).

Une clé réservée à un seul tunnel

restrict,port-forwarding,permitopen="10.0.1.10:5432",command="/usr/sbin/nologin" ssh-ed25519 AAAA... db-tunnel

Cette clé peut atteindre la base de données à cette adresse et sur ce port. Une redirection vers un autre port échoue, et une commande ne reçoit que This account is currently not available. Pour l'utiliser, ajoutez -N afin que ssh ne demande aucune commande :

ssh -N -L 5432:10.0.1.10:5432 user@server

Un nom d'hôte dans permitopen doit correspondre exactement à ce qu'envoie le client, sans résolution. localhost et 127.0.0.1 comptent comme deux valeurs différentes.

Attention : cela autorise encore ssh -R

port-forwarding active les deux sens, et permitopen n'en limite qu'un. Dans notre essai, la clé ci-dessus pouvait encore ouvrir une redirection distante avec ssh -R. permitlisten="none" n'est pas une issue, car la ligne devient alors invalide et la clé cesse complètement de fonctionner. Faites plutôt l'une de ces deux choses :

  • Réglez permitlisten sur un seul port que rien n'utilise, par exemple permitlisten="127.0.0.1:9". Nous avons vérifié qu'une redirection sur tout autre port est refusée.
  • Limitez le compte dans la configuration du serveur, ce qui vaut pour toutes ses clés :
# /etc/ssh/sshd_config.d/10-tunnel.conf
Match User tunnel
    AllowTcpForwarding local

Pour le rôle de -L, -R et -D, voyez les tunnels SSH locaux, distants et dynamiques.

Clés pour le transfert de fichiers

restrict seul n'empêche pas le transfert de fichiers. Dans notre essai, sftp fonctionnait encore avec cette option, car SFTP n'a besoin ni de terminal ni de redirection. Pour autoriser SFTP et rien d'autre :

restrict,command="internal-sftp" ssh-ed25519 AAAA... files

Pour autoriser rsync dans un seul dossier, utilisez rrsync, fourni avec rsync. Sur Debian, il se trouve dans /usr/bin/rrsync, et d'autres systèmes peuvent différer. Avec -ro, seule la lecture est permise, et nous avons confirmé que les envois échouent alors :

restrict,command="/usr/bin/rrsync -ro /srv/backup" ssh-ed25519 AAAA... backup-read

Ce que ces options ne font pas

  • Elles ne lient que cette clé. Une autre clé, ou un mot de passe, sur le même compte n'a aucune de ces limites. Donnez à chaque tâche son propre compte quand c'est possible, et désactivez les mots de passe.
  • L'utilisateur peut modifier le fichier. Si le compte peut écrire dans son propre authorized_keys, une commande capable d'écrire des fichiers peut supprimer les limites. Pour une frontière solide, laissez le fichier appartenir à root. Voir authorized_keys expliqué.
  • L'ordre des lignes compte. Si la même clé figure aussi sur une ligne sans restriction plus haut, c'est cette ligne qui l'emporte.

Questions

command= dans authorized_keys bloque-t-il SFTP ?

Oui, si command= désigne autre chose que internal-sftp, car SFTP exécute alors cette commande à la place. restrict seul ne bloque pas SFTP.

Qu'est-ce que SSH_ORIGINAL_COMMAND ?

Une variable d'environnement qui contient la commande demandée par le client quand une commande forcée s'exécute. Un script peut la vérifier et n'autoriser que quelques commandes exactes.

Peut-on mettre un nom d'hôte dans from= ?

Oui, mais cela dépend du DNS inverse, que l'autre partie peut contrôler. Les adresses IP et les plages CIDR sont plus sûres.

Sources

Autres guides : Sécuriser un serveur

Tous les guides