当使用 SSH 连接到主机时,通常在主机和来宾之间提供三个“管道”,分别为stdin
、stdout
和stderr
。
是否有命令行选项可以为其他文件描述符(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
该选项才能起作用。
但您可以使用socat
或netcat
或任何其他类似的工具支持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