我正在使用一个 Linux 脚本,该脚本的任务是将系统控制权转发给远程支持。在此脚本中,命令之一是 ssh 端口转发命令,它将转发远程摄像机的视频直播流的端口。在具有远程摄像头的系统上,该系统是未知的,因此假定始终位于防火墙后面,并且用户也缺乏如何端口转发其路由器以及获取动态 DNS 的知识。为了克服这个问题,“客户端”系统或相机计算机执行以下命令:
ssh -R 8089:dc-bb7925e7:8085 -p 2250 [email protected] -fNT
它将摄像机源 8085 的客户端端口转发到远程支持服务器 8089。远程支持应该能够访问 localhost:8089 并能够查看实时流。问题是这行不通。一旦我将 -f 标志插入到命令中,该命令就会中断并转发任何内容。
不管标志如何,问题是当执行此 ssh 命令时,所有其他应该运行的脚本和进程都会被搁置,因为 TTY 不允许脚本退出,直到连接断开。所以我尝试使用 -f 将 ssh 分叉到后台。这不起作用,因为端口没有被转发。我不明白为什么。
我需要的是转发端口,然后在连接保持打开状态时将其遗忘。重要的是,当客户端系统仍然正常运行时,远程支持可以控制 ssh。我究竟做错了什么?
如果不使用 -fNT,那么此功能正常,只是所有其他脚本都不会执行。
这是一个 Debian 系统。
答案1
我不认为这-f
实际上导致了你的问题。我没有发现你的命令有什么问题,在适当的情况下,你的命令会工作得很好。
但是,如果 ssh 客户端和 ssh 服务器之间的路径上有任何有状态的中间件,那么如果该中间件丢失状态,您的 ssh 连接将终止或停止。这意味着如果路径上有 NAT 或防火墙,您需要格外小心。
Keepalive 消息可以防止连接因超时而中断。它们不会阻止连接因其他原因(例如重新启动中间件)而终止,但在这些情况下,保活可以检测到它已经发生并确保连接立即终止而不是停滞。
在客户端上,您可以使用-o ServerAliveInterval=299
或类似的方法发送 keepalive,并确保 ssh 命令将很快终止,以防连接停止。通过将其包装在循环 shell 脚本中,您可以确保每次前一个连接终止时它都会打开一个新的 ssh 连接(但显然这需要脚本进行 fork 而不是 ssh 命令)。
如果您想保持端口转发处于-L
活动状态,那么ServerAliveInterval
重新生成 ssh 命令就足够了。但-R
还有另外一件事,可能会出错。
如果有任何东西已经在服务器上的端口上侦听,则将-R
无法设置端口转发,但 ssh 命令仍将继续运行。因此,在这种情况下,旨在重新生成 ssh 命令的循环不会有任何好处。
解决这个问题的方法是传递-o ExitOnForwardFailure=yes
给 ssh 命令。显然,如果您只是使用该标志组合在循环中启动 ssh,它可能会连续多次尝试绑定到同一个占用的端口。因此,在连续尝试之间休息一下是一个非常好的主意。每次 ssh 命令终止时,您的脚本应该简单地休眠几秒钟,然后再重试。
如果不存在多个脚本来执行我上面描述的所有操作,我会感到惊讶。
到目前为止我提到的内容并没有涵盖您需要的一切。
如果连接停止并且客户端注意到并尝试重新启动 ssh 命令,服务器可能尚未注意到,因此服务器端的端口仍可能被占用。如果有人以愚蠢的方式配置了防火墙,则该问题可能会无限期地持续存在,因为客户端上的脚本会反复尝试绑定到仍被阻止的端口。
由于说服每个人修复他们的防火墙配置不太可能有好的结果,因此您最好通过调整您的 来避免该问题sshd_config
,该ClientAliveInterval
设置可用于使服务器向客户端发送 keepalive 消息,并在以下情况下关闭连接它没有收到回复。
结合以上所有内容应该足以使端口转发始终保持活动状态(假设客户端和服务器之间存在网络连接)。