使用用户/密码进行身份验证*一次*用于多个命令? (会话多路复用)

时间:2017-01-22 03:07:52

标签: bash ssh sftp scp openssh

我从Solaris文档获得了这个技巧,用于将ssh个公钥复制到远程主机(ssh-copy-id在Solaris上不可用。)

$ cat some_data_file | ssh user@host "cat >/tmp/some_data_file; some_shell_cmd"

似乎它可以适应更多涉及的事情。具体来说,我希望some_shell_command是从本地发送的脚本,以便在远程端执行,该脚本将与本地进行交互键盘。

我尝试了从多个来源发送stdin多个内容的方法。但是一些在本地shell中起作用的东西不适用于ssh,而下面的一些东西并没有完全符合我的要求:

$ echo "abc" | cat <(echo "def")     # echoes: def  (I wanted abc\ndef)
$ echo "abc" | cat  < <(echo "def")  # echoes: def  (I wanted abc\ndef)

$ echo "abc" | cat <<-EOF
> echo $(</dev/stdin)   #echoes: echo abc  (I wanted: abc) 
> EOF

# messed with eval for the above but that was a problem too.

@chepner认为在单ssh命令中完成所有这些操作是不可行的,并解释了一个替代方案,对我来说,研究和调整他的答案并不起作用。 (我记录了这个帖子中的结果)。没有它,默认情况下必须运行多个sshscp命令需要多次提示输入密码,这是一个主要障碍,因为我不能指望脚本的所有用户我在多用户环境中编写配置公钥授权,但他们不得不一遍又一遍地输入密码。

4 个答案:

答案 0 :(得分:7)

OpenSSH会话多路复用

即使在使用早期版本的OpenSSH时,此解决方案仍然有效 ControlPersist选项不可用。 (本回答结尾处的工作bash示例)

注意:OpenSSH 3.9通过“控制主连接”(2005年)引入了会话多路复用,但是ControlPersist选项在OpenSSH 5.6之前未引入(2010年发布)

ssh会话多路复用允许脚本进行一次身份验证,然后通过经过身份验证的连接执行多个ssh事务。例如,如果您的脚本使用sshscpsftp运行多个不同的任务,则每个事务都可以通过OpenSSH'控制主会话'执行通过引用文件系统中其命名套接字的位置。在运行必须执行多个ssh操作的脚本时,此密码身份验证方案非常有用,如果公钥验证不可用或无法依赖用户进行配置。

enter image description here

我见过的最新解决方案需要使用ControlPersist告诉ssh保持控制主连接无限期地打开,或者持续一段特定的秒数。但是具有OpenSSH之前5.6的系统没有该选项(并且升级它们可能不可行)。不幸的是,似乎没有太多关于在线问题的文档或讨论。

ControlPersist在游戏中迟到了ssh会话多路复用场景。这意味着必须有一种替代方法来配置会话多路复用,而不依赖于ControlPersist选项。最初我遇到了ssh会话过早终止并关闭控制连接客户端会话的问题,或者,如果我保持连接打开(保持ssh控件主机处于活动状态),终端I / O被阻止,我的脚本将挂起!但最终我发现了完成它的旗帜。见下文。

OpenSSH option            ssh flag          Purpose
-------------------       ---------         -----------------------------
-o ControlMaster=yes      -M                Establishes sharable connection
-o ControlPath=path       -S path           Specifies path of connection's named socket
-o ControlPersist=600                       Keep shareable connection open 10 min.
-o ControlPersist=yes                       Keep shareable connection open indefinitely
                          -N                Don't create shell or run a command
                          -f                Go into background after authenticating
                          -O exit           Closes persistent connection
ControlPersist form       Equivalent        Purpose
-------------------       ----------------  -------------------------
-o ControlPersist=yes     ssh -Nf           Keep control connection open indefinitely
-o ControlPersist=300     ssh -f sleep 300  Keep control connection open 5 min.

注意:scpsftp以不同的方式实现-S标记,而-M标记完全没有,因此,对于这些命令,{始终需要{1}}表格。

粗略的操作概述:
注意:此不完整的示例未按所示执行。

-o option

会话多路复用演示 (工作示例 - 仅验证一次):
如果您无法访问远程主机,只需在“主机...?”处输入localhost即可。提示您是否要尝试此演示脚本

ctl=<path to dir to store named socket>
ssh -fNMS $ctl user@host      # open control master connection
ssh -S $ctl …                 # example of ssh over connection
scp  -o ControlPath=$ctl …    # example of scp over connection
sftp -o ControlPath=$ctl …    # example of sftp over connection
ssh -S $ctl -O exit           # close control master connection

答案 1 :(得分:2)

$ echo "abc" | cat <(echo "def")

表达式<(echo "def")扩展为文件名,通常类似于/dev/fd/63,用于命名包含文本"def"的(虚拟)文件。所以让我们简化一下:

$ echo "def" > def.txt
$ echo "abc" | cat def.txt

这也只打印def

管道 将行abc提供给cat命令的标准输入。但是因为cat在其命令行上有一个文件名,它不会从其标准输入读取。 abc只是被静静地忽略了,cat命令打印了指定文件的内容 - 这正是你告诉它要做的。

答案 2 :(得分:2)

使用主控制套接字,您可以使用多个进程,而无需多次进行身份验证。这只是一个简单的例子;有关使用更安全的套接字的建议,请参阅man ssh_config下的ControlPath

通过本地采购somecommand,您的意思并不十分清楚;我将假设它是一个本地脚本,您希望将其复制到远程主机。最简单的方法就是将其复制以运行它。

# Copy the first file, and tell ssh to keep the connection open
# in the background after scp completes
$ scp -o ControlMaster=yes -o ControlPersist=yes -o ControlPath=%C somefile user@host:/tmp/somefile
# Copy the script on the same connection
$ scp -o ControlPath=%C somecommand user@host:
# Run the script on the same connection
$ ssh -o ControlPath=%C user@host somecommand
# Close the connection
$ ssh -o ControlPath=%C -O exit user@host

当然,用户可以使用公钥身份验证来避免在所有中输入他们的凭据,但ssh每次都会经历身份验证过程。此处,身份验证过程仅使用ControlMaster=yes命令一次完成。其他两个进程重用该连接。使用-O exit的最后一个命令实际上并不连接;它只是告诉本地连接关闭它。

答案 3 :(得分:1)

echo abc | cat <(echo def)的问题是<()赢得“提供输入”竞赛。幸运的是,bash将允许您使用多个<()构造提供许多输入。所以诀窍是,如何将echo abc的输出输入<()

怎么样:

    $ echo abc | cat <(echo def) <(cat)
    def
    abc

如果您需要先处理来自管道的输入,只需切换顺序:

    $ echo abc | cat <(cat) <(echo def)
    abc
    def