Comment créer un tunnel SSH inverse derrière un NAT

Sur la machine derrière le NAT, lancez ssh -N -R 2222:localhost:22 user@server, où server a une adresse publique. Puis, sur ce serveur, lancez ssh -p 2222 user@localhost pour atteindre la machine cachée. Par défaut, le port n'écoute que sur la boucle locale du serveur : il n'est donc pas exposé à Internet, sauf si vous réglez GatewayPorts.

Mis à jour le . Client et serveur OpenSSH 10.0 sur Debian 13, dans des conteneurs jetables sur un même réseau, l'un jouant le rôle de la machine derrière le NAT.

Comment ça marche

Une machine derrière un NAT ne peut pas accepter de connexions entrantes, mais elle peut en établir de sortantes. Elle se connecte donc à un serveur à adresse publique et lui demande d'écouter sur un port. Les connexions vers ce port reviennent par la connexion SSH déjà ouverte.

Pour les deux autres types de redirection, voyez les tunnels SSH locaux, distants et dynamiques.

Se connecter en SSH à la machine cachée

La machine cachée doit avoir son propre serveur SSH en marche. Sur la machine cachée :

ssh -N -o ExitOnForwardFailure=yes -R 2222:localhost:22 user@server

-N n'exécute aucune commande distante. -R 2222:localhost:22 signifie que le port 2222 du serveur mène au port 22 de cette machine. Ajoutez -f pour passer en arrière-plan.

Sur le serveur, vous pouvez voir le port et l'utiliser :

$ ss -ltn | grep 2222
LISTEN 0  128  127.0.0.1:2222  0.0.0.0:*
LISTEN 0  128      [::1]:2222     [::]:*
$ ssh -p 2222 hiddenuser@localhost

Depuis votre portable, passez par le serveur en une seule étape :

ssh -J user@server -p 2222 hiddenuser@localhost

Pour ne pas mélanger les clés d'hôte, puisque localhost peut désigner de nombreuses machines, mettez ceci dans ~/.ssh/config :

Host home
  HostName localhost
  Port 2222
  User hiddenuser
  ProxyJump user@server
  HostKeyAlias home

Montrer une application web locale

ssh -N -R 8080:localhost:3000 user@server

Désormais, curl http://localhost:8080/ sur le serveur atteint le port 3000 de votre machine. Dans notre essai, depuis une autre machine, curl http://server:8080/ était refusé tant que nous n'avions pas modifié GatewayPorts.

Avec le port 0, le serveur choisit un port libre et ssh l'affiche :

$ ssh -N -R 0:localhost:3000 user@server
Allocated port 33341 for remote forward to localhost:3000

GatewayPorts : qui peut atteindre le port

Ce réglage se trouve dans le sshd_config du serveur et vaut no par défaut. Ce que nous avons mesuré :

GatewayPorts-R 8080:...-R 0.0.0.0:8080:...
noBoucle locale uniquementBoucle locale uniquement, l'adresse est ignorée
clientspecifiedBoucle locale uniquementToutes les adresses, joignable depuis d'autres machines
yesToutes les adressesToutes les adresses

Avec yes, même -R localhost:8080:... écoutait sur toutes les adresses dans notre essai. Si vous avez besoin d'un accès depuis l'extérieur, clientspecified laisse le choix au client, et le pare-feu doit tout de même autoriser le port.

Garder le tunnel privé

  • Laissez GatewayPorts no et utilisez -J comme ci-dessus. Seule une personne pouvant se connecter au serveur peut alors atteindre le port.
  • N'oubliez pas les autres utilisateurs du serveur. La boucle locale est partagée par tous ceux qui y sont connectés. Ils peuvent atteindre le port, et le service derrière doit tout de même exiger sa propre authentification.
  • Utilisez une clé et un compte dédiés au tunnel, puisque la machine derrière le NAT conserve une clé qui permet de se connecter à votre serveur. Le guide autossh présente une configuration restreinte testée.

Quand le port est déjà pris

Avec ExitOnForwardFailure=yes, ssh s'arrête et quitte avec le code 255 :

Error: remote port forwarding failed for listen port 2222

Sans cette option, vous n'obtenez qu'un Warning et ssh reste connecté sans tunnel, ce qui passe facilement inaperçu. Une cause fréquente est qu'une ancienne connexion occupe encore le port sur le serveur. AllowTcpForwarding no ou une règle PermitListen sur le serveur provoque le même genre d'échec.

Le laisser tourner

Un simple ssh -R s'arrête quand la connexion tombe, et rien ne le relance. Pour un tunnel permanent, suivez autossh et systemd.

Questions

Quelle différence entre ssh -L et ssh -R ?

-L ouvre un port sur votre machine qui mène à quelque chose que le serveur peut joindre. -R fait l'inverse : il ouvre un port sur le serveur qui ramène à votre machine.

Pourquoi les autres machines n'atteignent-elles pas le port du tunnel inverse ?

Avec la valeur par défaut GatewayPorts no, le serveur n'écoute que sur sa boucle locale. Mettez GatewayPorts clientspecified ou yes dans sshd_config et ouvrez le port dans le pare-feu.

Comment garder un tunnel SSH inverse actif ?

Un simple ssh -R s'arrête quand la connexion tombe. Lancez-le avec autossh et systemd pour qu'il redémarre tout seul.

Sources

Autres guides : Tunnels et hôtes de rebond

Tous les guides