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

Posted

技术标签:

【中文标题】使用用户/密码*一次*对多个命令进行身份验证? (会话多路复用)【英文标题】:Authenticating with user/password *once* for multiple commands? (session multiplexing) 【发布时间】:2017-06-06 19:33:38 【问题描述】:

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

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

似乎它可以适应做更多涉及的事情。具体来说,我希望some_shell_command 是一个从local 发送到remote 端执行的脚本,它将与local 键盘交互。

我尝试了通过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 命令需要多次提示输入密码,这是一个主要的拖累,因为我不能指望我编写的脚本的所有用户都在一个多用户环境来配置公钥授权,也不会忍受一遍又一遍地输入密码。

【问题讨论】:

很容易看出为什么echo "abc" | cat &lt;(echo "def") 不起作用,cat 只会在没有提供命令行参数或只提供- 的情况下侦听stdin。 但是标准输入来自两个地方,或者至少被描述为来自两个地方。 什么意思? &lt;(command) 是进程替换,它将评估文件描述符的路径,尝试从cat 的 POV 运行 echo &lt;(command),这与在任何其他文件上调用它相同。 是的,它不能那样工作。它基本上创建一个文件描述符并将command 的标准输出重定向到它,然后计算到该文件描述符的路径,因此它与调用cat /path/to/file 相同。查找进程替换。 cat 不同,ssh 不会从本地文件中读取命令以远程执行,因此进程替换对您没有帮助。 【参考方案1】:

OpenSSH 会话多路复用


    即使在使用早期版本的 OpenSSH 时,此解决方案也可以工作,ControlPersistoption 不可用。 (此答案末尾的工作 bash 示例)


注意:OpenSSH 3.9 在“控制主连接”上引入了会话多路复用(2005 年),但是,ControlPersist 选项直到 OpenSSH 5.6 才引入(2010 年发布).

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



我见过的大多数当前解决方案都需要使用ControlPersist 告诉ssh 保持控制主连接打开,无论是无限期还是在特定的秒数内。但是 5.6 之前的 OpenSSH 系统没有这个选项(升级它们可能不可行)。不幸的是,网上似乎没有太多关于这个问题的文档或讨论。

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


OpenSSH 选项 ssh 标志 用途 -------- --------- ---------------------- -------- -o ControlMaster=yes -M 建立共享连接 -o ControlPath=path -S path 指定连接的命名套接字的路径 -o ControlPersist=600 保持可共享连接打开 10 分钟。 -o ControlPersist=yes 无限期保持可共享连接打开 -N 不创建外壳或运行命令 -f 认证后进入后台 -O exit 关闭持久连接 ControlPersist 形式等效用途 ------------------- ---- ---------------- ---------- -o ControlPersist=yes ssh -Nf 保持控制连接无限期打开 -o ControlPersist=300 ssh -f sleep 300 保持控制连接打开 5 分钟。

注意:scpsftp 实现 -S 标志的方式不同,而 -M 标志根本没有,因此,对于这些命令,始终需要 -o option 形式。


操作概略概述:注意:这个不完整的示例不会如图所示执行。

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

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

#!/bin/bash       # This script demonstrates ssh session multiplexing

trap "[ -z "$ctl" ] || ssh -S $ctl -O exit $user@$host" EXIT # closes conn, deletes fifo

read -p "Host to connect to? " host
read -p "User to login with? " user

BOLD="\n$(tput bold)"; NORMAL="$(tput sgr0)"
echo -e "$BOLDCreate authenticated persistent control master connection:$NORMAL"
sshfifos=~/.ssh/controlmasters
[ -d $sshfifos ] || mkdir -p $sshfifos; chmod 755 $sshfifos
ctl=$sshfifos/$user@$host:22 # ssh stores named socket for open ctrl conn here

ssh -fNMS $ctl $user@$host  # Control Master: Prompts passwd then persists in background

lcldir=$(mktemp -d /tmp/XXXX);                           echo -e "\nLocal  dir: $lcldir"
rmtdir=$(ssh -S $ctl $user@$host "mktemp -d /tmp/XXXX"); echo      "Remote dir: $rmtdir"

echo -e "$BOLDCopy self to remote with scp:$NORMAL"
scp -o ControlPath=$ctl $BASH_SOURCE[0] $user@$host:$rmtdir 

echo -e "$BOLDDisplay 4 lines of remote script, with ssh:$NORMAL"
echo "====================================================================="
echo $rmtdir | ssh -S $ctl $user@$host "dir=$(</dev/stdin); head -4 \$dir/*"
echo "====================================================================="

echo -e "$BOLDDo some pointless things with sftp:$NORMAL"
sftp -o ControlPath=$ctl $user@$host:$rmtdir <<EOF
    pwd
    ls
    lcd $lcldir
    get *
    quit
EOF

【讨论】:

【参考方案2】:

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

您在本地采购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,实际上并没有连接;它只是告诉本地连接自行关闭。

【讨论】:

这接近正确的答案,但对我来说“开箱即用”并没有用。可能是因为我使用的 openssh 比你早。因此,我添加了一个答案,以显示我是如何根据您的重要信息解决它的。【参考方案3】:
$ echo "abc" | cat <(echo "def")

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

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

这也将只打印def

管道确实abc 行馈送到cat 命令的标准输入。但是因为cat 在它的命令行上被赋予了一个文件名,它不会从它的标准输入中读取abc 被悄悄忽略,cat 命令打印指定文件的内容——这正是你告诉它的。

【讨论】:

但我得到与echo "abc" | cat &lt; &lt;(echo def)相同的结果 是的,这就是我的观点。我意识到我的答案不完整;我解释了该命令的工作原理,但不好修复它,所以它可以满足您的需求。 谢谢你,我很欣赏我刚刚接受实施和暗示的答案。【参考方案4】:

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

怎么样:

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

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

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

【讨论】:

以上是关于使用用户/密码*一次*对多个命令进行身份验证? (会话多路复用)的主要内容,如果未能解决你的问题,请参考以下文章

使用多个数据库进行 Django 身份验证

使用自定义用户名/密码对 WooCommerce API 进行身份验证

可以使用设备密码对 iOS 应用程序的用户进行身份验证吗?

Java Active Directory LDAP - 使用密码哈希对用户进行身份验证

如何在 Keycloak 中使用手机号码对用户进行身份验证

使用登录名和密码或证书进行身份验证