跟进:看起来,每台服务器运行几个月后出现的一系列快速断开连接可能只是巧合,只是为了揭示实际问题。无法重新连接的原因几乎肯定是由于 AliveInterval 值(kasperd 的答案)。使用 ExitOnForwardFailure 选项应该允许在重新连接之前正确发生超时,这在大多数情况下应该可以解决问题。MadHatter 的建议(kill 脚本)可能是确保隧道即使其他一切都失败也能重新连接的最佳方法。
我有一台位于防火墙后面的服务器 (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
。
进入/var/log/secure
服务器上的 sshd 显示以下错误:
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 在连接丢失时没有关闭其端的端口。我无法解释为什么,或者为什么在几个月近乎完美的运行之后会突然发生这种情况。
为了澄清这一点,我首先需要弄清楚为什么 sshd 在隧道失败后拒绝监听端口,这似乎是由于 sshd 保持端口打开并且从不关闭它们造成的。这似乎是主要问题。我只是不确定是什么原因导致它在几个月的表现都符合我的预期(即立即关闭端口并允许脚本重新连接)之后出现这种情况。
答案1
我同意 MadHatter 的观点,这可能是来自已失效 ssh 连接的端口转发。即使你当前的问题出在别的地方,你迟早也会遇到这种已失效的 ssh 连接。
出现此类断开连接的情况有三种:
- 两个端点中的一个重新启动,而连接的另一端完全处于空闲状态。
- 两个端点之一关闭了连接,但在关闭连接时,连接上出现了暂时中断。连接关闭后,中断持续了几分钟,因此另一端从未了解已关闭的连接。
- 在 ssh 连接的两个端点上,连接仍然完全正常,但有人在它们之间放置了一个状态设备,由于空闲导致连接超时。这个状态设备可能是 NAT 或防火墙,您之前提到的防火墙是主要嫌疑人。
弄清楚上述哪一种情况发生并不重要,因为有一种方法可以解决所有这三种情况。那就是使用 keepalive 消息。
您应该研究一下ClientAliveInterval
关键字 forsshd_config
和ServerAliveInterval
间隔 for ssh_config
or ~/.ssh/config
。
在循环中运行该ssh
命令可以正常工作。在循环中插入一个 sleep 也是一个好主意,这样当连接因某种原因失败时,您就不会最终淹没服务器。
如果客户端在服务器端连接终止之前重新连接,则可能会出现新的 ssh 连接处于活动状态,但没有端口转发的情况。为了避免这种情况,您需要ExitOnForwardFailure
在客户端使用关键字。
答案2
对于我来说,当ssh
隧道断开时,连接需要一段时间才能重置,因此该ssh
过程继续阻塞,导致我没有活动隧道,我不知道为什么。一种解决方法是将和ssh
置于后台以-f
产生新连接,而无需等待旧连接重置。可-o ExitOnForwardFailure=yes
用于限制新进程的数量。-o ServerAliveInterval=60
提高当前连接的可靠性。
您可以ssh
频繁重复该命令,例如,cron
在脚本中,或在循环中,例如,在下面,我们ssh
每 3 分钟运行一次命令:
while (1)
do
ssh -f user@hostname -Rport:host:hostport -N -o ExitOnForwardFailure=yes -o ServerAliveInterval=60
sleep 180
done
答案3
您可以使用以下命令找到绑定该服务器上端口的进程
sudo netstat -apn|grep -w X
看起来很可能是半失效的sshd
,但是当你有数据的时候为什么要做假设呢?这也是脚本在尝试重新启动隧道之前找到要发送信号 9 的 PID 的好方法。
答案4
根据我的经验,如果远程系统上仍有“某项”程序在运行,ssh 有一个令人讨厌的习惯,那就是不会干净地退出。例如在后台启动。您可以通过以下方式重现此情况:
ssh <server>
while true; do sleep 60; done&
exit
您的 ssh 将注销,但实际上不会关闭会话 - 直到远程进程退出(它不会退出,因为它是一个“while true”循环)。可能发生类似的事情 - 您的会话有一个由 ssh 生成的“卡住”进程。该端口仍在使用中,因此您的本地进程无法重新使用它。