登录时启动ssh-agent


261

我有一个站点,它是使用SSH别名从Bitbucket.com提取的远程Git存储库。我可以在服务器上手动启动ssh-agent,但是每次通过SSH登录时都必须这样做。

我手动启动ssh-agent:

eval ssh-agent $SHELL

然后添加代理:

ssh-add ~/.ssh/bitbucket_id

然后在执行以下操作时显示:

ssh-add -l

而且我很好。有没有什么方法可以自动执行此过程,所以我不必每次登录都这样做?服务器正在运行RedHat 6.2(圣地亚哥)。


2
每次登录时,您想要执行的任何操作都应使用.profile(终端登录)或.xinitrc(对于GUI登录)。
Barmar

1
啊! 我正在使用.bash_profile ... .profile和.bash_profile有什么区别?
Pathsofdesign 2013年

1
不确定为什么首先要以这种方式运行命令。作为的子进程ssh-agent <command>运行,因此您要启动一个新的shell。我想你要。<command>ssh-agenteval ssh-agent
Barmar

9
.bash_profile特定于bash,.profile对所有POSIX shell通用。bash将首先查找.bash_profile,然后默认为.profile
Barmar

5
产生ssh-agent“标准”(兼容POSIX的)外壳的正确方法是eval $(ssh-agent -s)。另请注意,您必须确保注销时正确摆脱代理,因此也建议trap 'kill $SSH_AGENT_PID' EXIT.profile启动代理的那一行之后放置您的代理。
kostix 2013年

Answers:


368

请仔细阅读本文。您可能会发现这非常有用:

http://mah.everybody.org/docs/ssh

以防万一上述链接消失了一天,我正在捕获以下解决方案的主要部分:

约瑟夫·R·里格尔(Daniel Starin)提出的以下解决方案:

将此添加到您的 .bash_profile

SSH_ENV="$HOME/.ssh/agent-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

这个版本特别好,因为它将查看您是否已经启动了ssh-agent,并且如果找不到它,将启动它并存储设置,以便下次启动时可以使用它们。贝壳。


8
无需重启机器。您可以在当前的Shell会话中.bash_profile使用重新加载source ~/.bash_profile。机器重启也将起作用,因为无论如何都会加载新的配置。
石蕊

11
使用SSH_ENV="$HOME/.ssh/env"(即不使用/环境)为什么?sshd使用〜/ .ssh / environment(请参见手册页:PermitUserEnvironment)。Github在他们的解决方案中也推荐了它-help.github.com/articles/…–
Andrew Murphy

7
当我将其放入〜/ .bashrc文件(而不是〜/ .profile或〜/ .bash_profile)时,此脚本对我有用。首次打开本地控制台时,它会提示您输入密码,此后一切正常,无需进一步提示。干杯。
安德鲁·帕特

3
ssh-agent在.bashrc中添加启动命令将使scp命令不起作用。
Dzanvu 2015年

5
仍然令人烦恼...即使您不使用ssh,也必须在每次登录时都执行此操作。每次调用ssh时都需要消除这种情况。理想情况下,您应该能够配置哪些主机导致加载哪些密钥。
Erik Aronesty '16

97

在Arch Linux上,以下工作确实很棒(应该在所有基于systemd的发行版上都可以工作):

通过将以下内容放入来创建系统用户服务~/.config/systemd/user/ssh-agent.service

[Unit]
Description=SSH key agent

[Service]
Type=simple
Environment=SSH_AUTH_SOCK=%t/ssh-agent.socket
ExecStart=/usr/bin/ssh-agent -D -a $SSH_AUTH_SOCK

[Install]
WantedBy=default.target

设置外壳以为套接字(.bash_profile, .zshrc, ...)提供环境变量:

export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket"

启用该服务,因此它将在登录时自动启动,并启动它:

systemctl --user enable ssh-agent
systemctl --user start ssh-agent

将以下配置设置添加到本地ssh配置文件~/.ssh/config(自SSH 7.2起有效):

AddKeysToAgent  yes

这将指示ssh客户端始终将密钥添加到正在运行的代理中,因此无需事先ssh-add密钥。


3
我在Ubuntu中尝试执行此操作时发现了此评论。至少考虑到我对系统应如何工作的了解,与将内置内容入侵启动脚本相比,它在内置系统中的发挥似乎要好得多。
xiterion

我在Ubuntu 16.04 LTS上尝试过。不幸的是,每个外壳程序进程都需要其单独的ssh-agent进程。也许即使阅读了文档,我还是缺乏知识。
荒木大辅(Daisuke Aramaki)

您也可以使用Type = simplewiki.archlinux.org/index.php/...
汉斯-J。Schmid

2
那么,该解决方案是否基本上是安装/配置systemd服务(但仅针对用户)?
Trevor Boyd Smith,

不过,这不会设置SSH_AGENT_PID环境变量:(
MrMeszaros

73

旧问题,但我确实遇到过类似情况。不要以为上面的答案可以完全满足需要。缺少的是keychain; 如果尚未安装,请安装它。

sudo apt-get install keychain

然后将以下行添加到您的 ~/.bashrc

eval $(keychain --eval id_rsa)

ssh-agent如果没有运行,它将启动,如果没有运行,则将其连接,将ssh-agent环境变量加载到您的Shell中,然后加载ssh密钥。

更改id_rsa~/.ssh要加载的任何私钥。

参考

/unix/90853/how-can-i-run-ssh-add-automatically-without-password-prompt


根据给定的说明,钥匙串对我不起作用。我添加到.bash_profile,ssh仍然每次都要求输入密码。我在同一shell中尝试了多次。没有骰子。回到基本的ssh-agent方法
javadba

keychain --eval id_[yourid file]在.bashrc中添加eval
xelber 2014年

4
由于StackOverflow注释的格式,我花了20分钟研究解决方案。按照上述xelber的评论,正确的解决办法是eval `keychain --eval id_[yourid file]`.bashrc。评估环境变量到当前shell所需的反引号,以访问正在运行的ssh-agent。
詹姆斯

2
这是正确和简单的解决方案。如果不想在执行keychain命令时看到日志,则可-q以为安静模式添加选项。有关钥匙串的详细信息:funtoo.org/Keychain
Ananta

4
谢谢,这是迄今为止最优雅的解决方案。
greenspand

36

公认的解决方案具有以下缺点:

  • 维护很复杂;
  • 它评估可能导致错误或安全漏洞的存储文件;
  • 它启动代理但不停止它,这相当于将钥匙点火。

如果您的密钥不需要输入密码,建议采取以下解决方案。在您的.bash_profile 末尾添加以下内容(根据需要编辑密钥列表):

exec ssh-agent $BASH -s 10<&0 << EOF
    ssh-add ~/.ssh/your_key1.rsa \
            ~/.ssh/your_key2.rsa &> /dev/null
    exec $BASH <&10-
EOF

具有以下优点:

  • 更简单的解决方案;
  • 当bash会话结束时,agent会话结束。

它可能有缺点:

  • 交互式ssh-add命令将只影响一个会话,实际上这仅在非常不典型的情况下才是问题;
  • 如果需要输入密码,则无法使用;
  • 启动的Shell变为非登录状态(这不会影响AFAIK)。

请注意,多个ssh-agent进程并不是不利条件,因为它们不会占用更多的内存或CPU时间。


我已经使用Git Bash在Windows 10中$ HOME以外的目录中获得了SSH密钥。我需要要做的就是更改RSA的路径。TYVM!
kayleeFrye_onDeck

7
我会说“如果您的钥匙不需要输入密码”,就相当于让钥匙点火。
布鲁诺·布鲁诺斯基

至少它在您自己的主机上,而不是网络上的某个位置。
midenok '16

1
“我会说“如果您的钥匙不需要键入密码”,就相当于让钥匙处于点火状态。” <-说明如何处理??由于密钥比口令灵活得多,因此撤销起来也容易得多(这是什么?您仅将密钥用于具有完全访问权限的sudo用户?tsk tsk)。用户的多个配置文件的多组键。如果您想使任何事情自动化(例如部署或端到端检查),那么祝您好运在“编排”期间不断输入密码。
Scott Prive

2
不要那样做 无密码密钥是一种不良做法。
user48678

25

将此添加到您的中~/.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只要它保持运行状态,它就会一直重复使用。


如果我们有多个键而没有命名,我们将使用什么~/.ssh/id_rsa呢?似乎ssh-add您的答案部分期望键的默认文件名。
加布里埃尔·斯台普斯

对。我相信您可以根据需要将文件名添加到最后一行的末尾
Collin Anderson

但是您仍然不能自动执行脚本,例如在没有手动输入密码的情况下提取git的东西?如何避免呢?
trainoasis

7

所以我曾经使用上述方法,但是我更喜欢代理在我的上一个bash会话结束时死掉。这比其他解决方案要长一些,但这是我的首选方法。基本思想是,第一个bash会话启动ssh-agent。然后,每个其他bash会话都会检查配置文件(~/.ssh/.agent_env)。如果存在并且正在运行一个会话,则为环境提供源并创建一个到该套接字文件的硬链接/tmp(需要与原始套接字文件位于同一文件系统上)。当bash会话关闭时,每个会话都会删除其自己的硬链接。最后一个关闭的会话将发现硬链接具有2个链接(硬链接和原始链接),删除进程自己的套接字并终止进程将导致0,最后一个bash会话关闭后将留下干净的环境。

# Start ssh-agent to keep you logged in with keys, use `ssh-add` to log in
agent=`pgrep ssh-agent -u $USER` # get only your agents           
if [[ "$agent" == "" || ! -e ~/.ssh/.agent_env ]]; then
    # if no agents or environment file is missing create a new one
    # remove old agents / environment variable files
    kill $agent running
    rm ~/.ssh/.agent_env 

    # restart
    eval `ssh-agent` 
    echo 'export SSH_AUTH_SOCK'=$SSH_AUTH_SOCK >> ~/.ssh/.agent_env             
    echo 'export SSH_AGENT_PID'=$SSH_AGENT_PID >> ~/.ssh/.agent_env             
fi

# create our own hardlink to the socket (with random name)           
source ~/.ssh/.agent_env                                                    
MYSOCK=/tmp/ssh_agent.${RANDOM}.sock                                        
ln -T $SSH_AUTH_SOCK $MYSOCK                                                
export SSH_AUTH_SOCK=$MYSOCK                                                

end_agent()                                                                     
{
    # if we are the last holder of a hardlink, then kill the agent
    nhard=`ls -l $SSH_AUTH_SOCK | awk '{print $2}'`                             
    if [[ "$nhard" -eq 2 ]]; then                                               
        rm ~/.ssh/.agent_env                                                    
        ssh-agent -k                                                            
    fi                                                                          
    rm $SSH_AUTH_SOCK                                                           
}                                                                               
trap end_agent EXIT                                                             
set +x              

如果我们在登录其他任何外壳程序(BASH以外的外壳程序)时将其作为BASH脚本运行,它也应该起作用,对吗?
hoijui

7

为了添加另一个解决方案:P,我将@spheenik和@ collin-anderson的解决方案结合使用。

 # Ensure that we have an ssh config with AddKeysToAgent set to true
 if [ ! -f ~/.ssh/config ] || ! cat ~/.ssh/config | grep AddKeysToAgent | grep yes > /dev/null; then
     echo "AddKeysToAgent  yes" >> ~/.ssh/config
 fi
 # Ensure a ssh-agent is running so you only have to enter keys once
 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

可以稍微优雅一点,但它简单易读。此解决方案:

  • 确保AddKeysToAgent yes在您的ssh配置中,因此密钥将在使用时自动添加
  • 不会提示您在登录时输入任何密码短语(再次,第一次使用时会输入一次密码短语)
  • 如果尚未启动ssh代理,则以静默方式启动它

欢迎评论:)


1
这对我来说非常有效。在Kubuntu上,我将其放在.profile中。
Shai

1
很高兴知道该AddKeysToAgent yes设置。谢谢。
Collin Anderson

3

我通过将其添加到/ etc / profile- 系统范围(或用户本地.profile或_.bash_profile_)中来解决了这一问题:

# SSH-AGENT 
#!/usr/bin/env bash
SERVICE='ssh-agent'
WHOAMI=`who am i |awk '{print $1}'`

if pgrep -u $WHOAMI $SERVICE >/dev/null
then
    echo $SERVICE running.
else
    echo $SERVICE not running.
    echo starting
    ssh-agent > ~/.ssh/agent_env
fi
. ~/.ssh/agent_env

如果当前用户未运行,这将启动一个新的ssh-agent,或者如果正在运行,则重新设置ssh-agent env参数。


感谢您说出如何判断该代理是否已在运行!
Mike Maxwell,

if pgrep -u $WHOAMI $SERVICE >/dev/null工作如何?
Josh Desmond

3

该用户的鱼外壳可以使用该脚本做同样的事情。

# content has to be in .config/fish/config.fish
# if it does not exist, create the file
setenv SSH_ENV $HOME/.ssh/environment

function start_agent                                                                                                                                                                    
    echo "Initializing new SSH agent ..."
    ssh-agent -c | sed 's/^echo/#echo/' > $SSH_ENV
    echo "succeeded"
    chmod 600 $SSH_ENV 
    . $SSH_ENV > /dev/null
    ssh-add
end

function test_identities                                                                                                                                                                
    ssh-add -l | grep "The agent has no identities" > /dev/null
    if [ $status -eq 0 ]
        ssh-add
        if [ $status -eq 2 ]
            start_agent
        end
    end
end

if [ -n "$SSH_AGENT_PID" ] 
    ps -ef | grep $SSH_AGENT_PID | grep ssh-agent > /dev/null
    if [ $status -eq 0 ]
        test_identities
    end  
else
    if [ -f $SSH_ENV ]
        . $SSH_ENV > /dev/null
    end  
    ps -ef | grep $SSH_AGENT_PID | grep -v grep | grep ssh-agent > /dev/null
    if [ $status -eq 0 ]
        test_identities
    else 
        start_agent
    end  
end


1

尝试了许多来源的夫妇解决方案,但似乎都太麻烦了。终于我找到了最简单的一个:)

如果您还不熟悉zshoh-my-zsh,请安装它。你会喜欢的 :)

然后编辑 .zshrc

vim ~/.zshrc

查找plugins部分并对其进行更新以使其ssh-agent像这样使用:

plugins=(ssh-agent git)

就这样!你必须ssh-agent启动和运行每次启动您的shell


1

我非常喜欢你的答案。它使从cygwin / linux主机工作变得容易得多。我结合了开始和结束功能以使其安全。

SSH_ENV="$HOME/.ssh/.agent_env"

function start_agent {
    echo "Initialising new SSH agent..."

    eval `/usr/bin/ssh-agent`
    echo 'export SSH_AUTH_SOCK'=$SSH_AUTH_SOCK >> ${SSH_ENV}
    echo 'export SSH_AGENT_PID'=$SSH_AGENT_PID >> ${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

# create our own hardlink to the socket (with random name)
MYSOCK=/tmp/ssh_agent.${RANDOM}.sock
ln -T $SSH_AUTH_SOCK $MYSOCK
export SSH_AUTH_SOCK=$MYSOCK

end_agent()
{
    # if we are the last holder of a hardlink, then kill the agent
    nhard=`ls -l $SSH_AUTH_SOCK | awk '{print $2}'`
    if [[ "$nhard" -eq 2 ]]; then
        rm ${SSH_ENV}
        /usr/bin/ssh-agent -k
    fi
    rm $SSH_AUTH_SOCK
}
trap end_agent EXIT
set +x
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.