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

235

J'utilise un tunnel SSH depuis le travail pour contourner divers pare-feu idotiques (ça va avec mon patron :)). Le problème, c’est qu’après un certain temps, la connexion SSH se bloque et que le tunnel est rompu.

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

Des points bonus pour celui qui peut me dire comment empêcher ma connexion SSH de rester suspendue, bien sûr!

Peltier
la source
C'est ton tunnel mort parce que l'inactivité? J'ai eu ce problème lors de tunnelisation des ports de mon téléphone donc je finalement pris fin commandes fictives sur la connexion pour le frai rendre « vivant » en utilisant la watchcommande comme: watch -n1 60 echo "wiiiii". Le tunnel ne mourra pas sauf si le réseau est en panne ou si vous ne l'utilisez pas.
erm3nda
1
Connexes: unix.stackexchange.com/q/200239
sampablokuper

Réponses:

281

On dirait que vous avez besoin d' autossh . Ceci surveillera un tunnel ssh et le redémarrera au besoin. Nous l'utilisons depuis quelques années et cela semble bien fonctionner.

autossh -M 20000 -f -N your_public_server -R 1234:localhost:22 -C

Plus de détails sur le paramètre -M ici

KeithB
la source
2
+1 pour l'autossh, il fait ce qu'il dit sur l'étain. Je pense qu'une partie de sa fonctionnalité consiste également à envoyer des paquets de style persistant pour éviter tout type de délai d'attente.
akent
30
Pourriez-vous mettre l'exemple de tunnel en utilisant autosshdans la réponse?
Ehtesh Choudhury
5
autossh -f -nNT -i ~/keypair.pem -R 2000:localhost:22 [email protected] Vous remarquerez peut-être que j'ai configuré cela à l'aide de -nNT, qui ne crée pas de terminal distant, ce qui me permet de placer autossh en arrière-plan, et l'option -i permettant à SSH d'utiliser un fichier .pem. Si vous souhaitez garder une connexion ouverte tout le temps, je vous recommande vivement de passer par la configuration supplémentaire.
juckele
2
Pour ce que cela vaut, il semble généralement préférable d'omettre le -Mparamètre: bugs.debian.org/cgi-bin/bugreport.cgi?bug=351162
rinogo
2
Je l’ai fait pour pouvoir réessayer lors d’un changement de réseau, cela fonctionne bien pour moi: autossh -M 0 -o "ServerAliveInterval 10" -o "ServerAliveCountMax 2" -L 9999: localhost: 19999 [email protected]
Luke Stanley
39

Tous les pare-feu avec état oublient une connexion après ne pas avoir vu un paquet pour cette connexion pendant un certain temps (pour éviter que les tables d'état ne se remplissent de connexions où les deux extrémités sont mortes sans fermer la connexion). La plupart des implémentations TCP envoient un paquet keepalive après une longue période sans avoir à entendre de l'autre côté (2 heures est une valeur commune). Si, toutefois, il existe un pare-feu avec état qui oublie la connexion avant que les paquets keepalive puissent être envoyés, une connexion longue durée mais inactive mourra.

Si tel est le cas, la solution consiste à éviter que la connexion ne devienne inactive. OpenSSH a une option appelée ServerAliveInterval qui peut être utilisée pour empêcher la connexion de rester inactive pendant trop longtemps (en prime, elle détectera le moment où l'homologue est décédé plus tôt, même si la connexion est inactive).

CesarB
la source
L'intervalle spécifié est en secondes, vous pouvez donc effectuer quelques ajustements. Si votre pare-feu avec état a un délai d'inactivité de 5 minutes, 60 ou 120 secondes suffisent pour maintenir la connexion ouverte. C'est l'un des moyens par lesquels je garde mes sessions ssh via mon routeur domestique.
Darren Hall
Merci, cela a aidé. Mais notez (d'après une réponse moins bien notée ici, superuser.com/a/146641/115515 ) que si vous spécifiez ServerAliveInterval et non ServerAliveCountMax, vous risquez peut-être que ssh se déconnecte volontairement plus tôt que vous le souhaitiez.
Metamatt
4
@metamatt, cette réponse de rang inférieur que vous avez mentionnée est classée en bas pour une bonne raison: C’EST MAUVAIS.
Lambart
24

Sur votre propre ordinateur Mac ou Linux, configurez votre SSH pour maintenir le serveur SSH en vie toutes les 3 minutes. Ouvrez un terminal et installez votre .ssh invisible chez vous:

cd ~/.ssh/ 

puis créez un fichier de configuration d'une ligne avec:

echo "ServerAliveInterval 180" >> config

vous devriez aussi ajouter:

ServerAliveCountMax xxxx (high number)

la valeur par défaut est 3; ServerAliveInterval 180 cesse alors d'envoyer des messages après 9 minutes (3 des 3 minutes spécifiées par ServerAliveInterval).

David Shaw
la source
2
Notez que votre commande n'est pas recommandée si vous avez déjà un fichier de configuration. Utiliser >> pour la redirection serait bien mieux!
Peltier
pourquoi ServerAliveInterval 180nous donne 6 minutes? intuition me fait essayer ceci: 180/60 == 3. Alors, est-ce que ServerAliveIntervaltravailler en multiples de 30 secondes?
nemesisfixx
@mcnemesis: ServerAliveInterval 180 signifie 3 minutes. ServerAliveCountMax Par défaut, 3 signifie 3 de ces intervalles, soit 9 minutes.
Metamatt
2
Je vote cette réponse parce que merci d'avoir mentionné ServerAliveCountMax et ce qui se passe si vous spécifiez ServerAliveInterval sans ServerAliveCountMax. Mais comme les commentaires précédents, je remarque que le calcul de "cesser d’envoyer après" est erroné, et je pense que cette réponse servirait mieux si elle donnait simplement les informations sur ces options, sans nous dire comment les appliquer avec les commandes cd et echo .
Metamatt
20
Voter à la baisse, car il n’a aucun sens de définir ServerAliveCountMax sur un «nombre élevé». ServerAliveCountMax spécifie combien de fois il essaiera d'envoyer le message "keepalive" avant d'abandonner. La valeur par défaut est 3, donc avec ServerAliveInterval 180, il cessera d'envoyer UNIQUEMENT si le serveur n'a pas répondu au bout de 9 minutes, auquel cas votre connexion est probablement bien morte.
Lambart
22

J'ai utilisé le script Bash suivant pour continuer à générer de nouveaux tunnels SSH lorsque le précédent est mort. L'utilisation d'un script est pratique lorsque vous ne voulez pas ou ne pouvez pas installer de paquets supplémentaires ni utiliser le compilateur.

while true
do
  ssh <ssh_options> [user@]hostname
  sleep 15
done

Notez que cela nécessite un fichier de clés pour établir la connexion automatiquement, mais c'est également le cas avec autossh.

Jawa
la source
2
Vous devriez ajouter les raisons pour lesquelles vous utiliseriez ce script par rapport à autossh, ou est-ce simplement que c'est plus facile ainsi?
Kyrias
4
Cela n'aiderait pas si SSH se fige, n'est-ce pas?
Nafg
1
Cela aide si vous ne pouvez pas installer des choses sur le serveur. autossh ne vient pas pré-installé et la bureucratie est parfois très obtuse.
Quarkex
Oui, préférable de ne pas avoir à installer des choses. Cela fait un an que je le fais comme seul moyen de garder une machine distante accessible (même avec crontab pour l’exécuter au redémarrage). Cela n'a jamais échoué et, plus important encore, je sais pourquoi cela n'échouera jamais.
sudo le
16

Systemd convient parfaitement pour cela.

Créez un fichier de service /etc/systemd/system/sshtunnel.servicecontenant:

[Unit]
Description=SSH Tunnel
After=network.target

[Service]
Restart=always
RestartSec=20
User=sshtunnel
ExecStart=/bin/ssh -NT -o ServerAliveInterval=60 -L 5900:localhost:5900 user@otherserver

[Install]
WantedBy=multi-user.target

(Modifiez la commande ssh à votre convenance)

  • cela fonctionnera en tant qu'utilisateur sshtunnelalors assurez-vous que cet utilisateur existe en premier
  • question systemctl enable sshtunnelde le configurer pour démarrer au démarrage
  • question systemctl start sshtunnelpour commencer immédiatement

Mise à jour janvier 2018 : certaines distributions (par exemple Fedora 27) peuvent utiliser la stratégie SELinux pour empêcher l'utilisation de SSH à partir de systemd init. Dans ce cas, une stratégie personnalisée devra être créée pour fournir les exemptions nécessaires.

IanB
la source
2
Cela ressemble beaucoup à mon esprit : gist.github.com/guettli/… Vos commentaires sont les bienvenus!
Guettli
Excellent pour un systemdsystème. Si l’on utilise Restart=on-failurealors tuer manuellement le client SSH n’entraînera pas un redémarrage par système en tant que client SSH avec une sortie réussie.
David Tonhofer
Si vous voulez démarrer ssh à partir d'un script (bash) donné en argument, ExecStartpar exemple pour construire la sshliste d'arguments, effectuez les vérifications de base, puis appelez-le à partir du script de la manière suivante exec /bin/ssh -N .... Voici ma commande: exec /bin/ssh -N -oExitOnForwardFailure=Yes -oTCPKeepAlive=no -oServerAliveInterval=5 -oServerAliveCountMax=6 -i "${LOCAL_PRIVATE_KEY}" -L "${TUNNEL_INLET}:${TUNNEL_OUTLET}" "${REMOTE_USER}@${REMOTE_MACHINE}"TUNNEL_INLET="127.0.0.1:3307"etTUNNEL_OUTLET="127.0.0.1:3306"
David Tonhofer
10

Il me semble que vous interprétez mal ServerAliveCountMax. Si je comprends bien la documentation, c'est le nombre de messages actifs du serveur qui peuvent rester sans réponse sans que la connexion soit interrompue. Donc, dans les cas comme ceux dont nous discutons ici, définir une valeur élevée garantira simplement qu’une connexion bloquée ne sera ni détectée ni interrompue!

Le simple fait de définir ServerAliveInterval devrait suffire à résoudre le problème avec un pare-feu qui oublie la connexion. Si vous laissez ServerAliveCountMax low, l’auteur de l’origine peut constater l’échec et s’arrêter si la connexion échoue quand même.

Ce que vous souhaitez, c’est 1) que la connexion reste ouverte en permanence dans des circonstances normales, 2) que l’échec de la connexion soit détecté et que le côté d’origine quitte en cas d’échec, et 3) que la commande ssh soit ré-émise chaque fois exits (la manière dont vous faites cela dépend beaucoup de la plate-forme, le script "while true" suggéré par Jawa est un moyen, sous OS XI, de configurer un élément de lancement).

utilisateur2793784
la source
9

Utilisez toujours l' ServerAliveIntervaloption SSH dans le cas où les problèmes de tunnel sont générés par des sessions NAT expirées.

Utilisez toujours une méthode de réapparition au cas où la connectivité cesserait complètement, vous avez au moins trois options ici:

  • programme autossh
  • bash script ( while true do ssh ...; sleep 5; done) ne supprime pas la commande de veille, sshrisque d'échouer rapidement et de réapparaître trop de processus
  • /etc/inittab, pour avoir accès à une boîte livrée et installée dans un autre pays, derrière NAT, sans transfert de port vers la boîte, vous pouvez le configurer pour créer un tunnel ssh de nouveau à vous:

    tun1:2345:respawn:/usr/bin/ssh -i /path/to/rsaKey -f -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip 'sleep 365d'
    
  • script de démarrage sur Ubuntu, où /etc/inittabn'est pas disponible:

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

ou utilisez toujours les deux méthodes.

Claudiuf
la source
1
+1 pour l'option en ligne si vous ne le souhaitez pas pour toutes vos connexions SSH
utilisateur1146334
Vous écrivez "au cas où la connectivité tomberait complètement". Maintenant, je ne comprends pas, quels problèmes autossh se corrige-t-il et que ne fait-il pas? Je pensais, bien sûr, que cela prendrait soin de toute connexion brisée, comme débrancher le câble pendant quelques heures, mais peut-être pas?
Mads Skjern
6

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

Modifier

~/.ssh/config

Et ajouter

ServerAliveInterval 15
ServerAliveCountMax 4

Selon la page de manuel de ssh_config:

ServerAliveCountMax
         Sets the number of server alive messages (see below) which may be
         sent without ssh(1) receiving any messages back from the server.
         If this threshold is reached while server alive messages are
         being sent, ssh will disconnect from the server, terminating the
         session.  It is important to note that the use of server alive
         messages is very different from TCPKeepAlive (below).  The server
         alive messages are sent through the encrypted channel and there‐
         fore will not be spoofable.  The TCP keepalive option enabled by
         TCPKeepAlive is spoofable.  The server alive mechanism is valu‐
         able when the client or server depend on knowing when a connec‐
         tion has become inactive.

         The default value is 3.  If, for example, ServerAliveInterval
         (see below) is set to 15 and ServerAliveCountMax is left at the
         default, if the server becomes unresponsive, ssh will disconnect
         after approximately 45 seconds.  This option applies to protocol
         version 2 only.

 ServerAliveInterval
         Sets a timeout interval in seconds after which if no data has
         been received from the server, ssh(1) will send a message through
         the encrypted channel to request a response from the server.  The
         default is 0, indicating that these messages will not be sent to
         the server.  This option applies to protocol version 2 only.
nachopro
la source
Il semble assez souvent, toutes les 15 secondes, envoyer une requête ping au serveur.
Lambart
@Lambart, mais si la connexion est vraiment irrégulière et interrompt souvent les connexions, il détecte au moins une connexion inactive et permet de réessayer plus tôt.
binki
4

ExitOnForwardFailure yesest un bon complément aux autres suggestions. S'il se connecte mais ne peut pas établir la redirection de port, il est tout aussi inutile pour vous que s'il ne s'était pas connecté du tout.

jcomeau_ictx
la source
C'est 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 que l’hôte local, car dans ce cas, l’hôte local essaiera de se reconnecter, mais le transfert ne pourra pas être établi car le port est toujours ouvert.
Raúl Salinas-Monteagudo
1

J'ai eu besoin de maintenir un tunnel SSH à long terme. Ma solution fonctionnait à partir d'un serveur Linux, et il ne s'agit que d'un petit programme en C qui rappelle ssh en utilisant l'authentification par clé.

Je ne suis pas sûr de la pendaison, mais des tunnels sont morts suite à des délais trop longs.

J'adorerais fournir le code au répondant, mais je n'arrive pas à le trouver pour le moment.

Baumgart
la source
1

Bien qu'il existe des outils comme autossh qui aident à redémarrer la session SSH ... ce que je trouve vraiment utile, c'est de lancer la commande "screen". Il vous permet de reprendre vos sessions SSH même après votre déconnexion. Particulièrement utile si votre connexion n’est pas aussi fiable qu’elle devrait l’être.

... n'oubliez pas de cocher la réponse "correcte" si cela vous aide à k! ;-)

Koss
la source
7
... mais la question était de savoir comment garder les tunnels SSH ouverts, pas seulement une session de terminal. L'écran est génial quand même!
akent
J'utilise déjà l'écran, mais cela ne résout pas mon problème: - / Merci pour votre réponse, cependant.
Peltier
1

Un peu de bidouille, mais j'aime bien utiliser screen pour le garder. J'ai actuellement une avance à distance qui fonctionne depuis des semaines.

Exemple, en commençant localement:

screen
ssh -R ......

Lorsque le transfert à distance est appliqué et que vous avez un shell sur l'ordinateur distant:

screen
Ctrl + a + d

Vous avez maintenant un transfert à distance ininterrompu. L'astuce consiste à lancer l'écran des deux côtés

landypro
la source
1

J'ai récemment eu ce problème moi-même, car ces solutions vous obligent à ressaisir le mot de passe chaque fois que vous utilisez un identifiant de mot de passe. J'ai utilisé sshpass dans une boucle avec une invite de texte pour éviter d'avoir le mot de passe dans le fichier de commandes.

Je pensais partager ma solution sur cette thead au cas où quelqu'un d'autre aurait le même problème:

#!/bin/bash
read -s -p "Password: " pass
while true
do
    sshpass -p "$pass" ssh user@address -p port
    sleep 1
done
Brainfloat
la source
0

J'ai eu des problèmes similaires avec mon précédent fournisseur de services Internet. Pour moi, c'était la même chose avec n'importe quelle connexion TCP, visite de sites Web ou envoi de courrier.

La solution consistait à configurer une connexion VPN sur UDP (j'utilisais OpenVPN). Cette connexion était plus tolérante vis-à-vis de la cause des déconnexions. Ensuite, vous pouvez exécuter n'importe quel service via cette connexion.

Il peut toujours y avoir des problèmes avec la connexion, mais puisque le tunnel sera plus tolérant, toute session SSH ressentira un bref blocage plutôt que d'être déconnectée.

Pour ce faire, vous aurez besoin d’un service VPN en ligne que vous pouvez configurer sur votre propre serveur.

hultqvist
la source
0

Comme autosshcela ne répond pas à nos besoins (il existe une erreur s'il ne parvient pas à se connecter au serveur dès la première tentative), nous avons écrit une application pure bash: https://github.com/aktos-io/link- avec serveur

Il crée un tunnel inverse pour le port sshd (22) du NODE sur le serveur par défaut. Si vous devez effectuer d'autres actions (telles que le transfert de ports supplémentaires, l'envoi de mails en connexion, etc.), vous pouvez placer vos scripts on-connectet vos on-disconnectdossiers.

ceremcem
la source