SSH:除了标准输入,标准输出,标准错误外,还提供其他“管道” fds


12

当连接到SSH与一台主机,通常主机和客户之间提供了三个“管道”,为stdinstdoutstderr

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

例如,我想做

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

这会在本地打开的文件描述符10中打印“测试”。


2
考虑closefrom(STDERR_FILENO + 1)到OpenSSH源代码下的各种调用,可能并非没有棘手的源代码编辑。您要做什么才能做到这一点?
2015年

该协议除了stdin/ out/ 之外err,还支持通过隧道传输其他流,但是AFAIK,没有服务器/客户端以任何方式提供对该功能的支持。
salva

@thrig不是OP,已经有很长时间了,但是如果您仍然好奇这可能对您有什么用,我希望在这里找到关于如何通过ssh,bash脚本和该脚本的标准输入。类似于:infinite-output-cmd | ssh user@host bash /proc/self/fd/3 3< local-script-to-execute-remotely.sh
JoL

@thrig我--forwardfd想到甚至根本不需要这样的东西。ssh可以在打开其他任何东西之前先检查打开的文件描述符是什么,然后将它们自动转发到远程端的相同文件描述符。像我的例子一样,它可能是完全透明的。我想知道为此打补丁有多困难ssh。就像您说的那样,根据这些原因背后的原因可能会很棘手closefrom(STDERR_FILENO + 1)
JoL

Answers:


6

您可以使用套接字转发(自opensh-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 

1

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

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

仅作为示例,此脚本将经过压缩的手册页传递到远程,然后通过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

1

@jakuje给出的答案的问题是:它仅适用于套接字,但是您不能使用期望使用它们的文件的标准UNIX工具:

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

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

还有一个问题是本地套接字文件没有在远程主机上被删除。接下来,您运行相同的命令时,会收到警告,并且套接字未正确创建。你可以给的选项-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/像在我的示例中那样使用世界可写目录。ssh启动会话时,该目录也必须已经存在。并且套接字inode甚至在会话关闭后仍将保留,因此,使用诸如“ $ HOME / .ssh。$$”之类的东西会随着时间的过去使目录中的inode失效。

您还可以使用绑定到的TCP套接字localhost,这可以避免因索引节点失效而使文件系统混乱,但是即使有了它们,您仍然不得不选择一个(唯一的)未使用的端口号。因此仍然不理想。(ssh具有用于动态分配端口的代码,但是我找不到在远程主机上检索该信息的方法。)

复制文件最简单的方法可能是使用ssh的内置连接共享功能,并在交互式会话仍在并行运行时执行scpsfrp命令。请参阅使用ssh将文件复制回本地系统

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.