运行时ssh -R 2222:localhost:22 remotehost
,ssh 客户端将继续侦听端口 2222,remotehost
无论remotehost
该客户端转发到localhost
端口 22 的现有到端口 2222 的 tcp 连接有多少。
有没有办法配置转发端口的 ssh 客户端仅获得第一个连接进而停止听在转发端口上(不干扰现有转发的 tcp 连接)? (用户无权更改 上 ssh 服务器的配置remotehost
。)
我试图获得类似的行为socat
的tcp4-listen
(除非存在选项,否则不接受第二个连接fork
),但通过安全的 ssh 通道。
编辑/附录:我的问题可以通过假设的“ssh 功能请求”来解释。假设 ssh 有一个ssh_config
客户端选项ForwardLimit num,mins
在这种情况下,如果连接数num
在最后几分钟内达到,ssh 将暂时停止侦听,如果连接数为零mins
,则永久停止侦听。 mins
(这就像防火墙速率控制,但由在用户空间中运行的 ssh 客户端强制执行。)然后可以通过添加选项来解决问题ForwardLimit 1,0
。但是,由于这样的功能不存在,我正在寻找最好的解决方法。
答案1
这里有一个方法,使用OpenSSH,关闭打开的远程转发,而不需要关闭SSH启动它的连接。将其集成到 OP 的确切用例中应该很容易。
这需要使用ControlMaster
选项,它的同伴ControlPath
(应该使用足够的进一步定制代币,但这里保持简单)和-O
的参数ssh
.
ControlMaster
允许通过单个网络连接共享多个会话。当设置为 时
yes
,ssh(1) 将侦听使用参数指定的控制套接字上的连接ControlPath
。其他会话可以使用ControlPath
与ControlMaster
设置为no
(默认)相同的方式连接到此套接字。这些会话将尝试重用主实例的网络连接,而不是启动新的网络连接 [...]
-O
ctl_cmd控制活动连接复用主进程。当指定 -O 选项时,ctl_cmd 参数将被解释并传递到主进程。有效命令有:“check”(检查主进程是否正在运行)、“forward”(请求转发而不执行命令)、“cancel”(取消转发)、“exit”(请求主站退出)和“stop”(请求主站停止接受进一步的复用请求)。
第一个 ssh 命令应使用选项ControlMaster=yes
(或auto
):
ssh -o ControlMaster=yes -o ControlPath=/tmp/mysshcontrolpath -R 2222:localhost:22 remotehost
任何进一步SSH与主机交互的命令SSH连接应该使用ControlMaster=no
(或也auto
)和使用完全一样ControlPath
。
以某种方式等待传入的 ssh 连接(这是需要集成的部分)并运行以下命令。如果同一用户重新连接,则甚至可以通过传入连接执行以下命令,因为它现在正在本地系统上执行(不需要秘密,但必须使用ControlPath
同一用户访问套接字):
ssh -o ControlMaster=no -o ControlPath=/tmp/mysshcontrolpath -O cancel -R 2222:localhost:22 remotehost
除了一个小的(如果使用非交互式身份验证)竞争窗口之外,如果传入的连接立即运行,则可以允许第二个连接在第一个连接正在进行时成功SSH连接成功后,侦听套接字将被关闭,以防止进一步的连接。
此命令不会尝试连接到remotehost
(使用 时-O
,语法仍然需要远程主机参数,但会被忽略):它只会连接(通过本地 UNIX 套接字)到主服务器SSH仍在运行并要求其关闭指定的远程转发 ( -O cancel -R 2222:localhost:22
)。
它也可以用于在关闭后添加另一个或相同的内容,使用:)-O forward -R 2222:localhost:22
。实际上,使用-O ...
是在不是主机时改变转发的唯一方法SSH命令(并使用ControlPath
由此类主 ssh 创建的命令):以这种方式使用的其他 ssh 命令不能拥有其自己的转发,因为他们正在重用主服务器SSH连接。
另请参阅ControlPersist
根据用例哪些可能有用:是否初始(主)SSH完成后将保持打开状态或不打开状态。如果启用,它可能会与-N
具有某种按需转发机制的选项一起使用。同样-O exit
也可以有它的用处。
答案2
这个答案扩展了@AB 基于 ssh-O cancel
操作的答案。这里的取消操作是自动触发 在 TCP 级别当第一个转发的连接开始时立即。此外,即使在第一个连接已开始但取消操作尚未完成的窗口期间,也会阻止任何第二个连接。
(注:硬编码转发端口号2222(远程监听端口)和22(本地目标端口)只是示例;一般远程转发会使用任意tcp端口(-R remoteport:localhost:localport
)。)
使第一个转发连接自动触发阻止进一步连接的操作
这个想法(正如我在 @AB 的答案的第一条评论中概述的那样)是通过无分叉在本地主机上注入一个额外的 tcp 转发中间步骤索卡特“拦截来自”的传入转发连接并在完成到最终目标端口的转发之前remotehost
运行该操作。-O cancel
步骤如下:
第一个 ssh 命令与上面 AB 的答案类似,但我们使用可用的非特权中间端口(例如 1111)来代替最终目标端口 22:
ssh -o ControlMaster=yes -o ControlPath=/tmp/mysshcontrolpath -R 2222:localhost:1111 remotehost
(此时,对端口 2222 的连接尝试
remotehost
将失败,因为它将被转发到本地端端口 1111,而本地端未侦听端口 1111。)现在在本地主机上运行以下命令,在第一个传入转发连接时将触发“取消在 ssh 远程端口上的进一步侦听并完成当前传入 tcp 连接”:
socat tcp4-listen:1111 system:'"ssh -o ControlMaster=no -o ControlPath=/tmp/mysshcontrolpath -O cancel -R 2222:localhost:1111 remotehost && socat - tcp4:localhost:22"',nofork
(这里我们假设已经使用公钥、ssh-agent 等设置了非交互式 ssh 身份验证。)
一个微妙的点。在此设置中,第二个远程连接永远不会成功完成,即使在第一个远程连接仍在启动的比赛窗口期间也是如此。然而,这仅从高层视图(例如从上面的传输层)来看才是正确的,因为第二个远程连接永远不会完成将连接完全转发到本地计算机上的最终目标端口。但是从较低级别的“防火墙”视图(例如网络层)来看remotehost
,在竞争窗口期间建立了与远程转发端口的第二个连接(当第一个连接仍在启动并且取消操作尚未生效时)仍然会看到 TCP 响应(事实上,将发生完整的 TCP 握手),这与 上根本没有被侦听的远程转发端口不同remotehost
。