357 votes

Comment garder de manière fiable un tunnel SSH ouvert ?

Je utilise un tunnel SSH depuis le travail pour contourner divers pare-feu idiots (mon patron est d'accord :)). Le problème est que, après un certain temps, la connexion SSH se bloque généralement et le tunnel est rompu.

Si je pouvais au moins surveiller le tunnel automatiquement, je pourrais redémarrer le tunnel lorsqu'il se bloque, mais je n'ai même pas trouvé de moyen de le faire.

Points bonus pour celui qui peut me dire comment éviter que ma connexion SSH se bloque, bien sûr !

0 votes

Est-ce que votre tunnel est mort à cause de l'inactivité? J'ai rencontré ce problème lors du tunneling des ports depuis mon téléphone, j'ai donc finalement lancé des commandes factices sur la connexion pour le maintenir "actif" en utilisant la commande watch comme ceci : watch -n1 60 echo "wiiiii". Le tunnel ne mourra pas à moins que le réseau soit compromis ou que vous ne l'utilisiez pas.

2 votes

16voto

Matt Conway Points 91

Pour ceux qui ne veulent pas (ou) ne peuvent pas utiliser AutoSSH...

J'ai un NAS auquel je veux accéder depuis internet, je ne peux pas utiliser le renvoi de port car mon FAI utilise le CGNAT (mon adresse IP publique n'est pas vraiment celle publique, je suis derrière un autre routeur dont je n'ai aucun contrôle). Par conséquent, pour atteindre mon NAS, j'ai un VPS (que je loue chez OVH pour un coût mensuel très bas), et qui a une adresse IP publique fixe. Donc pour atteindre mon NAS depuis internet, je dois simplement créer un tunnel SSH entre mon NAS et mon VPS, qui reste ouvert de manière fiable tout le temps (pour un accès 24h/24 et 7j/7). Cependant, j'ai souffert du tunnel SSH qui était "fermé" en raison de l'inactivité (malgré le processus ssh restant actif). Cela peut facilement être surmonté en utilisant l'option de maintien de la connexion (keep alive) pour que le client (dans mon cas, le VPS) "ping" le serveur (dans mon cas, le NAS).

Pour créer un tunnel SSH, j'exécute la commande suivante (depuis le NAS) :

ssh -NT -o ServerAliveInterval=60 -o ServerAliveCountMax=10 -o ExitOnForwardFailure=yes -i /var/services/homes/foouser/.ssh/id_rsa -R 8080:localhost:80 -R 4443:localhost:443 foouser@

Pour expliquer cette commande :

  • -N - Ne pas exécuter de commande à distance; cela est utile pour simplement faire suivre les ports.
  • -T - Désactive l'allocation de pseudo-tty.
  • -R 8080:localhost:80 - Indique que le port donné sur l'hôte distant (serveur) doit être transmis à l'hôte et au port donnés du côté local. Dans ce cas, cela signifie transmettre le port 80 du serveur distant au port 8080 du client.
  • -i /chemin/vers/clé - Spécifie le chemin de la clé ssh utilisée pour établir la session ssh, sans cela vous devrez saisir le nom d'utilisateur (si non fourni) et le mot de passe pour établir la session ssh.
  • ServerAliveInterval - le nombre de secondes que le client attendra avant d'envoyer un message "en vie" au serveur pour maintenir la connexion active.
  • ServerAliveCountMax - le nombre de messages "en vie" qui peuvent être envoyés sans réponse du serveur. Si ce seuil est atteint, ssh se déconnectera du serveur, mettant fin à la session.
  • ExitOnForwardFailure - si défini sur "yes", la connexion sera terminée si ssh ne peut pas mettre en place toutes les translations de ports dynamiques, tunnel, locales et à distance demandées (par exemple, si une extrémité ne peut pas se lier et écouter sur un port spécifié).
  • foouser@ - Spécifie le compte utilisateur foouser utilisé pour établir la session ssh de redirection de port distant avec le serveur .

Il vaut également la peine d'ajouter quelques options de configuration ssh au serveur (dans mon cas, sur mon VPS) ; en ajoutant le fichier suivant s'il n'existe pas déjà :

[foouser@vps ~]$ cat /home/foouser/.ssh/config
Host *
    TCPKeepAlive yes
    ClientAliveInterval 30
    ClientAliveCountMax 9999

Remarque : vous pourriez remplacer le * (qui signifie appliquer cette configuration à "tous les hôtes") par un hôte spécifique - Dans mon cas, mon NAS (c'est-à-dire l'hôte qui se connecte à mon VPS) est derrière mon routeur ; l'adresse IP publique de mon routeur change fréquemment car elle est attribuée en DHCP (par mon FAI) donc j'ai opté pour "tous les hôtes".

Processus SystemD (NAS Synology)

J'ai également cette commande (celle qui démarre le tunnel SSH en tant que processus systemd, si cela intéresse quelqu'un, voici le script :

foouser@nas:~$ cat /etc/systemd/system/sshtunnel-web.service 
[Unit]
Description=Tunnel SSH pour WebStation
After=network.target

[Service]
Restart=always
RestartSec=1
User=foouser
ExecStart=/bin/ssh \
    -NT \
    -o ServerAliveInterval=60 \
    -o ServerAliveCountMax=10 \
    -o ExitOnForwardFailure=yes \
    -i /var/services/homes/foouser/.ssh/id_rsa \
    -R 8080:localhost:80 \
    -R 4443:localhost:443 \
    foouser@

[Install]
WantedBy=multi-user.target

Pour démarrer et activer le service du tunnel SSH :

foouser@nas:~$ sudo systemctl daemon-reload
foouser@nas:~$ sudo systemctl start sshtunnel-web.service
foouser@nas:~$ sudo systemctl enable sshtunnel-web.service

Cela a fonctionné de manière fiable pour moi pendant plusieurs mois. Cela inclut une fiabilité sur plusieurs redémarrages de mon routeur domestique, du serveur VPS et du NAS.

0 votes

Vous avez oublié d'ajouter StartLimitIntervalSec=0 à la section [Unit]. Cela désactive la fonction de limitation de taux de systemd, qui empêche le redémarrage des services s'ils échouent trop rapidement. Cela pourrait se produire si le périphérique du réseau local est temporairement indisponible, et ssh se fermerait immédiatement avec une erreur de connexion refusée.

14voto

user2793784 Points 131

Il me semble bien que vous interprétez mal ServerAliveCountMax. Tel que je le comprends dans la documentation, il s'agit du nombre de messages de serveur actif qui peuvent rester sans réponse sans que la connexion soit interrompue. Ainsi, dans des cas comme ceux que nous discutons ici, le définir sur une valeur élevée garantira simplement qu'une connexion bloquée ne sera pas détectée et terminée!

Il suffit simplement de définir ServerAliveInterval pour résoudre le problème d'un pare-feu qui oublie la connexion, et laisser un ServerAliveCountMax faible permettra à l'extrémité d'origine de détecter l'échec et de se terminer si la connexion échoue de toute façon.

Ce que vous voulez, c'est : 1) que la connexion reste ouverte en permanence dans des circonstances normales, 2) que les échecs de connexion soient détectés et que le côté d'origine se termine en cas d'échec, et 3) que la commande ssh soit réémise chaque fois qu'elle se termine (la façon de le faire dépend fortement de la plateforme, le script "while true" suggéré par Jawa est une méthode, sous OS X j'ai en fait mis en place un élément launchd).

12voto

H. Kocher Points 138

Toujours utiliser l'option SSH ServerAliveInterval en cas de problèmes de tunnel générés par des sessions NAT expirées.

Toujours utiliser une méthode de reprise en cas de perte totale de connectivité, vous avez au moins trois options ici :

  • programme autossh

  • script bash (while true do ssh ...; sleep 5; done) ne pas supprimer la commande sleep, ssh peut échouer rapidement et vous allez relancer trop de processus

  • /etc/inittab, pour avoir accès à une boîte expédiée et installée dans un autre pays, derrière NAT, sans redirection de port vers la boîte, vous pouvez le configurer pour créer un tunnel ssh de retour vers vous :

    tun1:2345:respawn:/usr/bin/ssh -i /chemin/vers/rsaKey -f -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip 'sleep 365d'
  • script upstart sur Ubuntu, où /etc/inittab n'est pas disponible :

    start on net-device-up IFACE=eth0
    stop on runlevel [01S6]
    respawn
    respawn limit 180 900
    exec ssh -i /chemin/vers/rsaKey -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip
    post-stop script
        sleep 5
    end script

ou toujours utiliser les deux méthodes.

1 votes

+1 pour l'option inline au cas où vous ne la voudriez pas pour toutes vos connexions SSH.

0 votes

Vous écrivez "dans le cas où la connectivité tombe entièrement". Maintenant je ne comprends pas, quels problèmes autossh résout-il lui-même, et lesquels ne résout-il pas? Je pensais, bien sûr, qu'il s'occuperait de toute connexion interrompue, comme débrancher le câble pendant quelques heures, mais peut-être pas?

10voto

nachopro Points 181

J'ai résolu ce problème avec ceci :

Modifier

~/.ssh/config

Et ajouter

ServerAliveInterval 15
ServerAliveCountMax 4

Conformément à la page de manuel pour ssh_config :

ServerAliveCountMax
         Définit le nombre de messages de serveur en vie (voir ci-dessous) qui peuvent être envoyés sans que ssh(1) ne reçoive de réponses du serveur. Si ce seuil est atteint lorsque des messages de serveur en vie sont envoyés, ssh se déconnectera du serveur, mettant fin à la session. Il est important de noter que l'utilisation de messages de serveur en vie est très différente du TCPKeepAlive (ci-dessous). Les messages de serveur en vie sont envoyés à travers le canal chiffré et ne peuvent donc pas être falsifiés. L'option TCP keepalive activée par TCPKeepAlive est falsifiable. Le mécanisme de serveur en vie est précieux lorsque le client ou le serveur dépendent de savoir quand une connexion est devenue inactive.

         La valeur par défaut est 3. Par exemple, si ServerAliveInterval (voir ci-dessous) est défini sur 15 et que ServerAliveCountMax est laissé à sa valeur par défaut, si le serveur devient non réactif, ssh se déconnectera après environ 45 secondes. Cette option s'applique uniquement à la version du protocole 2.

 ServerAliveInterval
         Définit un intervalle de temps en secondes après lequel si aucune donnée n'a été reçue du serveur, ssh(1) enverra un message à travers le canal chiffré pour demander une réponse du serveur. La valeur par défaut est 0, indiquant que ces messages ne seront pas envoyés au serveur. Cette option s'applique uniquement à la version du protocole 2.

0 votes

Toutes les 15 secondes semble assez fréquent pour interroger le serveur.

0 votes

@Lambart mais si la connexion est vraiment instable et se coupe souvent, elle détecte au moins une connexion morte et donne l'opportunité de réessayer plus tôt.

0 votes

Ne fonctionne pas sur macOS 10.15.3

8voto

jcomeau_ictx Points 771

ExitOnForwardFailure yes est un bon complément aux autres suggestions. Si elle se connecte mais ne parvient pas à établir le transfert de port, elle vous est tout aussi inutile que si elle n'avait pas du tout réussi à se connecter.

0 votes

Il s'agit d'une très bonne idée. Même autossh est inutile si la connexion précédente est perçue comme expirée plus tôt du côté distant que du côté local, car dans ce cas, le côté local tentera de se reconnecter, mais la redirection ne peut pas être établie car le port est toujours ouvert.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X