Garder un tunnel SSH actif avec autossh et systemd

Lancez le tunnel comme service systemd avec Restart=always, et utilisez autossh -M 0 -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes comme commande. Les options de keepalive font remarquer à ssh qu'une connexion est morte, autossh le relance, et systemd relance l'ensemble après un plantage ou au démarrage. Utilisez une clé dédiée sur un compte qui ne peut ouvrir que ce tunnel.

Mis à jour le . autossh 1.4g, systemd 257 et OpenSSH 10.0 sous Debian 13, dans des conteneurs jetables privilégiés.

1. Créer une clé pour le tunnel

Sur la machine qui démarre le tunnel, créez un compte local et une clé sans phrase secrète, car le service démarre sans personne. Enregistrez ensuite la clé d'hôte du serveur, pour que la première connexion ne s'arrête pas pour poser la question :

sudo apt install autossh
sudo useradd -m -s /usr/sbin/nologin tunnel
sudo -u tunnel mkdir -m 700 /home/tunnel/.ssh
sudo -u tunnel ssh-keygen -t ed25519 -N "" -C tunnel -f /home/tunnel/.ssh/id_ed25519
ssh-keyscan -t ed25519 server | sudo -u tunnel tee /home/tunnel/.ssh/known_hosts

Comparez cette clé d'hôte avec l'empreinte réelle du serveur avant de vous y fier. Voir known_hosts expliqué.

2. Restreindre le compte sur le serveur

Une clé sans phrase secrète sur une machine distante ne doit ouvrir que ce tunnel. Sur le serveur, créez un compte nommé tunnel et mettez la clé publique dans son ~/.ssh/authorized_keys :

restrict,port-forwarding ssh-ed25519 AAAA... tunnel

Ajoutez ensuite ceci à la fin de /etc/ssh/sshd_config :

Match User tunnel
  AllowTcpForwarding remote
  PermitListen localhost:2222
  X11Forwarding no
  ForceCommand /usr/sbin/nologin
sudo sshd -t && sudo systemctl reload ssh

Nous avons testé le résultat avec cette clé :

EssaiRésultat
-R 2222:localhost:22Fonctionne
-R 2223:localhost:22remote port forwarding failed for listen port 2223
Lancer une commande ou un shellThis account is currently not available.
-L 9000:localhost:22Connexion fermée, rien ne passe

La dernière ligne demande le bloc Match. Avec seulement permitlisten="localhost:2222" dans authorized_keys, une redirection -L fonctionnait encore lors de notre test, ce qui permettait à la clé d'atteindre les services sur la boucle locale du serveur. Plus de détails sur les options de clé dans comment restreindre une clé SSH.

3. Créer le service

Enregistrez ceci dans /etc/systemd/system/autossh-tunnel.service :

[Unit]
Description=Reverse SSH tunnel to server
Wants=network-online.target
After=network-online.target

[Service]
User=tunnel
Environment=AUTOSSH_GATETIME=0
ExecStart=/usr/bin/autossh -M 0 -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes -i /home/tunnel/.ssh/id_ed25519 -R 2222:localhost:22 tunnel@server
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now autossh-tunnel.service
systemctl status autossh-tunnel

Le rôle de chaque élément

  • -M 0 désactive le port de surveillance propre à autossh : il ne relance donc ssh que lorsque ssh se termine. Le README d'autossh suggère les options de keepalive pour remplacer le port de surveillance.
  • ServerAliveInterval=30 et ServerAliveCountMax=3 font quitter ssh après environ 90 à 120 secondes de silence du serveur. Dans notre test, un intervalle de 5 secondes avec le même nombre a pris 20 secondes, et un gel de 100 secondes avec ces valeurs ne l'a pas déclenché.
  • ExitOnForwardFailure=yes fait quitter ssh si le port ne peut pas être ouvert. Sans lui, vous pouvez avoir une connexion active sans tunnel.
  • AUTOSSH_GATETIME=0 fait qu'autossh continue d'essayer même si la première tentative échoue, ce qui compte au démarrage.
  • Restart=always relance le service si autossh lui-même meurt.
  • N'utilisez pas l'option -f d'autossh ici, car systemd attend que le processus reste au premier plan.

4. Le voir se rétablir

Ce que nous avons faitCe qui s'est passé
Tué la session sur le serveurautossh a lancé aussitôt un nouveau ssh
Gelé le serveur pendant 28 secondes, avec un intervalle de 5 secondesTimeout, server server not responding. après 20 secondes, puis une nouvelle connexion dès le retour du serveur
Tué autossh avec kill -9, ainsi que son sshsystemd a relancé le service, et NRestarts est passé à 1

Observez-le vous-même avec journalctl -u autossh-tunnel -f. Sur le serveur, ss -ltn | grep 2222 montre le port.

Si le port reste pris après une panne réseau

Après une vraie coupure, le serveur peut garder l'ancienne connexion et son port : la nouvelle échoue alors avec remote port forwarding failed jusqu'à l'expiration de l'ancienne. Dans le bloc Match du serveur, ajoutez :

  ClientAliveInterval 30
  ClientAliveCountMax 3

Le serveur abandonne alors les clients morts après environ 90 secondes, et les nouvelles tentatives réussissent.

Pour les autres raisons d'un port occupé, voir tunnel SSH : Address already in use et remote port forwarding failed.

Avez-vous vraiment besoin d'autossh

Avec systemd et les options de keepalive, ssh seul fonctionne aussi. ssh se termine, et Restart=always le relance. Remplacez la commande dans l'unité par :

ExecStart=/usr/bin/ssh -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes -i /home/tunnel/.ssh/id_ed25519 -R 2222:localhost:22 tunnel@server

Dans notre test, cela s'est rétabli après une session tuée avec un seul redémarrage systemd, après les 10 secondes de RestartSec. autossh redémarre plus vite, et c'est l'option à prendre si vous n'avez pas systemd.

Une différence observée : systemctl restart a journalisé status=1/FAILURE pour autossh à son arrêt. Le service a quand même redémarré normalement.

Questions

Que fait autossh -M 0 ?

Il désactive le port de surveillance. autossh relance alors ssh seulement quand ssh se termine, et les options ServerAliveInterval et ServerAliveCountMax font terminer ssh quand la connexion meurt.

Faut-il autossh avec systemd ?

Pas toujours. Un simple ssh avec les options keepalive et Restart=always revient aussi. autossh redémarre plus vite et fonctionne sans systemd.

Pourquoi mon tunnel inverse échoue-t-il après une coupure réseau ?

Le serveur peut encore garder l'ancienne connexion et son port. Ajoutez ClientAliveInterval 30 et ClientAliveCountMax 3 côté serveur pour qu'il coupe les clients morts après environ 90 secondes.

Sources

Autres guides : Tunnels et hôtes de rebond

Tous les guides