SSH:除了 stdin、stdout、stderr 之外,还提供额外的“管道”fd

SSH:除了 stdin、stdout、stderr 之外,还提供额外的“管道”fd

当使用 SSH 连接到主机时,通常在主机和来宾之间提供三个“管道”,分别为stdinstdoutstderr

是否有命令行选项可以为其他文件描述符(3及以后)创建转发?

例如,我想做

ssh --forwardfd=10:3 remotehost 'echo test >&3'

这会将“test”打印到本地打开的文件描述符 10。

答案1

您可以使用套接字转发来完成此操作,该功能自 openssh-6.7 起可用。这是某种管道。该技术的描述如下:http://www.25thandclement.com/~william/projects/streamlocal.html

您将获得数据的双向路径。有mysql的例子:

将远程服务器上的 MySQL 客户端连接代理到本地实例:

ssh -R/var/run/mysql.sock:/var/run/mysql.sock \
    -R127.0.0.1:3306:/var/run/mysql.sock somehost 

答案2

@jakuje 的答案的问题是:它只适用于插座,但是您不能使用期望的标准 UNIX 工具文件跟他们:

ssh -R/tmp/sock.remote:/tmp/sock.local "$HOST" 'LANG=C cat >/tmp/sock.remote'

bash: /tmp/sock.remote: 没有这样的设备或地址

还有一个问题是本地socket文件在远程主机上没有被删除;当您下次运行相同的命令时,您会收到警告,并且套接字未正确重新创建。您可以选择-o StreamLocalBindUnlink=yes取消ssh旧套接字的链接,但在我的测试中这还不够;您还必须编辑您以sshd_config包含StreamLocalBindUnlink=yes该选项才能起作用。

但您可以使用socatnetcat或任何其他类似的工具支持UNIX 本地套接字netcat-traditional不是足够了!)使用本地套接字转发进行文件传输:

# start local process listening on local socket, which receives the data when ssh opens the connections and saves it to a local file
nc -l -N -U /tmp/sock.local >/tmp/file.local &
# connect to remote $HOST and pipe the remote file into the remote socket end
ssh \
 -o ExitOnForwardFailure=yes \
 -o StreamLocalBindUnlink=yes \
 -R /tmp/sock.remote:/tmp/sock.local \
 "$HOST" \
 'nc -N -U /tmp/sock.remote </tmp/file.remote'

您还可以运行交互式命令,在这种情况下,您应该使用它ssh -t来分配 TTY。

此解决方案的问题在于,您必须对 UNIX 本地套接字的路径进行硬编码:在本地,这并不是什么大问题,因为您可以$$在路径中包含临时目录,以使其对每个进程或用户都是唯一的,但是在远程端你最好不要/tmp/像我在示例中那样使用 world-writeable 目录。当ssh会话启动时,该目录也必须已经存在。即使会话关闭后,套接字 inode 仍将保留,因此使用“$HOME/.ssh.$$”之类的内容会随着时间的推移用死 inode 使您的目录变得混乱。

您还可以使用绑定到 的 TCP 套接字localhost,这将使您免于因死 inode 而使文件系统变得混乱,但即使使用它们,您仍然需要选择一个(唯一的)未使用的端口号。所以还是不太理想。 (ssh有动态分配端口的代码,但我发现无法在远程主机上检索该信息。)

复制文件最简单的解决方案可能是使用 ssh 的内置功能连接共享功能并在交互式会话仍在并行运行时执行scp或命令。sfrp使用 ssh 将文件复制回本地系统

答案3

我确信这应该是可能的。我只能建议一种破解方法,即使用额外的 ssh 连接来每个连接携带另一对文件描述符。例如,以下概念证明脚本首先执行 ssh 来运行虚拟命令(睡眠),将本地 fd 5 和 6 连接到远程 stdin 和 stdout,假设这些 fd 是您想要添加到通常的 0,1 中的 fd, 2.

然后真正的 ssh 就完成了,在远程它将远程 fds 5 和 6 连接到另一个 ssh 的 stdin 和 stdout。

举个例子,这个脚本将一个 gzip 压缩的手册页传递到远程,远程通过 man 解压缩并运行它。真实 ssh 的 stdin 和 stdout 仍然可用于其他用途。

#!/bin/bash
exec 5</usr/share/man/man1/ssh.1.gz 6>/tmp/out6 # pretend need 5 and 6

ssh remote 'echo $$ >/tmp/pid; exec sleep 99999' <&5 >&6 &
sleep 1 # hack. need /tmp/pid to be set

ssh remote '
  pid=$(</tmp/pid) 
  exec 5</proc/$pid/fd/0 6>/proc/$pid/fd/1
  echo start
  gzip -d <&5 | man /dev/stdin >&6
  echo stop
  kill -hup $pid
'
wait
less /tmp/out6

相关内容