Credit : Logo officiel
Les tunnels SSH : acceder a ses services a distance
Les tunnels SSH : acceder a ses services a distance
La semaine derniere, un client m'appelle en panique : son MariaDB tourne sur un VPS IONOS Debian 12, il doit faire un dump rapide depuis son poste pour deboguer un probleme de production, mais le port 3306 n'est evidemment pas ouvert sur Internet (et heureusement). En cinq minutes, on a mis en place un tunnel SSH local et il a pu lancer son mysqldump comme si la base tournait sur sa machine. C'est exactement pour ce genre de cas que les tunnels SSH existent : exposer un service interne uniquement a ta session, le temps qu'il faut, sans toucher au pare-feu ni ouvrir le moindre port public.
Je vais te montrer comment je les utilise au quotidien : tunnels locaux, distants, proxy SOCKS dynamique, autossh pour les connexions persistantes, et l'integration systemd pour que ca redemarre tout seul. C'est le genre d'outil qui parait abstrait au debut et qui devient indispensable des que tu l'as compris.
Comprendre le principe d'un tunnel SSH
Un tunnel SSH, c'est une connexion chiffree entre ta machine et un serveur, dans laquelle on encapsule du trafic TCP arbitraire. SSH ouvre un canal supplementaire en plus du shell, et redirige les paquets entre un port local et une destination distante (ou inversement).
Trois modes existent :
- Local forwarding (
-L) : un port de ta machine est relie a un service distant via le serveur SSH. - Remote forwarding (
-R) : un port du serveur est relie a un service de ta machine locale. - Dynamic forwarding (
-D) : ton SSH devient un proxy SOCKS5 generaliste.
Le gros avantage : tout passe dans la connexion SSH, donc c'est chiffre, authentifie et soumis aux memes regles que ton acces shell. Si t'as bien securise SSH (on en parle plus bas), tes tunnels sont aussi solides que ton serveur.
Pourquoi c'est preferable a ouvrir un port
Tu pourrais ouvrir le port 3306 dans iptables avec une whitelist d'IP. Trois problemes :
- L'IP whitelist change quand tu deplaces ton bureau ou que ton operateur reattribue ton IP. Tu casses ton acces sans le savoir jusqu'au prochain besoin.
- MariaDB en TLS demande un setup non trivial (cert serveur, cert client, conf my.cnf). Sans TLS, tu envoies tes credentials en clair sur Internet.
- Tu cumules les services exposes : MySQL, Redis, phpMyAdmin, Memcached... chaque port est une nouvelle surface d'attaque a maintenir.
Avec un tunnel SSH, tu as un seul port expose (le 22 ou ton port custom), TLS implicite via le chiffrement OpenSSH 9.2, authentification par cle, et tous tes services restent en 127.0.0.1 cote serveur. Defense en profondeur sans effort.
Tunnel local (Local Port Forwarding)
Le cas le plus courant, et de loin. Tu veux acceder a un service distant comme s'il etait local.
Syntaxe generale
ssh -L [port_local]:[hote_destination]:[port_destination] utilisateur@serveur
hote_destination est resolu depuis le serveur SSH, pas depuis ta machine. C'est ce qui permet d'atteindre des services qui ecoutent sur 127.0.0.1 cote serveur.
Acceder a MariaDB ou MySQL distant
Ta base ecoute uniquement sur 127.0.0.1:3306 du serveur (configuration recommandee). Depuis ta machine :
ssh -L 3307:127.0.0.1:3306 admin@mon-serveur-ionos.fr -p 2222
Maintenant connecte-toi via localhost:3307 :
mysql -h 127.0.0.1 -P 3307 -u wp_user -p wordpress_db
Ou pour un dump complet :
mysqldump -h 127.0.0.1 -P 3307 -u wp_user -p wordpress_db > backup_$(date +%F).sql
Ta base de donnees est accessible en local sans ouvrir le port 3306 sur Internet. Et meme si tu te trompes dans tes regles iptables, le risque d'exposition est nul.
Acceder a phpMyAdmin non expose
Une pratique que je recommande : installer phpMyAdmin sur le serveur mais le faire ecouter uniquement en local via Nginx. Pour y acceder de temps en temps :
ssh -L 8080:127.0.0.1:80 admin@mon-serveur-ionos.fr -p 2222
Ouvre http://localhost:8080/phpmyadmin dans ton navigateur. Pas de panneau d'administration expose, pas de page de login publique a brute-forcer.
Acceder a Redis ou Memcached
ssh -L 6380:127.0.0.1:6379 admin@mon-serveur-ionos.fr -p 2222
redis-cli -p 6380
Tu peux ainsi inspecter le cache d'un WordPress qui utilise Redis sans avoir a installer redis-cli sur le serveur ni ouvrir le port.
Acceder a un Elasticsearch / Kibana interne
Meme principe pour les pile ELK :
ssh -L 9200:127.0.0.1:9200 -L 5601:127.0.0.1:5601 admin@elastic-host.fr
Puis curl http://localhost:9200/_cluster/health pour interroger le cluster, ou ton navigateur sur http://localhost:5601 pour Kibana. Les ports Elastic ne sont jamais exposes a Internet, c'est la bonne pratique du projet officiel.
Tunnel distant (Remote Port Forwarding)
L'inverse. Ca rend un service de ta machine accessible depuis le serveur. Plus rare mais super utile dans certains cas.
Syntaxe
ssh -R [port_distant]:[hote_local]:[port_local] utilisateur@serveur
Cas d'usage : montrer une app en dev
Tu developpes en local sur le port 3000 (Next.js, Node, peu importe) et tu veux qu'un collegue puisse y acceder via le serveur :
ssh -R 8080:127.0.0.1:3000 admin@mon-serveur-ionos.fr -p 2222
L'app est dispo sur localhost:8080 du serveur. Pour la rendre accessible de l'exterieur, ajoute dans /etc/ssh/sshd_config :
GatewayPorts yes
Puis systemctl reload ssh. Maintenant http://mon-serveur-ionos.fr:8080 pointe sur ton app locale. Attention quand meme : tu exposes ta machine personnelle sur Internet via ce tunnel. Eteins-le des que la demo est finie.
Cas d'usage : webhook depuis un service externe
Tu integres un webhook Stripe ou GitHub et tu dois le tester en local sans ngrok ? Un tunnel inverse fait le job, a condition que ton serveur dispose d'un domaine et d'un certificat SSL. Tu mets un Nginx en reverse proxy devant le port 8080 et tu pointes le webhook sur l'URL HTTPS du serveur.
Cas d'usage : serveur derriere un NAT / CG-NAT
Un de mes clients a un NAS Synology a la maison derriere une box opt-out IPv4. Pas d'IP publique, donc pas de port forwarding possible. Solution : depuis le NAS, on fait un ssh -R 2200:localhost:22 vers un VPS IONOS qui sert de relais. Depuis n'importe ou, je me connecte au VPS sur le port 2200 et j'arrive sur le NAS en SSH. Le tunnel inverse devient un "trou de souris" pour traverser un NAT impossible a configurer. Avec autossh et systemd cote NAS, c'est permanent et resilient.
Tunnel dynamique (SOCKS Proxy)
Le tunnel dynamique cree un proxy SOCKS5 qui route tout le trafic a travers le serveur :
ssh -D 1080 admin@mon-serveur-ionos.fr -p 2222
Configure ton navigateur (ou systeme) avec le proxy SOCKS5 sur localhost:1080. Tout ton trafic passe par le serveur. Cas d'usage typiques :
- Tester un site depuis l'IP du serveur (utile pour valider une whitelist IP cote provider).
- Naviguer en securite sur un WiFi public sans VPN.
- Acceder a des services geo-restreints quand le serveur est dans le bon pays.
- Debugger un probleme de DNS qui ne se manifeste que depuis certains reseaux.
Avec curl, c'est une bonne facon de tester :
curl --socks5-hostname localhost:1080 https://monsite.fr
curl --socks5-hostname localhost:1080 https://ifconfig.me
La deuxieme commande te renvoie l'IP publique du serveur, preuve que le tunnel fonctionne. Le flag --socks5-hostname (vs --socks5) est important : il delegue la resolution DNS au serveur, donc tu beneficies de son resolveur, ce qui evite les fuites DNS.
Branchement avec Firefox
Firefox supporte SOCKS5 nativement. about:preferences > Network Settings > Manual proxy > SOCKS5 host 127.0.0.1 port 1080, et coche Proxy DNS when using SOCKS v5. Plus pratique avec une extension comme FoxyProxy qui te laisse switcher entre direct et tunnel selon le pattern d'URL : tres utile quand seuls quelques sites doivent passer par le tunnel.
Options utiles a connaitre
Les flags qui changent vraiment la vie quand tu fais des tunnels au quotidien :
ssh -L 3307:127.0.0.1:3306 \
-N \
-f \
-o ServerAliveInterval=60 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
admin@mon-serveur-ionos.fr -p 2222
Detail :
-N: pas de shell, juste le tunnel. Plus propre, plus securise.-f: passe en arriere-plan apres l'authentification.ServerAliveInterval=60: envoie un keepalive toutes les 60 secondes pour eviter que la connexion tombe (NAT, firewall, proxy d'entreprise).ServerAliveCountMax=3: nombre de keepalives sans reponse avant de considerer la connexion morte.ExitOnForwardFailure=yes: si le port local est deja pris ou que le forward echoue, ssh quitte au lieu de te laisser une fausse impression que tout va bien.
Tunnels persistants avec autossh
OpenSSH ne sait pas se reconnecter tout seul. Pour des tunnels qui doivent rester ouverts en permanence, autossh detecte les coupures et reconnecte automatiquement :
apt install autossh -y
autossh -M 0 -f -N \
-L 3307:127.0.0.1:3306 \
-o ServerAliveInterval=60 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
admin@mon-serveur-ionos.fr -p 2222
-M 0 desactive le port de monitoring d'autossh (le legacy) et delegue la detection a ServerAliveInterval, ce qui est la methode recommandee aujourd'hui.
Service systemd pour autossh
Pour un tunnel au boot, je cree un utilisateur dedie tunnel (sans mot de passe, avec une cle SSH propre) puis le fichier /etc/systemd/system/tunnel-mysql.service :
[Unit]
Description=Tunnel SSH vers MariaDB
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=tunnel
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 0 -N -L 3307:127.0.0.1:3306 -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes -i /home/tunnel/.ssh/id_ed25519 admin@mon-serveur-ionos.fr -p 2222
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Activation :
systemctl daemon-reload
systemctl enable --now tunnel-mysql
systemctl status tunnel-mysql
AUTOSSH_GATETIME=0 evite qu'autossh abandonne si la premiere tentative echoue (utile au boot si le reseau n'est pas tout a fait pret).
Restreindre la cle SSH cote serveur
Cote serveur, dans ~/.ssh/authorized_keys du compte tunnel, ajoute des restrictions strictes a la cle :
restrict,permitopen="127.0.0.1:3306",port-forwarding,no-pty,no-X11-forwarding ssh-ed25519 AAAAC3...
La directive permitopen limite cette cle a forwarder uniquement vers 127.0.0.1:3306. Meme si la cle privee fuite, l'attaquant ne peut pas l'utiliser pour pivoter sur le reseau interne ou ouvrir un shell : il aura juste un acces a MariaDB, qu'il devra encore authentifier. C'est la base de la defense en profondeur applique au tunnel persistant.
Config SSH cote client : le confort ultime
Simplifie-toi la vie avec ~/.ssh/config :
Host ionos
HostName mon-serveur-ionos.fr
User admin
Port 2222
IdentityFile ~/.ssh/id_ed25519
LocalForward 3307 127.0.0.1:3306
LocalForward 6380 127.0.0.1:6379
LocalForward 8080 127.0.0.1:80
DynamicForward 1080
ServerAliveInterval 60
ServerAliveCountMax 3
ExitOnForwardFailure yes
Apres ca un simple ssh ionos ouvre la connexion avec tous les tunnels d'un coup. Si tu veux juste les tunnels sans shell, ssh -N ionos. C'est le genre de config qui te fait gagner un temps fou au quotidien.
Aliases pratiques pour les tunnels ponctuels
Dans ~/.bashrc ou ~/.zshrc, je definis des aliases pour les usages repetitifs :
alias tun-db='ssh -N -L 3307:127.0.0.1:3306 ionos'
alias tun-redis='ssh -N -L 6380:127.0.0.1:6379 ionos'
alias tun-pma='ssh -N -L 8080:127.0.0.1:80 ionos'
alias tun-socks='ssh -N -D 1080 ionos'
Je peux ainsi lancer tun-db & pour ouvrir la base en arriere-plan, faire mes operations, puis kill %1 pour fermer. C'est le genre de raccourci qui semble bete mais qui transforme l'experience quotidienne quand tu fais ces operations dix fois par jour.
Patterns avances avec ProxyJump
Dans une infra ou tu as un bastion en frontal et plusieurs serveurs internes derriere :
Host bastion
HostName bastion.monsite.fr
User dylan
Port 22
Host prod-*
User admin
ProxyJump bastion
LocalForward 3307 127.0.0.1:3306
ssh prod-db1 fait automatiquement le saut via le bastion et ouvre le tunnel MySQL. Pratique pour les infras avec acces controle, et bien plus propre que les vieux ssh -L ... -t bastion ssh ... enchaines.
Erreurs courantes et leur fix
Voici les pieges sur lesquels je suis tombe le plus souvent (et que mes clients me remontent regulierement) :
bind: Address already in use: le port local est deja occupe par un autre process.ss -tlnp | grep 3307pour identifier le coupable, puis tue-le ou choisis un autre port.- Le tunnel s'ouvre mais la connexion sur localhost ne passe pas : verifie que le service distant ecoute bien sur l'adresse que tu cibles. Si MariaDB est sur
bind-address = 127.0.0.1cote serveur, ton-L 3307:127.0.0.1:3306est correct. Si c'est un socket Unix uniquement, le tunnel TCP ne marchera jamais. channel 2: open failed: administratively prohibited:AllowTcpForwardingest surnodanssshd_config. Passe-le ayes(oulocal/remoteselon ton besoin) puissystemctl reload ssh.- Le tunnel tombe au bout de quelques minutes : NAT du provider ou firewall qui coupe les connexions inactives. Ajoute
ServerAliveInterval=60cote client ouClientAliveInterval=60cote serveur. GatewayPortsignore : tu as configureGatewayPorts yesmais ton remote forward reste sur127.0.0.1. Force avecssh -R 0.0.0.0:8080:127.0.0.1:3000 .... Et reflechis bien avant : tu ouvres ta machine au monde entier.Permission denied (publickey)apres reboot : la cle SSH d'un service systemd n'est pas accessible parce que le HOME du usertunneln'a pas les bonnes permissions.chmod 700 /home/tunnel/.ssh && chmod 600 /home/tunnel/.ssh/id_ed25519.- autossh tourne mais ne reconnecte pas : tu n'as pas mis
AUTOSSH_GATETIME=0et la premiere tentative ayant echoue, autossh est passe en mode "abandon". Ajoute la variable dans le service systemd.
Pour aller plus loin
Les tunnels SSH sont une brique parmi d'autres. Pour bien les exploiter, je te recommande ces lectures :
- Securiser SSH avec sshd_config — la base avant d'exposer quoi que ce soit en SSH.
- Simplifier ses connexions avec ssh config — pour aller plus loin que les patterns vus ici.
- Bases de systemd pour gerer les services Linux — comprendre les units utilisees pour autossh.
- Proteger son serveur du brute force avec Fail2ban — protection cote serveur du port SSH expose.
- Comprendre et configurer iptables — la couche reseau qui complete le tunnel.
- Hardening Linux : securiser son serveur — tour complet de securisation systeme.
SSH, ton couteau suisse reseau
Une fois que tu as integre les tunnels SSH dans ta routine, tu te demandes comment tu as fait sans. Plus besoin d'ouvrir des ports, plus besoin d'installer un VPN pour acceder a un service ponctuel, plus besoin de copier des dumps a la main : un ssh -L et c'est regle. Combine ca avec une bonne config dans ~/.ssh/config et autossh pour la persistance, et tu as une infrastructure d'acces a distance robuste, chiffree, gratuite et parfaitement integree a Linux.
EDIT 2026 : si tu utilises VS Code Remote SSH, les tunnels se configurent directement dans le fichier config SSH et VS Code les gere automatiquement, y compris le forward du port 22 du serveur de developpement vers ta machine. Et pour les setups multi-tenants ou tu veux exposer plusieurs services via SSH a des partenaires sans leur donner de shell, regarde du cote de sslh couple a un user nologin avec permitopen strict : c'est une combinaison rare mais redoutable.