链接两个 ssh 端口转发

链接两个 ssh 端口转发

我一直在超级用户上浏览答案,但是没有找到可以解决我的用例的答案。

我想链接两个命令来让我通过隧道进行隧道传输:

ssh -L 22:hostB:22 user@hostA

ssh -L 10002:localhost:10001 user@localhost

现在我做的是输入第一个命令,打开另一个终端,然后输入第二个命令。如果我这样做

ssh … && ssh …

仅当我“退出”第一个连接后,第二个命令才会执行。

由于 hostA 上的服务器配置,jumphost 等更高级的 ssh 选项不起作用,因此我很确定这是唯一可以像这样访问 10001 端口的选项。

有没有办法将这两个命令链接到一个别名中,或者使用一些带有超时的简单 bash 脚本等?

答案1

我认为你不能仅仅ssh -L 10002:hostB:10001 user@hostA因为

  • 要么监听 B 的任何东西10001都只绑定到本地接口,
  • 或者 B 上的防火墙不允许hostB:10001A 连接,
  • 管他呢。

ssh … && ssh …

仅当我“退出”第一个连接后,第二个命令才会执行。

是的,这是正常的。在下一个命令运行之前,前面的部分&&必须终止并返回退出状态(或者不终止,取决于状态)。

这将几乎立即运行第二个ssh但它有缺陷:

(ssh…&)&&ssh…

这是有缺陷的,因为第一个ssh对第二个没有影响。第二个可能在第一个建立隧道之前就运行了。询问密码也可能有问题。

对于此用例ssh-f选项。从man 1 ssh

-f
请求ssh在命令执行之前进入后台。[…]

因此基本命令可能是:

ssh -fNL 22:hostB:22 user@hostA && ssh -NL 10002:localhost:10001 user@localhost

-N是否要使用第二个取决于您ssh。现在的诀窍是,第一个ssh进程在询问密码(如果适用)并建立第一个隧道后分叉到后台,因此暂时有两个“第一个”ssh进程。后台进程开始处理隧道;前台进程退出,这样就可以&&工作了。

您可能需要-o ExitOnForwardFailure=yes。如果无法建立第一个隧道,则第一个隧道ssh将失败退出,第二个隧道将无法运行。改进的命令是:

ssh -fNL 22:hostB:22 \
    -o ExitOnForwardFailure=yes \
    user@hostA &&
ssh -NL 10002:localhost:10001 user@localhost

请注意,当第二个ssh退出时,第一个(在后台)仍然存在。优点是您可以ssh稍后运行第二个。缺点是如果您不想让它保留,则需要手动终止第一个。考虑到这一点,请考虑以下方法:

ssh -fNL 22:hostB:22 \
    -o ExitOnForwardFailure=yes \
    user@hostA
ssh -NL 10002:localhost:10001 user@localhost

请注意,没有&&。现在,如果旧的第一个隧道ssh仍在运行,那么新的隧道将失败,因为它无法绑定到端口。ssh无论如何,第二个隧道都会运行,它将使用第一个隧道,而不管隧道有多旧。

可能会发生没有旧版本的情况ssh,并且第一个ssh版本会因某种原因失败。如果是这样,第二个版本将运行,并且可能会失败(连接被拒绝),因为端口上没有任何监听22

但您可能不想留下“空闲”ssh进程,您可能希望在第二个ssh进程退出后自动终止它。显然killall ssh可能会造成附带损害。让我们找一些更微妙的东西:

-M
ssh客户端置于“主”模式以进行连接共享。[…]

[…]

-O ctl_cmd
控制主动连接多路复用主进程。[…]

[…]

-S ctl_path
指定用于连接共享的控制套接字的位置 […]

#!/bin/sh

socket=/tmp/hostA.socket

ssh -fNL 22:hostB:22 \
    -o ExitOnForwardFailure=yes \
    -MS "$socket"
    user@hostA &&
{
ssh -NL 10002:localhost:10001 user@localhost
ssh -S "$socket" -O exit dummy
}

第二个命令ssh终止后,第三个命令将通知第一个命令退出。在第三个ssh命令中,重要的是套接字,而不是主机名;您仍然需要提供一些主机名,因此dummy。第一个命令ssh将在退出前删除套接字,应该不会留下任何垃圾。

但仍有几个问题值得担心:

  • 如果脚本中断,则第一个ssh会保留。为了以防万一,要么删除,要么在最开始&&发送(它可能是第零个)。-O exitssh
  • 如果第一个ssh突然死机,套接字可能会保留下来。这将使任何新的第一个失败。一个好主意可能是像这样ssh扩展零个:ssh

    ssh -S "$socket" -O exit dummy || rm "$socket"
    
  • /tmp/hostA.socket可能不是套接字的最佳位置。在命令行选项中ssh_config,等效于。看看-SControlPathman 5 ssh_config对此有如下评论:

    建议ControlPath用于机会性连接共享的任何文件至少包括%h%p%r(或备选%C)并放置在其他用户不可写入的目录中。

    /run/user/$UID如果您的操作系统支持的话,则可能存在“其他用户不可写的目录” (我相信这是systemd的事情)。

相关内容