如何在没有密码提示的情况下自动运行ssh-add?


247

我想通过SSH在网络(静态以太网)上的多台计算机之间进行通信。为了做到这一点,我需要在每次登录到特定计算机上时都运行ssh-add,我该如何做才能将其设置一次,并且每次我登录或重新启动时都不会要求我输入密码我的机器?

我知道有一种方法可以在bash_profile文件中添加一些行,但是每次我重新启动/登录到特定计算机时,我仍然需要输入密码。

if [ -z "$SSH_AUTH_SOCK" ] ; then
    eval `ssh-agent -s`
    ssh-add
fi

Answers:


348

这是在安全性和便利性之间进行权衡的典型示例。幸运的是,有很多选择。最合适的解决方案取决于使用方案和所需的安全级别。

带有密码的ssh-key,否 ssh-agent

现在,每次使用密钥进行身份验证时都必须输入密码。从安全角度来看,这是最佳选择,但可用性却最差。这也可能导致按顺序选择了较弱的密码短语,以减轻重复输入密码短语的负担。

带有密码的ssh-key,带有 ssh-agent

添加以下内容~/.bash_profilessh-agent在登录时自动启动并加载ssh-key:

if [ -z "$SSH_AUTH_SOCK" ] ; then
  eval `ssh-agent -s`
  ssh-add
fi

现在,必须在每次登录时输入密码。从可用性的角度来看,虽然稍好一点,但它的缺点是ssh-agent,无论在登录会话期间是否使用密钥,都会提示输入密码短语。每次新登录都会生成一个不同的ssh-agent实例,即使注销后,该实例仍将使用添加的键在内存中运行,除非明确终止。

ssh_agent在注销时终止,请将以下内容添加到~/.bash_logout

if [ -n "$SSH_AUTH_SOCK" ] ; then
  eval `/usr/bin/ssh-agent -k`
fi

或以下 ~/.bash_profile

trap 'test -n "$SSH_AUTH_SOCK" && eval `/usr/bin/ssh-agent -k`' 0

ssh-agent通过在文件系统中固定位置(例如Collin Anderson的answer)中创建与代理的持久通信套接字,可以避免创建多个实例。但是,这是对生成多个代理程序实例的一种改进,除非注销后显式杀死解密的密钥仍保留在内存中。

在台式机上,台式机环境中随附的ssh代理(例如Gnome密钥环SSH代理)可能是一种更好的方法,因为通常可以使它们在首次在登录会话期间使用ssh-key时提示输入密码。将解密的私钥存储在内存中,直到会话结束。

带有密码的ssh-key,带有 ssh-ident

ssh-ident是可以ssh-agent代表您进行管理并根据需要加载身份的实用程序。它仅在需要时添加一次密钥,而与需要访问的终端,ssh或登录会话数无关ssh-agent。它还可以根据所连接的主机或从中调用目录ssh来添加和使用不同的代理和不同的密钥集。当与不同主机一起使用代理转发时,这允许隔离密钥。它还允许在GitHub等网站上使用多个帐户。

要启用ssh-ident,请安装它,并将以下别名添加到您的~/bash_profile

alias ssh='/path/to/ssh-ident'

带有密码的ssh-key,带有 keychain

keychain是一个小型实用程序,ssh-agent可以代表您进行管理,并允许ssh-agent登录会话结束时继续运行。在后续登录时,keychain将连接到现有ssh-agent实例。实际上,这意味着必须仅在重新引导后的首次登录期间输入密码。在随后的登录中,将ssh-agent使用现有实例中的未加密密钥。这对于在cron没有无密码ssh密钥的作业中允许无密码RSA / DSA身份验证也很有用。

要启用keychain,请安装它,然后将以下内容添加到~/.bash_profile

eval `keychain --agents ssh --eval id_rsa`

从安全角度来看,ssh-identkeychain比糟糕ssh-agent局限于特定会话的生存情况,但他们提供的便利性高的水平。为了提高的安全性keychain,有些人在--clear~/.bash_profile钥匙串调用中添加了该选项。通过这样做,必须按上述方式在登录时重新输入密码短语,但是cron在用户注销后,作业仍将有权访问未加密的密钥。该keychain wiki页面有更多的信息和示例。

没有密码的ssh-key

从安全角度来看,这是最糟糕的选择,因为私钥在暴露时完全不受保护。但是,这是确保重新启动后无需重新输入密码的唯一方法。

带有密码短语的ssh-key,带有 ssh-agent将密码短语传递给 ssh-add from脚本

例如,虽然ssh-add从脚本传递密码短语似乎是一个简单的主意,但是echo "passphrase\n" | ssh-add它看起来ssh-add 不像从不读取密码短语那样直接stdin,而是/dev/tty直接打开以供阅读

这可以被合作周围expect,用于自动化交互式应用程序的工具。以下是一个脚本示例,该脚本使用存储在脚本中的密码添加了ssh-key:

#!/usr/bin/expect -f
spawn ssh-add /home/user/.ssh/id_rsa
expect "Enter passphrase for /home/user/.ssh/id_rsa:"
send "passphrase\n";
expect "Identity added: /home/user/.ssh/id_rsa (/home/user/.ssh/id_rsa)"
interact

请注意,从安全的角度来看,由于密码是以明文形式存储在脚本中的,因此这比拥有无密码的ssh-key更好。如果要使用此方法,则重要的是要确保expect包含密码短语的脚本具有适当的权限设置,以使其仅由密钥所有者可读,可写和可运行。


1
好的,但是当我将您的代码放入〜/ .bash_profile时,每次登录时都必须输入密码,我也不希望这样。我根本不关心安全性。回声“ pass \ n” | ssh-add不起作用
zdun8 2013年

3
@ user1607072是的,这就是ssh-agent代码段中的~/.bash_profile行为,如答案中所述。您可能想看看该keychain实用程序。有了keychain你需要重启后第一次登录时输入密码,但后续登录keychain将连接到现有的ssh-agent实例与在内存中解密的密钥。除此之外,还可以选择不使用密码生成ssh密钥,但是当然不建议这样做。
Thomas Nyman 2013年

3
@ user1607072虽然我会强烈建议一种更安全的方法,但是有一种方法可以将密码短语ssh-add从脚本传递到。原因echo "pass\n" | ssh-add不起作用,ssh-add是不从读取密码stdin,而是/dev/tty直接打开以进行读取。使用名为的实用程序更新了答案,以包括针对此问题的解决方法expect
Thomas Nyman 2013年

1
@ user1607072对于您的用例来说,这可能有些过头,但是Kerberos与ssh GSSAPI支持结合使用也可以用于无密码的ssh登录。ssh中相应的身份验证方法称为gssapi-with-mic。通常在较大的网络中使用此方法,但是当然,如​​果您对此感兴趣,可能值得研究。
Thomas Nyman 2013年

1
@ErickBrown:已经在这里回答。如果您在systemd登录管理器中禁用了用户徘徊,则SSH Agent单元应在注销时停止。如果启用了用户徘徊,即使关闭了最后一个登录会话,systemd用户实例和SSH代理单元仍保持运行。
Thomas Nyman '18

93

将此添加到您的中~/.bashrc,然后注销并重新生效。

if [ ! -S ~/.ssh/ssh_auth_sock ]; then
  eval `ssh-agent`
  ln -sf "$SSH_AUTH_SOCK" ~/.ssh/ssh_auth_sock
fi
export SSH_AUTH_SOCK=~/.ssh/ssh_auth_sock
ssh-add -l > /dev/null || ssh-add

每次重新启动后,这仅应在您首次登录时提示输入密码。ssh-agent只要它保持运行状态,它就会一直重复使用。


1
非常简洁,这样您就只能运行一个ssh-agent(:@thomasNyman第二种解决方案中的多个代理对我来说似乎是安全隐患...
drevicko

1
在对各个站点进行研究并阅读了各种解决方案之后,这里的这一点似乎是最清晰,直接的。非常好。+1
Beco博士

1
最好这样做:“ alias ssh = ssh-check-agent”,并让check-agent版本执行上述操作:a)您仅获得一个代理,b)仅在需要时获得该代理
Erik Aronesty

2
我认为-s是默认设置,所以我们已经在这样做了。
Collin Anderson

1
ssh-add -l当代理具有身份时返回退出代码0,否则返回1,因此您可以从最后一个命令中删除grep并使用ssh-add -l > '/dev/null' || ssh-add
Grant Humphries

16

与OP的问题没有密切关系,但对其他人可能有用:由于7.2.0 ssh(1)具有一个选项,允许在首次认证时向ssh-agent添加密钥;该选项AddKeysToAgent,可以设置为yesnoask,或confirm,全系统或您的个人.ssh/config文件。

参考:https : //www.openssh.com/txt/release-7.2


2
适用于该.ssh/config文件的新手:这适用于文件ssh及其ssh背后使用的任何东西,例如scp,并且可以在每个主机的基础上完成。
SEoF

每次我登录并尝试进行git pull时,它仍然会要求我提供密码。
火车

@trainosis问题是您可能没有正在运行的ssh-agent实例将已解密的密钥保存在内存中以备将来使用。使用ssh-agent时,您只需在每个登录会话中输入一次给定密钥的密码。
eestrada

7

ssh-agent 缓存各种未锁定的ssh密钥,因此您可以使ssh密钥受密码保护,而不必每次都键入它们。

为了缓存解锁的密钥,显然需要解锁那些密钥。要解锁用密码锁锁定的钥匙,显然需要知道这些密码。

任何不需要人类授权的方法(例如“键入密码”)都将使您的系统不安全;它也会使ssh-agent的整个目的变得毫无意义。

综上所述,您可以简单地使用不受密码保护的ssh-key(Enter在生成密钥时要求输入密码时会命中)。由于没有任何密码,ssh-agent因此不需要(而不是)缓存密码。


我同意,只要您的密钥正确地只允许用户使用,与没有许可的密钥相比,ssh-agent几乎没有优势。我喜欢ssh进入登录服务器,然后该服务器具有一堆无权限密钥,每个密钥只能用于解锁另一台服务器。登录服务器无法执行其他操作,因此更难于破解/欺骗等。其他服务器没有密码访问权限,仅是密钥。
Erik Aronesty '16

5

这是一种自动化SSH密码的解决方法。

  1. 创建一个单行脚本,将您的密码打印到标准输出,例如:

     echo 'echo MY_SSH_PASSWORD' > ~/.print_ssh_password && chmod 700 ~/.print_ssh_password
    

    重要提示:确保复制前导空格以防止将密码存储到历史记录中

并使用以下方法之一。

  • 使用标准输入法:

    cat ~/.ssh/id_rsa | SSH_ASKPASS=~/.print_ssh_password ssh-add -
    
  • 命名管道方法:

    1. 创建一个命名管道(您也可以尝试使用流程替换):

      mkfifo --mode 0600 ~/.ssh_fifo
      
    2. ssh-add通过指定用于身份验证的程序来运行:

      cat ~/.ssh/id_rsa >~/.ssh_fifo | SSH_ASKPASS=~/.print_ssh_password ssh-add ~/.ssh_fifo
      

    请参阅:man ssh-add了解更多信息SSH_ASKPASS


2
echo my_passphrase是一个很大的安全漏洞。首先,键入密码后,密码将以明文形式显示在所用外壳程序的历史记录文件中。第二个命令行参数在Unix(ps -ef)上是世界可读的。切勿在命令行参数中输入密码!
ceving 2013年

1
@ceving添加额外的前导空格可以解决历史文件的问题。添加了额外的信息。
kenorb

@kenorb:不能解决更大的ps输出密码可见问题。无论如何,历史记录文件通常通常只能由拥有用户读取,但是系统上的所有用户都可以读取命令行。
Thomas Nyman

4

我不建议您在登录时使用ssh-add(需要打开ssh-agent)。这是因为您无法控制ssh-agent节何时结束,并且在不需要在一个登录节中使用密钥文件时会带来安全风险。

相反,我建议编写一个脚本来打开ssh-agent的section子外壳,并自动添加所有密钥文件,并在需要使用ssh时调用该脚本。如果可以采用,请继续阅读。

您将有两个选择:

  1. 删除密钥的所有密码短语,如果密钥文件被盗,它们的安全性很弱。(因此不推荐

  2. 对密钥使用相同的密码短语。然后,当您使用时ssh-add keyfile1 keyfile2 ...,每个部分只需键入一次密码。

在这两种情况下,您都可以如下编写脚本文件“ ssh_keys_section.sh”:

#!/bin/bash
# This script run a ssh-agent on a sub-shell and automatically ssh-add all keyfiles at once.
# This agent ends when you type `exit` to close the sub-shell.
exec ssh-agent bash -c "ssh-add /path/to/keyfile1 /path/to/keyfile2 ...; exec bash"

备注:

  • 更改或删除密码短语的命令: ssh-keygen -p -f keyfile
  • 在子外壳中,您甚至可以通过使用类似/path/to/yourterminal &(取决于操作系统)的命令来分叉更多共享相同解锁密钥的终端。

例如,在Cygwin中的Windows上,/path/to/yourterminal &==>mintty &
Johnny Wong

2
if [ ! -S ${HOME}/.ssh/ssh_auth_sock ]; then
  eval $(ssh-agent)
  ln -sf "${SSH_AUTH_SOCK}" ${HOME}/.ssh/ssh_auth_sock
fi
export SSH_AUTH_SOCK=${HOME}/.ssh/ssh_auth_sock

ssh_keys=$(find -E ~/.ssh -type f -regex '.*(rsa$|pem)')
ssh_agent_keys=$(ssh-add -l | awk '{key=NF-1; print $key}')

for k in "${ssh_keys}"; do
    for l in "${ssh_agent_keys}"; do
        if [[ ! "${k}" = "${l}" ]]; then
            ssh-add "${k}" > /dev/null 2>&1
        fi
    done
done

2

我曾经使用过Steampowered提到的脚本,现在我做了下面的脚本,因为它不会留下文件。

zsh仅在外壳上工作。

#!/bin/sh

AGENT_BIN=`which ssh-agent`
AGENT_ADD_BIN=`which ssh-add`
AGENT_PID=`ps -fe | grep ${AGENT_BIN} | awk -vuser=$USER -vcmd="$AGENT_BIN" '$1==user && $8==cmd{print $2;exit;}'`
if [ -z "$AGENT_BIN" ]; then
    echo "no ssh agent found!";
    return
fi
if [ "" -eq "$AGENT_PID" ]; then
    if read -sq "YN?Do you want to unlock your ssh keys?"; then
        echo ""
        output=`$AGENT_BIN | sed 's/echo/#echo/g'`
        eval $output
        $AGENT_ADD_BIN
    fi
else
    for f in "/proc/"*
    do
        cmdline=`cat "$f/cmdline"`
        if [ "${AGENT_BIN}" -ef "${cmdline}" ]; then
            export SSH_AUTH_SOCK=`cat $f/net/unix | grep --binary-file=text -oP '((/[^/]*?)+/ssh-[^/]+/agent\.\d+$)'`
            export SSH_AGENT_PID=${f##*/}
            break;
        fi
    done
fi

1
SSH_ENV="$HOME/.ssh/environment"

function start_agent {
     echo "Initialising new SSH agent..."
     /usr/bin/ssh-agent | sed 's/^echo/#echo/' > "${SSH_ENV}"
     echo succeeded
     chmod 600 "${SSH_ENV}"
     . "${SSH_ENV}" > /dev/null
     /usr/bin/ssh-add;
}

# Source SSH settings, if applicable

if [ -f "${SSH_ENV}" ]; then
     . "${SSH_ENV}" > /dev/null
     #ps ${SSH_AGENT_PID} doesn't work under cywgin
     ps -ef | grep ${SSH_AGENT_PID} | grep ssh-agent$ > /dev/null || {
         start_agent;
     }
else
     start_agent;
fi

在此提供信用:https : //www.cygwin.com/ml/cygwin/2001-06/msg00537.html

此解决方案也在此处得到认可:http : //mah.everybody.org/docs/ssh


1

SSH的单一登录解决方案可能会导致我进入pam_ssh

根据本文,概念是:

如果您通过ssh使用多台基于* nix的计算机,则您可能会厌倦了每次要访问另一个邮箱时都要不断输入密码的麻烦。有一种安全的方法可以让您访问ssh可以访问的每台计算机,而不必输入其他密码(原始登录密码除外)。


这实际上很简单,您基本上只需创建一个公钥/私钥对以对其他计算机进行身份验证,然后让PAM生成代理以在登录后加载密钥,从而提供一种单点登录解决方案来访问所有远程计算机。机器。本指南将引导您完成设置。

我尚未验证这是否可以正常工作。


0

将此添加到您的~/.bashrc文件:

ssh-add -L|grep identities > /dev/null && ssh-add /path/to/ssh/private/key

我看不到这与问题的关系,该问题是有关在后续登录时不提示输入密码的问题。
克里斯·

0

为了添加(可能没有密码)密钥,并确保ssh-add即使在X下运行,也无论如何都不会提示输入密码:

DISPLAY= ssh-add -k /path/to/key </dev/null &>/dev/null

退出状态指示成功或失败。


0

如果您将Seahorse作为您的密码管理器运行,则 ... D

实现您正在寻找的目标的另一个解决方案是,只需将ssh密钥添加到seahorse中,以便在登录时自动解锁。这样做的主要好处是,通过gdm登录后,即使密钥具有密码,也无需输入密钥密码。这需要私钥和公钥。他们还必须遵循海马的命名约定。可以接受默认值(对于私钥,请使用id_rsa;对于公钥,请使用id_rsa.pub…真的是privatekeynameprivatekeyname.pub

将您的ssh密钥添加到seahorse以便在登录时自动解锁;(在fedora25上,虽然路径很可能非常相似,但我不确定该路径在其他发行版中的位置)

/lib64/seahorse/seahorse-ssh-askpass /path/to/keys/here

对我来说

/lib64/seahorse/seahorse-ssh-askpass ~/.ssh/id_rsa

(在我的案例中,seahorse会自动假定公钥为id_rsa.pub)

执行完命令后,seahorse将弹出一个可爱的小gtk密码字段,以输入私钥的密码。或者如果您生成没有密码的密钥,则将其保留为空白。

如果一切正常,Seahorse不会提示您。您将需要尝试SSH到目标计算机。然后,seahorse会提示您再次以图形方式用密码解锁密钥(此操作仅会发生一次),但是这次看起来应该有所不同; P(这也是seahorse进行一些seahorse ssh添加魔术的部分) ),并提供在登录时解锁密钥的选项,您必须选中此选项才能实现目标。

仅仅因为我没有阅读所有答案,我建议您在尝试此答案之前,撤消每个人告诉您的与ssh-add有关的操作。否则可能会导致您的密钥idk发生错误。


0

这是权威脚本。

更新$ PASSW,然后将其复制粘贴到您的终端中

# <sshpass> via typinator
# Updated: 2017-01-18_21h36
#
# apt-get update -y; apt-get install expect -qy

# Pass this value to ssh-add
PASSW="myfancypass123"

# Define a name for this script
THIS_SCRIPT="$(date +%Y-%m-%d_%H-%M-%S-%N)".sh

# Create a fresh directory to work from / Clean up
rm -rf ~/temp; mkdir -p ~/temp; cd ~/temp; ls -la


# Output our bash script file - BEGIN
cat <<< '
#!/bin/bash

set -u     # Stop if an unbound variable is referenced
set -e     # Stop on first error
export HISTIGNORE="expect*";

# Normal CMDs
echo && echo "The process should take about 10 seconds:" && echo
eval "$(ssh-agent -s)"; sleep 0.5;

# Define VAR passed when this bash-script was launched
password="$@"

# Launch the expect magic
expect -c "
    spawn ssh-add /root/.ssh/id_rsa
    expect "?assword:"
    send \"$password\r\"
    expect "?password:"
    send \"$password\r\"
    expect eof"

export HISTIGNORE="";
export password="";
' > $THIS_SCRIPT
# Output our bash script file - END


# Ensure we are in the right path
cd ~/temp; ls -la; sleep 1;

# Run the bash script
chmod +x ./$THIS_SCRIPT; ./$THIS_SCRIPT "$PASSW"; unset password;

# Clean up
rm -rf ~/temp; mkdir -p ~/temp; cd ~/temp; ls -la

0

我知道的最好方法是使用从以前的工作改编的PAM登录脚本,因为在这个问题上找不到满意的答案。

您的密码短语将使用系统密码和强大的派生功能进行加密存储。登录时,系统密码用于解密密码并将其添加到代理。

https://github.com/capocasa/systemd-user-pam-ssh

与提出的所有其他解决方案相比,优点是它结合了等效于安全性的安全性,相当于在引导时以零努力手动运行ssh-add。它不需要任何额外的工具,并且具有一个默认情况下已在大多数系统(OpenSSL)上安装的额外依赖项。


0

我在macOS上的设置如下(在.zshrc.bash_profilebash人群中):

# Kill then Load the ssh-agent and set the necessary env variables it outputs
sshRestart() {
    # if all else fails
    # pkill -u $(whoami) ssh-agent;

    if [ -n "$SSH_AUTH_SOCK" ] ; then
        eval `/usr/bin/ssh-agent -k`
    fi
    eval `ssh-agent -s`
    ssh-add ~/.ssh/YOUR_KEY_FILE
    echo "Restarted SSH agent"
}

if [ -z "$SSH_AUTH_SOCK" ] || [[ $SSH_AUTH_SOCK == *"/private/tmp/"* ]] ; then
    eval `ssh-agent -s` > /dev/null 2>&1
    ssh-add ~/.ssh/YOUR_KEY_FILE > /dev/null 2>&1
fi

|| [[ $SSH_AUTH_SOCK == *"/private/tmp/"* ]]部分在macOS上是必需的,因为默认值为/private/tmp/com.apple.launchd.SOMETHINGHERE/Listeners。否则,@ Thomas Nyman综合答案将失败,因为$SSH_AUTH_SOCK它始终设置为某些内容。

然后.zlogout(或.bash_logout针对bash人士):

if [ -n "$SSH_AUTH_SOCK" ] ; then
    eval `/usr/bin/ssh-agent -k`
fi

在macOS Mojave 10.14.5上测试

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.