跟进:看起来像是几个月运行每个服务器的快速断开连接可能是巧合,只是用来揭示实际的问题。 无法重新连接的原因几乎可以肯定是由于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连接。
有三种方式可以实现这种不可用的连接:
弄清楚以上三种情况中哪一种发生并不重要,因为有一种方法可以解决这三个问题。 这就是使用keepalive消息。
您应该查看sshd_config
的ClientAliveInterval
关键字和ssh_config
或~/.ssh/config
的ServerAliveInterval
时间间隔。
在循环中运行ssh
命令可以正常工作。 在循环中插入hibernate是一个好主意,当连接出于某种原因失败时,最终不会导致服务器泛滥。
如果客户端在服务器上的连接终止之前重新连接,则可能会导致新的ssh连接处于活动状态,但没有端口转发。 为了避免这种情况,您需要在客户端使用ExitOnForwardFailure
关键字。
你可以find绑定该服务器端口的进程
sudo netstat -apn|grep -w X
看起来很可能是sshd
的半失效,但是为什么在有数据的时候做假设呢? 在尝试重新启动隧道之前,脚本也可以find一个PID发送信号9的好方法。
对于我来说,当一个ssh
隧道断开它需要一段时间的连接重置,所以ssh
进程继续阻止离开我没有活动的隧道,我不知道为什么。 解决方法是使用-f
将ssh
放入后台,并产生新的连接,而无需等待旧的连接重置。 -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产生。 该端口仍在使用,因此它不能被当地进程重新使用。