选择用于Git的私钥

142

我有两个Git服务器,需要使用两个不同的SSH密钥。

git clone user1@server1:blahblahblah 使用 ~/.ssh/id_rsa,但是我需要根据要连接的服务器指定使用哪个密钥。

哪个Git命令行参数可以完成这个工作?(我正在运行Linux。)


2
这个问题已经在http://superuser.com/questions/232373/tell-git-which-private-key-to-use得到了解答。 - Daira Hopwood
可能是Specify an SSH key for git push without using ~/.ssh/config的重复问题。 - Ciro Santilli OurBigBook.com
9个回答

219

还有一种可能性,那就是设置core.sshCommand,例如:

git config --local core.sshCommand "/usr/bin/ssh -i /home/me/.ssh/id_rsa_foo"

当你在Github上有多个账户时,这种策略特别有用:因为所有账户都使用git@github.com作为ssh连接Github,并且它使用ssh密钥来确定你是哪个Github用户。 在这种情况下,无论是.ssh/config还是ssh-agent都不能实现你想要的功能。

更新 — 在执行上述操作之前,您需要拥有一个本地存储库,因此,如果您正在尝试克隆远程存储库,则需要按照 drewbie18 的回答手动指定密钥:

git clone -c core.sshCommand="/usr/bin/ssh -i /home/me/.ssh/id_rsa_foo" git@github.com:me/repo.git

当你克隆了该存储库后,你可以使用git config命令来永久设置此项。


22
这应该是被选中的答案。所需的配置已经很好地本地化在仓库中,需要使用不同的ssh密钥来访问不同的git服务器。无需修改~/.ssh/config(即使在GitHub上有多个账户的情况下也无需修改/etc/hosts)。 - mr.b
1
然而,有一个问题:在配置之前无法克隆存储库,而为了进行配置,您需要先将其克隆。或者,您可以使用“git init”初始化存储库,在本地配置以使用正确的ssh命令,然后添加远程(与“git clone”相比,需要3个步骤而不是1个步骤)。 - mr.b
3
要克隆代码库,我会采用下面@Guss提供的解决方案,通过设置GIT_SSH_COMMAND环境变量来实现。但在正常使用中,我更喜欢自己的解决方案,因为它避免了每次启动新的shell时都需要重新导出该变量的麻烦。 - Richard Smith
3
感谢您提供这个优雅的解决方案。但我想澄清的是,core.sshCommand 仅适用于 git >= 2.10.0 - slonepi
1
@akostadinov - 你为什么想要禁用严格的主机密钥检查?这只会在你第一次通过ssh连接到该服务器时询问你是否添加主机密钥,通常这将是你从该服务器克隆存储库的第一次。 - Richard Smith
显示剩余7条评论

91

如果您是通过SSH连接的话,密钥将受到SSH参数的控制,而不是Git参数。

SSH在 ~/.ssh/config 文件中查找配置参数。编辑该文件并添加IdentityFile条目来为这两个Git服务器进行配置,就像这样:

Host server1.whatever.com
  IdentityFile /path/to/key_1
Host server2.whatever.com
  IdentityFile /path/to/key_2

这篇文章有更多细节。


21
那并没有回答问题。问题是,当SSH处理授权时,我该如何向Git传递这个参数? - keks
1
@Keks:你不能通过git命令行传递ssh参数。这个答案提供了一个解决方法。还有其他的解决方法,例如https://git.wiki.kernel.org/index.php/GitTips#How_to_pass_ssh_options_in_git.3F。请注意,该方法也不使用命令行参数来修改ssh行为。 - Cameron Skinner
13
确实如此,但是你的原始回答并没有回答问题。你也可以使用ssh-agent并使用ssh-add,然后只需使用git。当git通过ssh连接时,密钥已经启用。你看,有几种方法,而你的原始答案并没有真正帮助到。 - keks
2
这基本上是一个仅包含链接的答案。您能否在您的回答中总结文章的相关部分? - Flimm
2
此回答已过时。 - nurettin
显示剩余2条评论

47

通常情况下,您可以使用~/.ssh/config来实现此功能。只需按照以下方式将服务器地址与要用于它们的密钥配对:

Host github.com
  IdentityFile ~/.ssh/id_rsa.github
Host heroku.com
  IdentityFile ~/.ssh/id_rsa.heroku
Host *
  IdentityFile ~/.ssh/id_rsa

Host * 表示任何服务器,因此我将其用于将 ~/.ssh/id_rsa 设置为默认要使用的密钥。


16
如果你在一个类似GitHub的服务器上有多个账号,并且想要为每个账号使用不同的私钥,那么这个想法行不通。 - Flimm
2
有关在一个服务器上使用多个 GitHub 帐户,请参见此答案 https://dev59.com/cHA75IYBdhLWcg3wkJ71 - lexicalscope

26

[更新]: 可以查看我的另一个答案,其中介绍了基于配置的方法和一个现成的脚本供您使用。

在我的情况下,类似于@Richard Smith的情况(顺便说一下,他的解决方案对我没用),我需要在不同的代码库下为同一个服务器使用不同的密钥。

对我来说,解决方法是正确地使用环境变量GIT_SSH_COMMAND设置会话,如下:

export GIT_SSH_COMMAND="ssh -o IdentitiesOnly=yes -i ~/.ssh/my-secret-identitiy"

更新:

这里需要注意的另一件事情是正确设置环境变量可能会很麻烦,因此我使用像Liquid PromptFish Shell这样的命令提示符修改工具来连接到Shell并根据当前目录和某些规则持续更新环境变量。例如,我所有需要与Gitlab配合使用我的个人SSH密钥的个人项目都在~/Documents/Projects/personal下,所以当shell hook运行pwd并发现当前目录在该路径下时,它会自动设置所需的GIT_SSH_COMMAND变量。


较旧的git客户端(例如我的1.7.1版本)将此环境变量称为GIT_SSH。 - Attila Csipak

18
你可以使用--global--local配置,全局将更新~/.gitconfig文件,本地将更新存储库.git/config中的配置,并覆盖全局(~/.gitconfig)配置。
git config --local --add core.sshCommand 'ssh -i ~/.ssh/my_key'

4
非常顺利,这正是我所需要的——一个特定代码库的解决方案。 - Quaestor Lucem

17

使用 ssh-add path-to-private-key 命令可直接运行,无需进一步设置。


11
如果您有两个部署密钥而且它们只能用于特定的存储库,那么就可能会使用错误的密钥并显示您没有访问权限。 - cdmckay
1
我遇到了cdmckay提到的问题。 - Suge

14

我是Windows用户,我遇到了这个问题,并且有一个与我在这里看到的解决方案略有不同的解决方法。我面临的问题是,我只想使用特定的私钥克隆存储库,而不必全局配置我的git配置文件或添加特定的git bash设置,因为我在PowerShell中工作。基本上,我只想让一些私有密钥坐在我的.ssh文件夹里,根据需要在特定的仓库中引用它们。

以下命令可用于此:

git clone -c core.sshCommand="ssh -i ~/.ssh/<PRIVATE KEY NAME>" <CLONE URL>

本质上,它在初始化git仓库时设置core.sshCommand选项,然后再运行克隆。因此,你希望为这个仓库使用的特定ssh密钥仅针对此仓库进行设置。这可能并不是所有情况下的理想解决方案,但对于我想要的内容来说是可以的。


谢谢。我不知道 git clone 命令中的 -c 选项。这比我在克隆时建议设置环境变量更好。 - Richard Smith
这确实应该是被接受的答案。它回答了实际提出的问题(也是我想要的答案...)。另外,它在Linux上也可以正常工作。 - Hugh Perkins
(请注意,对于其他命令,您可以在命令之前放置“-c”,例如对于“git push”,请执行“git push -c ...等...推送”) - Hugh Perkins

5
另一种选择是编写一个小脚本让 core.sshCommand 变得更加智能 - 检查当前工作目录是否配置了特定的 SSH 密钥,如果有,则使用它;否则,依赖标准 SSH 密钥解析。
以下是第一个版本:
#!/bin/bash
key="$(git config ssh.key)"
if [ -n "$key" ]; then
        ssh -o IdentitiesOnly=yes -i "$key" "$@"
else
        ssh "$@"
fi

然后将其设置为全局git SSH命令:

chmod 755 ~/.local/bin/git-ssh-command
git config --global core.sshCommand ~/.local/bin/git-ssh-command

(~/.local/bin是在SystemD操作系统上“放置用户脚本”的当前标准

设置好后,您可以通过设置配置选项ssh.key来配置任何存储库使用特定的SSH密钥:

git config --local ssh.key ~/.ssh/my-non-default-private-key

额外的可选技巧

  • 将全局ssh.key设置为“默认回退到非默认SSH密钥”或其他内容。
  • 由于git在存储库的根目录中执行core.sshCommand,因此您的自定义git-ssh-command可以查看该目录并具有一些关于目录名称的启发式方法。这可以在else部分中完成,因此仅当没有在ssh.key中指定特定密钥时启动启发式方法。
  • 您可以添加git remote -v检查来基于远程添加启发式方法,例如Eike的脚本
  • 如果您想查看存储库远程,但有多个需要不同密钥的远程,则可以在脚本开头添加remote="$1:$(sed "s,.* ,,;s,',,g"<<<"$2")"来解决正在操作的远程 - 并针对此进行检查($remote将类似于git remote -v输出的中间列)。

更新:

以下是自定义SSH命令的版本,它查看了远程URL并执行了一些启发式方法 - 在我的情况下,我具有相同公共git托管服务的多个不同身份(用于工作、个人等),我可以通过查看远程URL(检查Gitlab组或Github组织等)选择正确的身份:

#!/bin/bash
while ! [[ "$1" =~ "git@" ]]; do shift; done # git may send ssh options
if [[ "$2" =~ ^git-lfs-authenticate.* ]]; then # reconstruct url for git-lfs
        remote="$1:$(awk '{print$2}'<<<"$2")"
else # reconstruct url for standard git commands
        remote="$1:$(sed "s,.* ,,;s,',,g"<<<"$2")"
fi
echo "Detected $remote from $@" >&2
key="$(git config ssh.key)"
if [ -n "$key" ]; then # use specified SSH key, if set
        ssh -o IdentitiesOnly=yes -i "$key" "$@"
elif [[ "$remote" == git@gitlab.com:my-company* ]]; then # use company id
        ssh -o IdentitiesOnly=yes -i ~/.ssh/company-id "$@"
elif [[ "$remote" =~ git@bitbucket.org:.*other-org.* ]]; then # bitbucket has weird urls
        ssh -o IdentitiesOnly=yes -i ~/.ssh/custom-org-key "$@"
else # otherwise use whatever the agent has (my personal key)
        ssh "$@"
fi

1
其他答案启发了我写一个小脚本,根据命令行选项或(如果存在)git remote -v的值来选择ssh密钥。 希望对你有所帮助! 实际上回答问题:使用gat。 另请参见https://bitbucket.org/eikerobert/gat/src/master/
#!/bin/bash

usegat=false

for VAR in "$@"
do
    if [ "$VAR" != "${VAR/git@bitbucket.org:myaccount/}" ]; then
        usegat=true
    fi
done

if [ $usegat=false ]; then
   /usr/bin/git rev-parse --is-inside-work-tree >/dev/null 2>&1
   isinsidegitrepo=$?
    #echo $isinsidegitrepo
   if [ $isinsidegitrepo = 0 ]; then
       remote=`/usr/bin/git remote -v`
       if [ "$remote" != "${remote/git@bitbucket.org:myaccount/}" ]; then
           usegat=true
       fi
   fi
fi


if [ $usegat = true ]; then
    # echo "TRUE"
    /usr/bin/git -c core.sshCommand="/usr/bin/ssh -i /home/myaccount/.ssh/mykey" "$@"
else
     #echo "FALSE"
    /usr/bin/git "$@"
fi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接