SSH远程端口转发失败

跟进:看起来像是几个月运行每个服务器的快速断开连接可能是巧合,只是用来揭示实际的问题。 无法重新连接的原因几乎可以肯定是由于AliveInterval值(kasperd的答案)。 使用ExitOnForwardFailure选项应允许在重新连接之前正确地执行超时,这在大多数情况下应该解决问题。 MadHatter的build议(杀死脚本)可能是确保隧道可以重新连接,即使其他一切都失败的最好方法。

我有一台防火墙后面的服务器(A),它在几个端口上启动一个反向隧道到一个小的DigitalOcean VPS(B),所以我可以通过B的IP地址连接到A。 隧道一直工作了大约3个月,但在过去24小时内突然失败了四次。 同样的事情发生在另一个VPS提供商身上 – 几个月的完美运营,然后突然发生了多个快速失败。

我在机器A上有一个脚本,它会自动执行隧道命令( ssh -R *:X:localhost:X address_of_B对于每个端口X),但是当它执行时, Warning: remote port forwarding failed for listen port X

进入服务器上的sshd /var/log/secure显示以下错误:

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

解决需要重新启动VPS。 在此之前,所有尝试重新连接都会导致“远程端口转发失败”消息,并且无法工作。 现在到隧道只停留约4个小时的地步。

VPS上没有任何改变,它是一个单用户,单用户机器,只能作为反向隧道端点。 它在CentOS 6.5上运行OpenSSH_5.3p1。 看来sshd在连接丢失的时候并没有closures端口。 我无法解释为什么,或者为什么在经过几个月的几乎完美的操作后突然发生。

为了澄清一下,我首先需要弄清楚为什么sshd在隧道失败后拒绝监听端口,这似乎是由于sshd将端口打开并且从不closures端口引起的。 这似乎是主要的问题。 我只是不确定什么会导致它像我所期望的那样行事几个月后(即closures端口,并允许脚本重新连接)。

我同意MadHatter的观点,认为它可能是从已经失效的ssh连接端口转发的。 即使你当前的问题变成别的东西,你也可能迟早会遇到这样的ssh连接。

有三种方式可以实现这种不可用的连接:

  • 两个端点中的一个重新启动,另一端完全空闲。
  • 两个端点之一closures了连接,但在连接closures的时候,连接暂时中断。 连接closures后停电持续了几分钟,因此另一端从未了解closures连接。
  • 在ssh连接的两个端点上,连接仍然是完全正常的,但是有人在它们之间放置了一个有状态的设备,由于空闲而超时。 这个有状态的设备可能是一个NAT或防火墙,你已经提到的防火墙是一个主要的嫌疑犯。

弄清楚以上三种情况中哪一种发生并不重要,因为有一种方法可以解决这三个问题。 这就是使用keepalive消息。

您应该查看sshd_configClientAliveInterval关键字和ssh_config~/.ssh/configServerAliveInterval时间间隔。

在循环中运行ssh命令可以正常工作。 在循环中插入hibernate是一个好主意,当连接出于某种原因失败时,最终不会导致服务器泛滥。

如果客户端在服务器上的连接终止之前重新连接,则可能会导致新的ssh连接处于活动状态,但没有端口转发。 为了避免这种情况,您需要在客户端使用ExitOnForwardFailure关键字。

你可以find绑定该服务器端口的进程

 sudo netstat -apn|grep -w X 

看起来很可能是sshd的半失效,但是为什么在有数据的时候做假设呢? 在尝试重新启动隧道之前,脚本也可以find一个PID发送信号9的好方法。

对于我来说,当一个ssh隧道断开它需要一段时间的连接重置,所以ssh进程继续阻止离开我没有活动的隧道,我不知道为什么。 解决方法是使用-fssh放入后台,并产生新的连接,而无需等待旧的连接重置。 -o ExitOnForwardFailure=yes可用于限制新进程的数量。 -o ServerAliveInterval=60提高了当前连接的可靠性。

您可以频繁地重复执行ssh命令,例如在cron ,或者在脚本循环中,例如,在下面,我们每3分钟运行一次ssh命令:

 while (1) do ssh -f user@hostname -Rport:host:hostport -N -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 sleep 180 done 

根据我的经验,如果远程系统上仍然运行着“某些东西”,ssh有一个轻微的烦人的习惯,即不会干净地退出。 例如在后台开始。 你可以通过以下方式重现这个:

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

你的SSH将注销,但不会真正closures会话 – 直到远程进程退出(这不会,因为它是一个“真正的”循环)。 这可能是类似的事情发生 – 你的会话有一个“卡住”的过程,由ssh产生。 该端口仍在使用,因此它不能被当地进程重新使用。