Tutoriel Linux

Tunnel SSH : transférer un port sans exposer votre serveur

Débutant6 min de lecture
À retenirLinux n'est pas réservé aux experts. Le bon point de départ : une distribution accessible, une sauvegarde propre et quelques commandes comprises.

Vous devez accéder à une interface web, une base PostgreSQL ou un service d’administration qui n’écoute que sur 127.0.0.1. Ouvrir le port dans le pare-feu du serveur serait rapide, mais ce service deviendrait alors joignable depuis un réseau où il n’a rien à faire.

Un tunnel SSH transporte cette connexion dans votre session SSH. Pour un accès ponctuel, je préfère cette méthode : le service reste fermé publiquement et seul le poste qui ouvre le tunnel peut l’utiliser. Il faut toutefois choisir la bonne redirection et vérifier l’adresse d’écoute, surtout avec -R.

Illustration de Tux au centre d’un tunnel SSH sécurisé entre un poste local et un serveur Linux
Le tunnel SSH transporte un port dans une connexion chiffrée sans publier directement le service sur le réseau.

Transférer un port vers votre poste avec -L

La redirection locale -L ouvre un port sur votre poste, puis envoie les connexions vers une destination accessible depuis le serveur SSH. Sa forme générale est la suivante :

ssh -L adresse_locale:port_local:hote_cible:port_cible utilisateur@serveur_ssh

Pour atteindre une interface HTTP qui écoute uniquement sur le port 80 du serveur distant :

ssh -N -T -o ExitOnForwardFailure=yes \
  -L 127.0.0.1:8080:127.0.0.1:80 \
  [email protected]
  • -L crée la redirection locale ;
  • 127.0.0.1:8080 est le port ouvert sur votre poste ;
  • 127.0.0.1:80 est la destination vue depuis le serveur SSH ;
  • -N évite de lancer une commande distante ;
  • -T évite d’allouer un terminal ;
  • ExitOnForwardFailure=yes ferme la session si SSH ne peut pas créer la redirection demandée.

Cette dernière option valide la création du port d’écoute. Elle ne prouve pas que le service final répond. Testez toujours la destination après l’ouverture du tunnel.

Tant que la commande reste ouverte, vous pouvez tester le service depuis votre poste :

curl http://127.0.0.1:8080/

L’adresse située à droite de -L mérite votre attention. Dans l’exemple précédent, 127.0.0.1 désigne le serveur SSH, pas votre poste. Si la cible se trouve sur une autre machine du réseau privé, indiquez son adresse privée :

ssh -N -T -o ExitOnForwardFailure=yes \
  -L 127.0.0.1:15432:10.20.0.15:5432 \
  [email protected]

Votre client PostgreSQL se connecte alors à 127.0.0.1:15432. Le bastion ouvre la connexion réelle vers 10.20.0.15:5432. La base n’a pas besoin d’écouter sur Internet.

Vérifier que le tunnel écoute vraiment

Sur votre poste, contrôlez le port local avec ss :

ss -ltnp | grep ':8080'
ss -ltnp | grep ':15432'

Vous devez voir une écoute sur 127.0.0.1, associée au processus ssh. Si vous voyez 0.0.0.0 ou [::], le port peut être accessible depuis d’autres interfaces du poste. Fermez le tunnel et recréez-le en précisant explicitement 127.0.0.1.

Le guide sur les ports ouverts sous Linux avec ss détaille la lecture des adresses d’écoute. Vérifiez aussi que le serveur SSH peut joindre la destination finale :

curl -I http://127.0.0.1:80/
nc -vz 10.20.0.15 5432

Si le tunnel est créé mais que curl ou votre client SQL reçoit Connection refused, SSH fonctionne probablement. C’est la destination finale qui n’écoute pas sur l’adresse ou le port indiqué.

Utiliser -R sans publier un service par erreur

La redirection distante -R fait le trajet inverse. Elle ouvre un port sur le serveur SSH et le renvoie vers un service accessible depuis votre poste. Exemple : rendre une application locale sur le port 3000 accessible uniquement depuis le serveur distant :

ssh -N -T -o ExitOnForwardFailure=yes \
  -R 127.0.0.1:9000:127.0.0.1:3000 \
  [email protected]

Sur le serveur distant, un processus local peut maintenant appeler :

curl http://127.0.0.1:9000/

Par défaut, sshd lie ce port distant à l’interface de boucle locale. Ne remplacez pas 127.0.0.1 par 0.0.0.0 ou * sans avoir décidé qui doit pouvoir joindre le service. Le paramètre serveur GatewayPorts peut autoriser une écoute sur les autres interfaces. Une simple démonstration locale peut alors devenir un service exposé au réseau.

Évitez aussi l’option -g avec une redirection locale si vous ne souhaitez pas que d’autres machines se connectent au port transféré. Pour garder une surface minimale, liez vos tunnels à 127.0.0.1 des deux côtés.

Contrôler les restrictions du serveur SSH

Si SSH répond administratively prohibited ou refuse la redirection, consultez la configuration effective du serveur :

sudo /usr/sbin/sshd -T | grep -E '^(allowtcpforwarding|gatewayports|permitopen|permitlisten)'

AllowTcpForwarding peut autoriser les redirections locales, distantes, les deux ou aucune. PermitOpen et PermitListen servent à limiter les destinations et ports autorisés. Sur un serveur partagé, ces restrictions sont préférables à une autorisation globale donnée à tous les comptes.

Avant toute modification de /etc/ssh/sshd_config ou d’un fichier sous /etc/ssh/sshd_config.d/, gardez votre session actuelle ouverte et testez la syntaxe :

sudo /usr/sbin/sshd -t

Ne redémarrez SSH qu’après un retour sans erreur. La procédure complète est expliquée dans l’article sur le redémarrage du service SSH sur Ubuntu. Si vous utilisez encore un mot de passe pour ce compte, commencez plutôt par mettre en place une clé SSH.

Enregistrer un tunnel récurrent dans ~/.ssh/config

Pour une base utilisée régulièrement, évitez de recopier une commande longue. Ajoutez un hôte dédié dans ~/.ssh/config :

Host tunnel-db
    HostName bastion.example.net
    User admin
    LocalForward 127.0.0.1:15432 10.20.0.15:5432
    ExitOnForwardFailure yes
    ServerAliveInterval 30
    ServerAliveCountMax 3

Protégez le fichier, puis ouvrez le tunnel au premier plan :

chmod 600 ~/.ssh/config
ssh -N -T tunnel-db

Je conseille de garder la première exécution au premier plan. Vous voyez immédiatement une erreur de clé, de port local ou de redirection, et Ctrl + C ferme proprement le tunnel. Si vous le lancez ensuite en arrière-plan avec -f, identifiez le bon processus avant de l’arrêter :

pgrep -af 'ssh.*15432'
kill PID

Évitez kill -9 par réflexe. Un signal normal suffit généralement pour fermer la session et libérer le port.

Diagnostiquer un tunnel qui ne passe pas

  • Address already in use : un autre processus occupe le port local. Vérifiez-le avec ss -ltnp ou choisissez un autre port.
  • Connection refused : le service cible n’écoute pas, ou l’adresse de destination est fausse depuis le point de vue du serveur SSH.
  • Administratively prohibited : la configuration SSH bloque la redirection ou limite la destination demandée.
  • Timeout : le serveur SSH n’arrive pas à joindre la cible à cause du routage ou d’un pare-feu intermédiaire.
  • Tunnel qui tombe : vérifiez la stabilité de la connexion et utilisez ServerAliveInterval plutôt qu’une boucle qui relance SSH sans diagnostic.

Pour obtenir le détail de la négociation et des redirections, ajoutez temporairement -v :

ssh -v -N -T -L 127.0.0.1:8080:127.0.0.1:80 [email protected]

Un tunnel SSH protège le transport, mais il ne corrige pas les droits d’accès du service cible. Gardez l’écoute sur la boucle locale, utilisez un compte SSH limité, contrôlez les clés autorisées et fermez la session dès que l’accès n’est plus nécessaire.

Les syntaxes de -L, -R, -N et -g sont détaillées dans la documentation officielle du client OpenSSH. Les contrôles AllowTcpForwarding, GatewayPorts, PermitOpen et PermitListen sont décrits dans sshd_config(5).

sudo apt update && sudo apt upgrade