Bash 临时删除shell脚本中的ssh私钥密码

Bash 临时删除shell脚本中的ssh私钥密码,bash,ssh,passphrase,Bash,Ssh,Passphrase,我需要将一些文件从服务器A部署到服务器B。我通过SSH连接到服务器A,然后使用存储在服务器A上的私钥通过SSH连接到服务器B,私钥位于服务器B的授权密钥文件中。从A到B的连接发生在驻留在服务器A上的Bash shell脚本中 这一切都很好,很好,也很简单,直到一个有安全意识的管理员指出我存储在服务器a上的SSH私钥没有密码保护,所以任何可能侵入我在服务器a上的帐户的人都可以访问服务器B,以及C、D、E、F和G。我想他有一个观点 他提出了一个复杂的场景,在这个场景中,我将添加一个密码短语,然后修改

我需要将一些文件从服务器A部署到服务器B。我通过SSH连接到服务器A,然后使用存储在服务器A上的私钥通过SSH连接到服务器B,私钥位于服务器B的授权密钥文件中。从A到B的连接发生在驻留在服务器A上的Bash shell脚本中

这一切都很好,很好,也很简单,直到一个有安全意识的管理员指出我存储在服务器a上的SSH私钥没有密码保护,所以任何可能侵入我在服务器a上的帐户的人都可以访问服务器B,以及C、D、E、F和G。我想他有一个观点

他提出了一个复杂的场景,在这个场景中,我将添加一个密码短语,然后修改shell脚本,在开头添加一行,在其中调用

ssh-keygen -p -f {private key file}  
用passphrase回答输入我的旧passphrase的提示,用just return回答输入我的新passphrasw的(两个)提示,这将删除该passphrase,然后在最后,在执行scp命令之后 召唤

再一次,把密码短语放回去

对此我说“是的!”

我可以先在脚本中用

read -s PASS_PHRASE
然后使用ssh-keygen的-N和-p参数根据需要提供它

它几乎可用,但我讨厌shell脚本中的交互式提示。我想把这个问题归结为一个交互式提示,但最让我头疼的是我必须按两次enter键才能去掉密码短语

这在命令行中起作用:

ssh-keygen -p -f {private key file} -P {pass phrase} -N ''
但不是来自shell脚本。在这里,似乎我必须删除-N参数并接受输入两个返回的需要

这是我能做的最好的了。有人能改进这一点吗?还是有更好的方法来处理这个问题?我不敢相信没有

最好的方法是安全地处理这个问题,而不必输入密码,但这可能要求太高。我会满足于每次脚本调用一次

下面是整个脚本的简化版本,以骨架形式呈现

#! /bin/sh
KEYFILE=$HOME/.ssh/id_dsa
PASSPHRASE=''

unset_passphrase() {
        # params
        # oldpassword keyfile
        echo "unset_key_password()"
        cmd="ssh-keygen -p -P $1 -N '' -f $2"
        echo "$cmd"
        $cmd
        echo 
}

reset_passphrase() {
        # params
        # oldpassword keyfile
        echo "reset_key_password()"
        cmd="ssh-keygen -p -N '$1' -f $2" 
        echo "$cmd"
        $cmd
        echo
}

echo "Enter passphrase:"
read -s PASSPHRASE
unset_passphrase $PASSPHRASE $KEYFILE
# do something with ssh
reset_passphrase $PASSPHRASE $KEYFILE

我可以指出另一种解决办法。我不会将密钥存储在服务器
A
上,而是将密钥保存在本地。现在,我将在端口
4000
上创建一个转发到服务器
B
的本地端口

ssh -L 4000:B:22 usernam@A
然后在新的终端中,通过隧道连接到服务器
B

ssh -p 4000 -i key_copied_from_a user_on_b@localhost

但我不知道这对您来说有多可行。

请查看ssh代理。它缓存密码短语,以便您可以在特定时间段内使用密钥文件,而不管您有多少会话


OpenSSH支持所谓的“控制主机”模式,在这种模式下,您可以连接一次,让它在后台运行,然后让其他ssh实例(包括scp、rsync、git等)重用现有连接。这样就可以只键入一次密码(在设置控制主机时),但对同一目标执行多个ssh命令

有关详细信息,请在
manssh\u config
中搜索
ControlMaster

与ssh代理相比的优势:

  • 您不必记得运行
    ssh代理
  • 您不必生成ssh公钥/私钥对,如果脚本将由许多用户运行,这一点很重要(大多数人不理解ssh密钥,因此让一大群人生成它们是一项累人的工作)
  • 根据它的配置方式,
    ssh-agent
    可能会在脚本的一段时间内超时密钥;这不会
  • 只启动一个TCP会话,因此如果反复连接(例如,一次一个复制多个小文件),速度会快得多
用法示例(请原谅堆栈溢出的损坏语法突出显示):


正如您所发现的,将命令构建为字符串是很棘手的。使用阵列时更加健壮:

cmd=( ssh-keygen -p -P "$1" -N "" -f "$2" )
echo "${cmd[@]}"
"${cmd[@]}"
甚至可以使用位置参数

passphrase="$1"
keyfile="$2"
set -- ssh-keygen -p -P "$passphrase" -N "" -f "$keyfile"
echo "$@"
"$@"

空参数不会被引号括起来,但它在那里

数组是特定于bash的——它们在POSIX shell中不起作用(
/bin/sh
)。以空格安全和POSIX兼容的方式构建命令的唯一方法是通过位置参数(如您所建议的)或转义特殊字符并使用
eval
@Richard,没错,但问题确实有一个
bash
标记。Glenn:是的,尽管ssh代理看起来像是我问题的一个完整解决方案,还有一个棘手的问题,为什么在命令行上输入
ssh-keygen-p-p$1-N'-f$2
会成功,但在bash脚本中运行相同的命令却不会成功。谢谢,这样做和通过ssh代理有什么区别,这为我提供了一个类似的一次性输入密码短语?@Steve:更新了答案,包括了与
ssh代理相比的优势
cmd=( ssh-keygen -p -P "$1" -N "" -f "$2" )
echo "${cmd[@]}"
"${cmd[@]}"
passphrase="$1"
keyfile="$2"
set -- ssh-keygen -p -P "$passphrase" -N "" -f "$keyfile"
echo "$@"
"$@"