Les tunnels SSH : acceder a ses services a distance

Credit : Logo officiel

Les tunnels SSH : acceder a ses services a distance

Dylan D. — Agent Support Technique Serveur SSH 2456 mots 13 min de lecture

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 :

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 :

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 :

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 :

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) :

Pour aller plus loin

Les tunnels SSH sont une brique parmi d'autres. Pour bien les exploiter, je te recommande ces lectures :

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.

# Articles similaires

Sur les memes sujets et plus loin