关于 ssh ProxyCommand

Rob*_*ino 6 bash process io-redirection openssh

我正在寻找对以下 ProxyCommand 的深入解释,包括其操作的具体细节。如果可以的话,你能否为我彻底剖析它并改进它?为了可读性,如果没有别的。

ProxyCommand ssh gatewayserver 'exec 3<>/dev/tcp/targetserver/22; cat <&3 & cat >&3;kill $!'
Run Code Online (Sandbox Code Playgroud)

Sté*_*nez 5

(我的/dev/tcp系统上没有设备;但是bash似乎有一些内置的处理方法,分配一个连接到以下/host/port部分的 tcp 套接字。)

因此,您的 ssh 代理命令安全地运行一个 shell,gatewayserver它执行以下操作:

exec 3<>/dev/tcp/targetserver/22
Run Code Online (Sandbox Code Playgroud)

即在文件描述符 3 上附加一个套接字(连接到targetserver/ port)。然后:

cat <&3 & cat >&3; kill $!
Run Code Online (Sandbox Code Playgroud)

这是一种在文件描述符0(输入)和1(输出)和文件描述符3(输入和输出)之间进行双向重定向(使用两个单独的进程)的方法。的kill $!是那里杀后台进程cat <&3的其他工艺后,cat >&3又回来了。

所有这些只是一些更标准的等价物:

ProxyCommand ssh gatewayserver "tcpconnect targetserver port"
Run Code Online (Sandbox Code Playgroud)

使用/dev/tcp(或bash)功能代替tcpconnect命令。


更多细节:

ssh 使用的代理命令用于定义如何连接到远程主机targetserver(那里并不真正需要加密,因为将此通道上使用 ssh 协议)。在我们的例子中,我们想建立到这个目标主机的连接槽gatewayserver(可能是因为防火墙阻止targetserver直接连接。

所以一个过程

ssh gatewayserver 'exec 3<>/dev/tcp/targetserver/22; cat <&3 & cat >&3;kill $!'
Run Code Online (Sandbox Code Playgroud)

已启动并且:

  • 1ssh 客户端将使用filedescriptor(fd) (又名标准输出)将数据发送到目标主机。
  • 0ssh 客户端将使用fd (又名标准输入)从远程主机读取数据。

ssh gatewayserver首先用于连接到这将是第一跳网关。在这个主机上启动一个新的 shell,这个ssh实例将把原始主机上进程的fd 0/fd中继1到网关主机上运行的 shell 的fd 0/fd 1。这个shell执行的命令是:

exec 3<>/dev/tcp/targetserver/22; cat <&3 & cat >&3;kill $!
Run Code Online (Sandbox Code Playgroud)

exec没有命令将不会执行任何东西,它只是采用下列重定向shell本身。通常的重定向是:

  • n>file重定向的fdnfile(开放仅书面方式,n1如果中省略)。
  • n<file重定向的fdnfile(开放为只读,n0如果中省略)。
  • n<>file将 fd 重定向nfile(打开读取和写入)。
  • 当指定为n>&mn<&mn<>&m,将FDn被重定向到文件先前由FD指向m

这里使用了以下内容:

exec 3<>/dev/tcp/targetserver/22
Run Code Online (Sandbox Code Playgroud)

这会将新创建的 fd 重定向3到某个非常特殊的文件/dev/tcp/targetserver/22(这不是真正的文件,而是 bash 本身可以理解的文件)。在这里,bash 创建一个套接字(使用 tcp 协议的特殊文件)与targetserver端口22(我们期望找到sshd服务器的位置)通信,并且该文件在 fd 上打开(读和写)3

现在我们需要在 fd 上“抽取”数据0(来自客户端的数据)并将其发送到 fd 3(连接到目标服务器)。我们还需要通过在 fd 上“抽取”数据3并将其发送回 fd 1(客户端的结果)来确保向后通信。这两个“泵”是使用两个cat过程设置的:

  • cat <&3(从外壳的 fd 读取3 并写入外壳的 fd 1。)
  • cat >&3(从外壳的 fd 读取0 并写入外壳的 fd 3。)

两者cat必须并行运行,因此需要一个后台运行。在这里,我们希望读取 fd 0(可能是 tty)的那个是留在前台的那个。这给出:

cat <&3 &  #run in the background
cat >&3; kill $!
Run Code Online (Sandbox Code Playgroud)

kill $!是那里杀后台程序($!扩展到最后转到后台进程的PID)。这样当客户端挂断时,前台进程终止,执行kill,最后一个进程也终止。

就是这样!我们已经建立了桥梁:

源主机?—(ssh)??网关?—(泵+套接字)??目标服务器(端口22)

主机ssh user@targetserver上的一个将能够通过这个网桥连接到目标主机!