Tunnels SSH expliqués : redirection de port locale, distante et dynamique

La redirection de port SSH existe en trois types. ssh -L ouvre un port sur votre machine qui atteint quelque chose côté serveur. ssh -R ouvre un port sur le serveur qui atteint quelque chose de votre côté. ssh -D ouvre un proxy SOCKS local qui envoie n'importe quelle connexion via le serveur. Ajoutez -N pour ne lancer aucune commande distante, et -o ExitOnForwardFailure=yes pour qu'un tunnel impossible à ouvrir échoue avec une erreur.

Mis à jour le . Toutes les redirections, adresses de liaison, valeurs de GatewayPorts et d'AllowTcpForwarding et les messages d'échec ont été exécutés avec OpenSSH 10.3 sur macOS comme client vers OpenSSH 10.0 sur Debian 13, avec de petits serveurs HTTP comme cibles. Plusieurs options -L sur une même connexion, -L à travers -J et l'erreur de configuration due aux deux-points dans LocalForward ont été relancés avec OpenSSH 10.0 sur Debian 13.

Lequel vous faut-il ?

OptionLe port s'ouvre surÀ utiliser pour
-L localVotre machineAtteindre un service que seul le serveur peut atteindre, comme une base de données.
-R distantLe serveurLaisser le serveur atteindre quelque chose sur votre machine, comme un site de développement.
-D dynamiqueVotre machineNaviguer ou vous connecter à de nombreux hôtes comme si vous étiez sur le serveur.

Pour s'en souvenir : -L écoute sur votre machine locale, -R écoute sur le serveur distant (remote), et -D est dynamique car l'application choisit la destination à chaque fois.

Redirection locale : -L

ssh -N -L 8080:localhost:80 user@server
your app -> your machine, port 8080 -> SSH -> server -> localhost:80 as seen by the server

Le format est port_local:destination:port_destination. Désormais, http://127.0.0.1:8080 sur votre machine affiche le serveur web qui n'écoute que sur l'adresse de bouclage du serveur.

Le point essentiel est que c'est le serveur qui résout la destination. localhost désigne le serveur lui-même, pas votre machine. La destination peut aussi être un autre hôte que le serveur peut atteindre, et ce dernier saut n'est pas chiffré par SSH :

ssh -N -L 8080:intranet.example.com:80 user@server

Redirection distante : -R

ssh -N -R 9000:localhost:3000 user@server
a program on the server -> server, port 9000 -> SSH -> your machine -> localhost:3000 as seen by your machine

Un programme sur le serveur peut maintenant ouvrir http://127.0.0.1:9000 et atteindre l'application qui tourne sur votre machine. Cela marche même quand votre machine est derrière un NAT, parce que c'est votre côté qui a démarré la connexion.

Redirection dynamique : -D

ssh -N -D 1080 user@server
your app -> SOCKS proxy on your machine, port 1080 -> SSH -> server -> any host:port the app asks for

Dirigez une application qui gère SOCKS vers 127.0.0.1:1080. Pour tester avec curl :

curl --socks5-hostname 127.0.0.1:1080 http://intranet.example.com/

--socks5-hostname fait faire la résolution DNS par le serveur : les noms internes fonctionnent et la requête ne fuit pas depuis votre propre réseau. Avec --socks5 seul, le nom est résolu sur votre machine.

Les réglages du navigateur et les fuites DNS sont traités dans utiliser SSH comme proxy SOCKS.

Plusieurs ports, ou via un hôte de rebond

Répétez l'option pour ouvrir plusieurs redirections sur une même connexion :

ssh -N -L 127.0.0.1:8080:localhost:80 -L 127.0.0.1:5433:localhost:5432 user@server

Quand le serveur se trouve derrière un bastion, ajoutez -J. La redirection part du dernier serveur de la chaîne, c'est donc lui qui résout la destination :

ssh -N -J user@bastion -L 127.0.0.1:5433:localhost:5432 user@db-server

Les chaînes de serveurs sont expliquées dans utiliser ProxyJump et un hôte de rebond.

Lancer un tunnel sans shell

  • -N ne lance aucune commande distante. La session ne porte que le tunnel.
  • -f passe en arrière-plan après la connexion, vous pouvez donc encore saisir une phrase secrète avant.
  • -o ExitOnForwardFailure=yes se termine avec une erreur si la redirection ne peut pas être établie.
ssh -f -N -o ExitOnForwardFailure=yes -L 127.0.0.1:8080:localhost:80 user@server

Sans ExitOnForwardFailure, ssh affiche un avertissement et reste connecté sans tunnel fonctionnel, ce qui prête à confusion dans un script. Avec cette option, un port local déjà utilisé se termine avec le code 255 et Could not request local forwarding, et un port distant déjà utilisé avec remote port forwarding failed for listen port.

Un piège vu dans nos tests : avec un simple -L 8080:..., ssh écoute à la fois sur 127.0.0.1 et sur ::1. Si un seul des deux est pris, par exemple par un autre programme en IPv4, ssh affiche bind [127.0.0.1]:8080: Address already in use et continue de tourner même avec ExitOnForwardFailure=yes. Votre app peut alors atteindre cet autre programme. Écrire l'adresse, comme dans -L 127.0.0.1:8080:..., en fait un vrai échec : utilisez cette forme dans les scripts.

Pour arrêter un tunnel en arrière-plan, trouvez son processus et tuez-le :

pgrep -f 'L 127.0.0.1:8080'
kill PID

Pour les tunnels que vous lancez souvent, un socket de contrôle permet de les arrêter par leur nom avec ssh -O exit. Voir enregistrer, lancer et arrêter des tunnels SSH.

Pour garder un tunnel dans votre configuration, utilisez LocalForward, RemoteForward et DynamicForward :

Host dbtunnel
  HostName 203.0.113.10
  User user
  LocalForward 127.0.0.1:8080 localhost:80
  ExitOnForwardFailure yes

Lier à localhost uniquement

Un port redirigé est ouvert à quiconque peut l'atteindre, et le service derrière lui peut faire confiance aux connexions venant du serveur. Par défaut, ssh ne se lie qu'à l'adresse de bouclage : gardez ce comportement sauf raison contraire.

Ces commandes ouvrent le port local à tout votre réseau. Nous avons confirmé que les deux écoutent sur toutes les interfaces :

ssh -N -L 0.0.0.0:8080:localhost:80 user@server
ssh -N -g -L 8080:localhost:80 user@server

Pour -R, c'est le serveur qui décide, avec GatewayPorts dans sa configuration sshd :

GatewayPorts-R 9000:...-R 0.0.0.0:9000:...
no (par défaut)Bouclage uniquementBouclage uniquement, sans erreur
clientspecifiedBouclage uniquementToutes les interfaces
yesToutes les interfacesToutes les interfaces, et 127.0.0.1:9000 s'ouvre aussi sur toutes les interfaces

Si votre port -R n'est pas joignable de l'extérieur, GatewayPorts en est donc la raison habituelle. Exposer le service de votre portable à internet de cette façon est rarement ce que vous voulez. Préférez clientspecified si vous en avez besoin, et ajoutez une règle de pare-feu.

Quand le serveur refuse

AllowTcpForwarding dans la configuration sshd du serveur contrôle cela. Les valeurs sont yes (par défaut), no, local (autorise -L et -D) et remote (autorise -R).

  • Un -L ou -D bloqué se connecte normalement. L'erreur n'arrive que quand quelque chose utilise le port : channel 2: open failed: administratively prohibited: open failed.
  • Un -R bloqué affiche remote port forwarding failed for listen port.

La page de manuel note que désactiver les redirections apporte peu si les utilisateurs ont aussi un shell, puisqu'ils peuvent lancer leur propre redirecteur. Pour vraiment limiter quelqu'un, retirez aussi le shell, comme dans un utilisateur réservé au tunnel pour une base de données.

Deux autres erreurs reviennent souvent. Quand le port local ou distant est pris, voir Address already in use et remote port forwarding failed. Quand les connexions à travers le tunnel sont refusées, voir administratively prohibited: open failed.

Questions

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

-L ouvre un port sur votre machine qui mène à ce que le serveur peut joindre. -R ouvre un port sur le serveur qui ramène vers quelque chose sur votre machine.

Comment rediriger plusieurs ports avec une seule commande ssh ?

Répétez l'option : ssh -N -L 127.0.0.1:8080:localhost:80 -L 127.0.0.1:5433:localhost:5432 user@server. Une seule connexion les transporte toutes. Dans ~/.ssh/config, ajoutez une ligne LocalForward par port.

Comment garder un tunnel SSH actif ?

Lancez-le avec -N et -o ExitOnForwardFailure=yes, et laissez autossh ou un service systemd le relancer après une coupure. Voir autossh et systemd.

Pourquoi mon tunnel se connecte sans que rien ne marche ?

Le serveur peut interdire la redirection avec AllowTcpForwarding. Un -L ou -D bloqué se connecte quand même et n'échoue qu'à l'usage du port, avec administratively prohibited: open failed.

La redirection de port SSH est-elle sûre ?

Le trafic est chiffré entre votre machine et le serveur SSH. Le tronçon du serveur à la destination ne l'est pas, et un port qui écoute sur 0.0.0.0 peut servir à n'importe qui sur votre réseau : gardez l'adresse d'écoute sur 127.0.0.1.

Sources

Autres guides : Tunnels et hôtes de rebond

Tous les guides