为多个命令使用 user/password *一次* 进行身份验证? (会话多路复用)
Authenticating with user/password *once* for multiple commands? (session multiplexing)
我从 Solaris 文档中获得了这个技巧,用于将 ssh
public 密钥复制到远程主机(ssh-copy-id
在 Solaris 上不可用)。
$ cat some_data_file | ssh user@host "cat >/tmp/some_data_file; some_shell_cmd"
似乎它可以适应做更多复杂的事情。具体来说,我希望 some_shell_command
成为从 local 发送的脚本,在 remote 端执行,这将与 本地 键盘。
我尝试了通过 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
命令中完成所有这些是不可行的,并解释了一个对我研究和调整他的答案不起作用的替代方案。 (我在这个线程中记录了结果)。否则,默认情况下必须 运行 多个 ssh
和 scp
命令需要多次提示输入密码,这是一个主要的拖累,因为我不能指望所有用户我在多用户环境中编写的脚本,用于配置 public 密钥授权,他们也不会忍受必须一遍又一遍地输入密码。
echo abc | cat <(echo def)
的问题在于 <()
赢得了 "providing the input" 比赛。幸运的是,bash 将允许您使用多个 <()
结构提供许多输入。所以诀窍是,如何将 echo abc
的输出放入 <()
?
怎么样:
$ echo abc | cat <(echo def) <(cat)
def
abc
如果需要先处理来自管道的输入,只需调换顺序即可:
$ echo abc | cat <(cat) <(echo def)
abc
def
$ 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
命令打印指定文件的内容——这正是您告诉它要做的。
使用主控套接字,您可以使用多个进程而无需多次验证。这只是一个简单的例子;有关使用更安全套接字的建议,请参阅 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
当然,用户可以使用 public 密钥身份验证来避免输入他们的凭据 完全 ,但是 ssh
仍然会通过身份验证过程每一次。此处,身份验证过程仅通过使用 ControlMaster=yes
的命令完成 一次 。其他两个进程重用该连接。最后一个带有 -O exit
的命令实际上并没有连接;它只是告诉本地连接自行关闭。
OpenSSH 会话多路复用
此解决方案甚至在使用早期版本的 OpenSSH 时也有效,其中
ControlPersist
选项不可用。 (此答案末尾的工作 bash 示例)
注:OpenSSH 3.9 introduced Session Multiplexing over a "control master connection" (in 2005), However, the ControlPersist
option wasn't introduced until OpenSSH 5.6(2010年发布).
ssh 会话多路复用允许脚本进行一次身份验证,然后在经过身份验证的连接上执行多次 ssh t运行sactions。例如,如果您有一个使用 ssh
、scp
或 sftp
运行多个不同任务的脚本,则每个 t运行saction 都可以在 OpenSSH
'control master session' 通过引用其 named-socket 在文件系统中的位置。当 运行 脚本必须在 public 密钥身份验证不可用或不能依赖用户配置它的情况下执行多个 ssh 操作时,此密码身份验证方案很有用。
我见过的大多数当前解决方案需要使用 ControlPersist
告诉 ssh
保持控制主连接打开,无限期地或特定秒数。但是 OpenSSH 5.6 之前的系统没有这个选项(并且升级它们可能不可行)。不幸的是,网上似乎没有太多关于这个问题的文档或讨论。
ControlPersist
ssh session multiplexing 场景中迟到了。这意味着必须有一种替代方法来配置会话多路复用而不依赖 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.
注意:scp
和 sftp
实现 -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
会话多路复用演示 (工作示例 - 仅验证一次):
如果您无法访问远程主机,如果您想尝试此演示脚本,只需在 "Host...?" 提示符下输入 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 "${BOLD}Create 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 "${BOLD}Copy self to remote with scp:${NORMAL}"
scp -o ControlPath=$ctl ${BASH_SOURCE[0]} $user@$host:$rmtdir
echo -e "${BOLD}Display 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 "${BOLD}Do some pointless things with sftp:${NORMAL}"
sftp -o ControlPath=$ctl $user@$host:$rmtdir <<EOF
pwd
ls
lcd $lcldir
get *
quit
EOF
我从 Solaris 文档中获得了这个技巧,用于将 ssh
public 密钥复制到远程主机(ssh-copy-id
在 Solaris 上不可用)。
$ cat some_data_file | ssh user@host "cat >/tmp/some_data_file; some_shell_cmd"
似乎它可以适应做更多复杂的事情。具体来说,我希望 some_shell_command
成为从 local 发送的脚本,在 remote 端执行,这将与 本地 键盘。
我尝试了通过 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
命令中完成所有这些是不可行的,并解释了一个对我研究和调整他的答案不起作用的替代方案。 (我在这个线程中记录了结果)。否则,默认情况下必须 运行 多个 ssh
和 scp
命令需要多次提示输入密码,这是一个主要的拖累,因为我不能指望所有用户我在多用户环境中编写的脚本,用于配置 public 密钥授权,他们也不会忍受必须一遍又一遍地输入密码。
echo abc | cat <(echo def)
的问题在于 <()
赢得了 "providing the input" 比赛。幸运的是,bash 将允许您使用多个 <()
结构提供许多输入。所以诀窍是,如何将 echo abc
的输出放入 <()
?
怎么样:
$ echo abc | cat <(echo def) <(cat)
def
abc
如果需要先处理来自管道的输入,只需调换顺序即可:
$ echo abc | cat <(cat) <(echo def)
abc
def
$ 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
命令打印指定文件的内容——这正是您告诉它要做的。
使用主控套接字,您可以使用多个进程而无需多次验证。这只是一个简单的例子;有关使用更安全套接字的建议,请参阅 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
当然,用户可以使用 public 密钥身份验证来避免输入他们的凭据 完全 ,但是 ssh
仍然会通过身份验证过程每一次。此处,身份验证过程仅通过使用 ControlMaster=yes
的命令完成 一次 。其他两个进程重用该连接。最后一个带有 -O exit
的命令实际上并没有连接;它只是告诉本地连接自行关闭。
OpenSSH 会话多路复用
此解决方案甚至在使用早期版本的 OpenSSH 时也有效,其中
ControlPersist
选项不可用。 (此答案末尾的工作 bash 示例)
注:OpenSSH 3.9 introduced Session Multiplexing over a "control master connection" (in 2005), However, the ControlPersist
option wasn't introduced until OpenSSH 5.6(2010年发布).
ssh 会话多路复用允许脚本进行一次身份验证,然后在经过身份验证的连接上执行多次 ssh t运行sactions。例如,如果您有一个使用 ssh
、scp
或 sftp
运行多个不同任务的脚本,则每个 t运行saction 都可以在 OpenSSH
'control master session' 通过引用其 named-socket 在文件系统中的位置。当 运行 脚本必须在 public 密钥身份验证不可用或不能依赖用户配置它的情况下执行多个 ssh 操作时,此密码身份验证方案很有用。
我见过的大多数当前解决方案需要使用 ControlPersist
告诉 ssh
保持控制主连接打开,无限期地或特定秒数。但是 OpenSSH 5.6 之前的系统没有这个选项(并且升级它们可能不可行)。不幸的是,网上似乎没有太多关于这个问题的文档或讨论。
ControlPersist
ssh session multiplexing 场景中迟到了。这意味着必须有一种替代方法来配置会话多路复用而不依赖 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.
注意:scp
和 sftp
实现 -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
会话多路复用演示 (工作示例 - 仅验证一次):
如果您无法访问远程主机,如果您想尝试此演示脚本,只需在 "Host...?" 提示符下输入 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 "${BOLD}Create 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 "${BOLD}Copy self to remote with scp:${NORMAL}"
scp -o ControlPath=$ctl ${BASH_SOURCE[0]} $user@$host:$rmtdir
echo -e "${BOLD}Display 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 "${BOLD}Do some pointless things with sftp:${NORMAL}"
sftp -o ControlPath=$ctl $user@$host:$rmtdir <<EOF
pwd
ls
lcd $lcldir
get *
quit
EOF