Échec de la redirection du port distant SSH

27

Suivi: Il semble que la série rapide de déconnexions coïncidant avec quelques mois de fonctionnement de chaque serveur soit probablement fortuite et ne fasse que révéler le problème réel. La raison pour laquelle il n'a pas réussi à se reconnecter est presque certainement due aux valeurs AliveInterval (réponse de Kasperd). L'utilisation de l'option ExitOnForwardFailure devrait permettre au délai d'exécuter correctement avant de se reconnecter, ce qui devrait résoudre le problème dans la plupart des cas. La suggestion de MadHatter (le script kill) est probablement le meilleur moyen de s'assurer que le tunnel peut se reconnecter même si tout le reste échoue.

J'ai un serveur (A) derrière un pare-feu qui lance un tunnel inverse sur plusieurs ports vers un petit VPS DigitalOcean (B) afin que je puisse me connecter à A via l'adresse IP de B. Le tunnel fonctionne régulièrement depuis environ 3 mois, mais a soudainement échoué quatre fois au cours des dernières 24 heures. La même chose s'est produite il y a quelque temps sur un autre fournisseur de VPS - des mois de fonctionnement parfait, puis soudainement plusieurs pannes rapides.

J'ai un script sur la machine A qui exécute automatiquement la commande tunnel ( ssh -R *:X:localhost:X address_of_Bpour chaque port X) mais quand il s'exécute, dit-il Warning: remote port forwarding failed for listen port X.

Entrer dans le sshd /var/log/securesur le serveur montre ces erreurs:

bind: Address already in use
error: bind: Address already in use
error: channel_setup_fwd_listener: cannot listen to port: X

La résolution nécessite le redémarrage du VPS. D'ici là, toutes les tentatives de reconnexion donneront le message "échec de la redirection de port distant" et ne fonctionneront pas. C'est maintenant au point où le tunnel ne dure que 4 heures environ avant de s'arrêter.

Rien n'a changé sur le VPS, et il s'agit d'une machine à usage unique et à utilisateur unique qui ne sert que de point de terminaison de tunnel inverse. Il exécute OpenSSH_5.3p1 sur CentOS 6.5. Il semble que sshd ne ferme pas les ports de son côté lorsque la connexion est perdue. Je n'arrive pas à expliquer pourquoi, ou pourquoi cela se produirait soudainement après des mois de fonctionnement presque parfait.

Pour clarifier, je dois d'abord comprendre pourquoi sshd refuse d'écouter sur les ports après l'échec du tunnel, ce qui semble être causé par sshd laissant les ports ouverts et ne les fermant jamais. Cela semble être le principal problème. Je ne suis tout simplement pas sûr de ce qui pourrait le faire se comporter de cette façon après des mois de comportement comme je m'y attendais (c'est-à-dire fermer les ports immédiatement et permettre au script de se reconnecter).

Justin Mrkva
la source
Quelle est ta question? Comment résoudre l'erreur de liaison de port, ou comment savoir pourquoi ssh est en train de mourir, ou autre chose encore?
MadHatter prend en charge Monica
J'ai besoin de comprendre pourquoi sshd refuse d'ouvrir les ports sur le VPS (l'erreur de liaison). L'erreur de liaison de port semble être à l'origine du problème, et tout devrait fonctionner si je suis en mesure de résoudre ce problème.
Justin Mrkva
2
Pour tout retardataire, au lieu de créer manuellement un script pour maintenir la connexion ouverte, utilisez simplement autossh à la place, ce qui fait cela pour vous. serverfault.com/questions/598210/…
oligofren

Réponses:

28

Je suis d'accord avec MadHatter, qu'il s'agit probablement de redirection de port à partir de connexions ssh disparues. Même si votre problème actuel se révèle être autre chose, vous pouvez vous attendre à rencontrer de telles connexions ssh disparues tôt ou tard.

Il existe trois façons dont ces connexions disparues peuvent se produire:

  • L'un des deux points de terminaison a été redémarré tandis que l'autre extrémité de la connexion était complètement inactive.
  • L'un des deux points de terminaison a fermé la connexion, mais au moment où la connexion a été fermée, il y a eu une interruption temporaire de la connexion. La panne a duré quelques minutes après la fermeture de la connexion, et donc l'autre extrémité n'a jamais eu connaissance de la connexion fermée.
  • La connexion est toujours complètement fonctionnelle aux deux points de terminaison de la connexion ssh, mais quelqu'un a placé un périphérique avec état quelque part entre eux, ce qui a expiré la connexion en raison de l'oisiveté. Cet appareil avec état serait un NAT ou un pare-feu, le pare-feu que vous avez déjà mentionné est un suspect principal.

Déterminer lequel des trois ci-dessus se produit n'est pas très important, car il existe une méthode qui répondra aux trois. C'est l'utilisation de messages keepalive.

Vous devriez rechercher le ClientAliveIntervalmot - clé pour sshd_configet l' ServerAliveIntervalintervalle pour ssh_configou ~/.ssh/config.

L'exécution de la sshcommande en boucle peut fonctionner correctement. C'est également une bonne idée d'insérer un sommeil dans la boucle afin de ne pas inonder le serveur lorsque la connexion échoue pour une raison quelconque.

Si le client se reconnecte avant la fin de la connexion sur le serveur, vous pouvez vous retrouver dans une situation où la nouvelle connexion ssh est active, mais sans redirection de port. Pour éviter cela, vous devez utiliser le ExitOnForwardFailuremot - clé côté client.

kasperd
la source
Je pense que cela peut être le problème. En particulier, mon script sur A tentera de se reconnecter à B si le processus ssh meurt (bien sûr, puisque le message d'avertissement ne tue pas le processus ssh, il se bloque simplement lorsque cela se produit, mais c'est un problème pour un autre jour). Mais si A essaie de se reconnecter à B trop rapidement, B peut attendre que A se reconnecte. J'ai probablement besoin de m'assurer que B expire toujours avant que A se reconnecte. Combiner cela avec la suggestion de MadHatter de tuer les processus sshd avant de se reconnecter couvrira probablement 95% des cas possibles.
Justin Mrkva
1
Et en parlant du message d'avertissement de ne pas tuer SSH, cela m'a fait réfléchir ... et regarder les pages de manuel. Il s'avère que -o ExitOnForwardFailure yesc'est exactement ce dont j'avais besoin. C'est donc une chose de moins que je dois comprendre. Pour penser, j'allais écrire un script Python pour analyser ces messages d'avertissement. C'est beaucoup plus simple. : D
Justin Mrkva
Désolé d'avoir oublié ExitOnForwardFailurequand j'ai écrit ma réponse. Je l'ai ajouté à la réponse maintenant.
kasperd
4
Aucun problème, et c'était effectivement -o ExitOnForwardFailure=yes(notez le signe égal). Donc, si quelqu'un rencontre cela, ne copiez pas et ne collez pas mon commentaire précédent, cela ne fonctionnera pas. : P
Justin Mrkva
J'ai donc surveillé le serveur pendant environ 10 heures et il semble qu'il fonctionne bien; Je suppose à ce stade que cette réponse est correcte (je suis sûr à environ 99% sur la base de ce que j'ai vu) et que la série de déconnexions rapides était une coïncidence liée à des problèmes de réseau qui sont apparus quelques mois après démarrage de chaque service. Merci à tous pour votre aide. ;)
Justin Mrkva
4

Vous pouvez trouver le processus qui lie le port sur ce serveur avec

sudo netstat -apn|grep -w X

Il semble très probable que ce soit le demi-disparu sshd, mais pourquoi faire des hypothèses lorsque vous pouvez avoir des données? C'est également un bon moyen pour un script de trouver un PID auquel envoyer le signal 9 avant d'essayer de réactiver le tunnel.

MadHatter soutient Monica
la source
Je me souviens avoir vérifié cela sur le fournisseur VPS précédent, et j'ai confirmé que sshd était le processus d'écoute de ces ports. La prochaine fois que cela se produira, je le vérifierai ici, mais comme le comportement et la configuration sont exactement les mêmes, je ne m'attends pas à ce que ce soit différent.
Justin Mrkva
Génial, alors faites en sorte que votre script qui rouvre le tunnel tue le vieux tunnelier avant d'essayer de le faire.
MadHatter prend en charge Monica
Il n'y a jamais plus d'un script de tunnel (sur A) en cours d'exécution, si c'est ce que vous dites. D'un autre côté, si vous voulez que le script exécute à distance une commande sur B pour tuer les processus parasites ... ce n'est en fait pas une mauvaise idée. Mais une préoccupation est de tuer à plusieurs reprises toutes les connexions SSH si j'essaie de déboguer. Si le script sur A tue toujours B à cause d'un problème, alors je ne peux pas être constamment expulsé de B par le script voyou A. : P Je vais devoir tester pour m'assurer qu'il ne fait pas ça. Mais comme je l'ai dit, ce n'est pas une mauvaise idée. ;)
Justin Mrkva
Je ne pensais pas qu'il y en avait. Vous dites qu'il y a un script en cours d'exécution sur le serveur distant qui essaie de faire apparaître un tunnel et échoue, en raison de l'erreur de liaison, et je suppose qu'il ne s'exécute que lorsque vous en avez besoin (c'est-à-dire lorsque le tunnel existant n'est pas bon) parce que vous n'avez pas dit le contraire. Tout ce que je suggère, c'est qu'il tue le processus spécifique qui maintient le port ouvert avant d'essayer de faire apparaître le nouveau tunnel.
MadHatter prend en charge Monica
Le script exécutant ssh est uniquement sur le serveur A, le serveur B est un serveur ordinaire sans aucun script supplémentaire. Ce que je ferai probablement, c'est d'écrire un script kill à mettre sur le serveur B, puis de l'appeler à distance depuis A s'il ne parvient pas à se connecter un certain nombre de fois de suite. De cette façon, il est moins susceptible d'interférer avec d'autres connexions SSH. Et j'aurai probablement le journal du script kill à chaque exécution et quitter sans rien faire s'il est appelé trop souvent trop rapidement. Personnellement, il semble que la limitation du débit de tout script qui tue sshd soit probablement prudente. : P
Justin Mrkva
3

Pour moi, lorsqu'un sshtunnel se déconnecte, il faut un certain temps pour que la connexion se réinitialise, le sshprocessus continue de se bloquer, me laissant sans tunnel actif et je ne sais pas pourquoi. Une solution de contournement consiste à mettre sshen arrière-plan avec -fet à générer de nouvelles connexions sans attendre la réinitialisation des anciennes connexions. Le -o ExitOnForwardFailure=yespeut être utilisé pour limiter le nombre de nouveaux processus. La -o ServerAliveInterval=60améliore la fiabilité de votre connexion actuelle.

Vous pouvez répéter la sshcommande fréquemment, par exemple, dans un cron, ou, dans une boucle de votre script, par exemple, dans ce qui suit, nous exécutons la sshcommande toutes les 3 minutes:

while (1)
do
    ssh -f user@hostname -Rport:host:hostport -N -o ExitOnForwardFailure=yes -o ServerAliveInterval=60
    sleep 180
done
Stephen Quan
la source
une solution beaucoup plus robuste serait d'utiliser autossh
Marco Lavagnino
-o ExitOnForwardFailure=yesétait ce que je cherchais, merci beaucoup!
vadipp
1

D'après mon expérience, ssh a une habitude un peu gênante de ne pas quitter proprement si «quelque chose» fonctionne toujours sur le système distant. Par exemple, a commencé en arrière-plan. Vous pouvez le reproduire en:

ssh <server>
while true; do  sleep 60; done&
exit

Votre ssh se déconnectera, mais ne fermera pas réellement la session - jusqu'à ce que le processus distant se termine (ce qui ne sera pas le cas, car il s'agit d'une boucle "while true"). Il se peut que quelque chose de similaire se produise - votre session a un processus «bloqué» qui est généré par ssh. Le port reste utilisé et ne peut donc pas être réutilisé par votre processus local.

Sobrique
la source
La commande SSH complète qui s'exécute sur la machine A est ssh -o ConnectTimeout=10 -o BatchMode=yes -gnN -R *:X:localhost:X root@$TUNSRV 1>>tunnel.log 2>&1 &donc rien n'est exécuté par SSH sauf le tunnel lui-même, spécifiquement en raison de l'option -N. Tout ce qui est maintenu ouvert se fait sur le serveur distant B à l'aide de sshd lui-même.
Justin Mrkva