如何在Git执行shell命令时指定要使用的私有SSH密钥?

1761

也许有些不寻常,但我想在本地计算机执行shell(git)命令时指定要使用的私钥。

基本上像这样:

git clone git@github.com:TheUser/TheProject.git -key "/home/christoffer/ssh_keys/theuser"

甚至更好的方式(用Ruby):

with_key("/home/christoffer/ssh_keys/theuser") do
  sh("git clone git@github.com:TheUser/TheProject.git")
end

我看到使用 Net::SSH 连接远程服务器,并使用指定私钥的示例,但这是一个本地命令。是否可能?


10
请参考SuperUser上的这个问题:http://superuser.com/q/232373/90668。 - Flimm
74
为什么 Git 没有像 ssh 一样的“-i”选项,这让我感到不寻常。 - Nick T
56
从git 2.10版本(2016年第三季度)开始,你也可以使用新的配置项:git config core.sshCommand 'ssh -i private_key_file'。请参考我在下面的答案 - VonC
4
在我看来,HeyWatchThis的回答应该被接受为正确答案,因为它允许在设置之后像正常情况下一样执行所有git命令,而不像当前接受的答案需要为每个git命令创建一个子shell。 - gloriphobia
1
@VonC的core.sshCommand解决方案是唯一一个在重启后仍然有效的解决方案。 - Andrew Koster
显示剩余5条评论
40个回答

1642

这些解决方案对我都不起作用。

相反,我详细介绍一下@Martin v. Löwis提到的设置SSH的config文件。

SSH会查找用户的~/.ssh/config文件。我的设置如下:

Host gitserv
    Hostname remote.server.com
    IdentityFile ~/.ssh/id_rsa.github
    IdentitiesOnly yes # see NOTES below

然后我添加了一个远程 Git 仓库:

git remote add origin git@gitserv:myrepo.git

然后对我来说,git命令正常工作。

git push -v origin master

  • IdentitiesOnly yes是必需的,以防止SSH默认行为发送匹配每个协议的默认文件名的身份文件。如果您有一个名为~/.ssh/id_rsa的文件,则在没有此选项的情况下,它将首先尝试您的~/.ssh/id_rsa.github

参考


33
我发现当你在 .ssh/config 文件中指定多个密钥时,在 "Host" 行中使用的主机友好名称需要作为 "git remote add" 命令的一部分。如果行是 "Host stg",那么你需要使用 git remote add <someName> user@stg:/path_to_git_repo.git。如果你使用精确的服务器名称,如 user@myserver.com:/path_to_git_repo.git,则 Git 不会选择配置文件,因此它不会正确地选择私有密钥文件。我尝试将相同的内容推送到 GitHub 和 Heroku,并且只有在 "git remote add" 中给出友好名称时才能正常工作。 - Gopinath M.R
3
我对Github的主机不确定。我找到了这个链接:https://gist.github.com/jexchan/2351996. - Karsten
5
您可以使用 Host remote.server.com 并继续使用原始的URL。 - MauganRa
29
经过两次更改后,这对我起作用了。如果配置文件是新的,请不要忘记执行chmod 600 ~/.ssh/config(请参见此处)。如果您正在使用GitHub,请将Host gitserv替换为Host github.com,省略Hostname remote.server.com,并使用git remote add origin git@github.com:user_name/repo_name.git添加远程仓库。 - miguelmorin
1
这正是我想要的。完全忘记了 ~/.ssh/config。谢谢你,善良的陌生人。 - Ed Neville
显示剩余16条评论

1120

像这样的代码应该可以运行(由orip提出):

ssh-agent bash -c 'ssh-add /somewhere/yourkey; git clone git@github.com:user/project.git'

如果您喜欢使用子Shell,可以尝试以下方法(虽然它更容易出错):

ssh-agent $(ssh-add /somewhere/yourkey; git clone git@github.com:user/project.git)

Git会调用SSH,SSH会通过环境变量找到它的代理;这将反过来加载密钥。

另外,设置HOME也可以解决问题,前提是你愿意设置一个只包含.ssh目录的目录作为HOME;这个目录中可能包含一个identity.pub或者一个配置文件设置IdentityFile。


12
但这会将该密钥永久添加为已接受的SSH密钥,对吗?我想避免这种情况,这样用户2就不能干扰用户1的项目。由于这是一个Web应用程序,因此使用不同的操作系统用户并不实际,而这本来是最好的选择。 - Christoffer
43
不,当git完成后,ssh-agent就会终止,并且密钥会被遗忘。 - Martin v. Löwis
7
该命令无法在Windows Git Bash上工作。它会显示语法错误,附近的标记为“ssh-add”。 - Mohit
121
固定的命令行(适用于Windows或Linux)可能类似于:ssh-agent bash -c 'ssh-add sshkey; git clone url' - orip
8
“ssh-agent $(..)”语法对我不起作用,不确定这应该如何工作:(ba)sh应该首先执行$(..)中的命令,然后以输出作为参数运行ssh-agent。 - Johannes 'fish' Ziemke
显示剩余13条评论

876

从Git 2.3.0开始,我们还有一个简单的命令(不需要配置文件):

GIT_SSH_COMMAND='ssh -i private_key_file -o IdentitiesOnly=yes' git clone user@host:repo.git

请注意,-o IdentitiesOnly=yes 是必需的,以防止 SSH 默认行为将与每个协议匹配的默认文件名的身份验证文件发送,如上面的答案中所述。


3
尽管 ls -l /home/vagrant/.ssh/git 显示文件存在且权限为 444 Nov 16 18:12 /home/vagrant/.ssh/git,但我收到了 cannot run ssh -i /home/vagrant/.ssh/git: No such file or directory 的错误提示。 - ted
3
@ted: 将 /home/vagrant/.ssh/git 的权限设置为 400。 - Yash
10
简单易行的解决方案。我建议如果你需要经常这样做,可以创建一个别名(alias)。 - Lasse Meyer
10
请不要忘记运行chmod 400 <path-to-private-key-file>命令,否则Git命令可能会失败而没有特殊的错误信息。 - eonil
9
如果这个答案也包括 -o IdentitiesOnly=yes,就会很好,这样可以确保使用 -i 指定的密钥(而不是来自 SSH 代理的密钥)。 - robinst
显示剩余11条评论

530

别人对于 ~/.ssh/config 的建议都太过复杂。其实可以很简单,比如:

Host github.com
  IdentityFile ~/.ssh/github_rsa

38
你还需要加上 IdentitiesOnly 选项。 - Flimm
7
你可以通过操作远程仓库来拥有多个 GitHub 账户:git remote add ssh://personal/org/proj.git && git remote add ssh://corporate/org/proj.git。然后你的配置看起来像这样:Host personal HostName github.com ... Host corporate HostName github.com - emory
10
我的程序可以不使用IdentitiesOnly选项正常工作,有人可以解释一下为什么需要使用这个选项吗? - AlbinoDrought
2
不太匹配的粒度。我的公司在Github上有一个组织,而我个人也有一个Github账户,所以仅凭主机名无法很好地工作。 - anydoby
1
“Host *.github.com”和“Host github.com”之间有什么区别吗? - theonlygusti
显示剩余5条评论

376
从git 2.10+(Q3 2016:于2016年9月2日发布)开始,您可以为GIT_SSH_COMMAND设置一个config(而不仅仅是一个环境变量,如Rober Jack Willanswer中所描述的)。
查看提交3c8ede3(2016年6月26日)由Nguyễn Thái Ngọc Duy(pclouds完成。
(由Junio C Hamano -- gitster --提交dc21164中合并,2016年7月19日)

A new configuration variable core.sshCommand has been added to specify what value for GIT_SSH_COMMAND to use per repository.

core.sshCommand:

If this variable is set, git fetch and git push will use the specified command instead of ssh when they need to connect to a remote system.
The command is in the same form as the GIT_SSH_COMMAND environment variable and is overridden when the environment variable is set.

这意味着git pull可以是:
cd /path/to/my/repo/already/cloned
git config core.sshCommand 'ssh -i private_key_file' 
# later on
git pull

当克隆一个新的仓库时,如果还没有任何`.git/config`文件需要修改,可以先为一个命令设置它,比如`git clone`或者`git submodule add`。
git -c core.sshCommand="ssh -i private_key_file" clone host:repo.git

一旦存储库存在,您可以在.git/config中永久设置该选项。
cd <repo or submodule you just cloned>
git config core.sshCommand "ssh -i private_key_file"

这比设置一个GIT_SSH_COMMAND环境变量要容易,根据notedMátyás Kuti-Kreszács所述,在Windows上。
set "GIT_SSH_COMMAND=ssh -i private_key_file"

对于所有这些命令,你可以添加一个 -o IdentitiesOnly=yes 来限制 SSH 只使用你指定的私钥/公钥:
git config core.sshCommand 'ssh -i private_key_file -o IdentitiesOnly=yes' 
# or
git -c core.sshCommand="ssh -i private_key_file -o IdentitiesOnly=yes" clone host:repo.git
# or
set "GIT_SSH_COMMAND=ssh -i private_key_file -o IdentitiesOnly=yes"

gsullins建议在.zshrc中添加以下别名(在评论中提到)
alias git.key1="git config core.sshCommand 'ssh -i <absolute path to private key>'"

正如Jaredo Mills一条评论中指出的:

一个陷阱:如果您将您的仓库镜像到多个主机(Github、Gitlab),并使用不同的私钥,那么这种方法将发送错误的密钥。
密钥与仓库无关,而与用户名和主机名相关。 ~/.ssh/config 是正确的地方,可以正确地关联它,并且它具有适合此工作的正确匹配能力(请参阅man ssh_config)。

请参阅HeyWatchThis答案以进行说明。

15
工作。人们应该认为这是最好的答案。一旦发行,通过将版本复制到/tmp之前与.git/config文件进行比较,可以提供信息。已创建一个新条目:sshCommand = ... 说句实话,我使用了“git config core.sshCommand“ ssh -i $HOME/.ssh/privatekeyfile”。 - WeakPointer
10
你可以使用内联命令git -c core.sshCommand="ssh -i 私钥文件" clone 主机名:代码库.git,然后配置设置 git config core.sshCommand 'ssh -i 私钥文件' - dav_i
6
这绝对是最佳答案! - Wagner Patriota
3
很棒。自动为每个仓库设置密钥。 - daviewales
4
这应该是最佳答案。 - Kondziutek
显示剩余14条评论

164

我的 my_git_ssh_wrapper 内容:

#!/bin/bash

ssh -i /path/to/ssh/secret/key $1 $2

然后你可以通过以下方式使用这个键:

GIT_SSH=my_git_ssh_wrapper git clone git@github.com:TheUser/TheProject.git

5
如果您在同一域名下有多个账户,这是一个非常好的解决方案,其他解决方案无法很好地处理此类情况。 - Beka
1
不错的解决方案。您还可以使用以下命令简化此过程:> GIT_SSH=my_git_ssh_wrapper; git clone git@github.com:TheUser/TheProject.git - Shiva
2
这个解决方案还适用于在没有主目录的帐户中使用git的情况。 - piotrekkr
1
这是在cygwin环境下对我有效的唯一方法。 - ChatterOne
1
不要忘记为脚本添加可执行权限,以避免出现“权限被拒绝”的错误。 - Panoptik
显示剩余3条评论

157

综合答案和评论,最好的方式是设置git使用不同的密钥文件,然后忘记它,并支持相同主机的不同用户(例如个人GitHub账户和工作账户),同时也适用于Windows,方法是编辑~/.ssh/config(或c:\Users\<your user>\.ssh\config)并指定多个身份:

Host github.com
HostName github.com
IdentityFile /path/to/your/personal/github/private/key
User dandv

Host github-work
HostName github.com
IdentityFile /path/to/your/work/github/private/key
User workuser

那么,如果要将项目克隆为您的个人用户,请运行常规的git clone命令。

要将repo克隆为workuser,请运行git clone git@github-work:company/project.git


4
我给你点踩是因为你说的每件事情都已经在之前的答案中涵盖了,而且在我看来,之前的答案表述得更加清晰。例如,你为什么要分别将用户定义为"dandv"和"workuser"? - hroptatyr
3
你回答了一个4年前的问题,却没有提供新的信息,并声称你的答案是“最好的方法”。此外,你还给其他用户的答案投了反对票并骚扰他们删除自己的答案,只是为了让你的答案更受关注。 - rudimeier
1
你的想法是正确的,但这不会起作用。你必须使用用户“git”。问题是,你正在复制thamster在2012年发表的回复。 - jthill
14
我认为这是一个比@thamster更好的答案,仅因为它解释了主机别名。 - David Moles
3
我喜欢这个答案。但是,对我来说只有在我的ssh配置文件中添加IdentitiesOnly yes才有效。 - winni2k
显示剩余2条评论

107

问题出在当你有位于同一主机上的不同远程代码库(比如github.com),且你想使用不同的ssh密钥与它们进行交互(例如,不同的GitHub帐户)时。

为了实现这一点:

  1. First you should declare your different keys in ~/.ssh/config file.

    # Key for usual repositories on github.com
    Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_rsa
    
    # Key for a particular repository on github.com
    Host XXX
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_other_rsa
    

    By doing this you associate the second key with a new friendly name "XXX" for github.com.

  2. Then you must change the remote origin of your particular repository, so that it uses the friendly name you've just defined.

    Go to your local repository folder within a command prompt, and display the current remote origin:

    >git remote -v
    origin  git@github.com:myuser/myrepo.git (fetch)
    origin  git@github.com:myuser/myrepo.git (push)
    

    Then change origin with:

    >git remote set-url origin git@XXX:myuser/myrepo.git
    >git remote -v
    origin  git@XXX:myuser/myrepo.git (fetch)
    origin  git@XXX:myuser/myrepo.git (push)
    

    Now you can push, fetch... with the right key automatically.


3
这是我心目中最“正确”的答案,你可以在ssh配置文件中组织连接和密钥,这是最佳实践并且长期可支持的。 - james-see
其他解决方案似乎都是权宜之计,而这个方案则是利用工具支持的强大功能。 - Craig.C
1
这听起来正是我正在寻找的,但我无法让它工作。每当我运行git命令时,我会得到以下错误:ssh: Could not resolve hostname helloworld-wp-github: Name or service not known fatal: Could not read from remote repository. - maskedjellybean
1
添加 User git 是我所遗漏的部分。您可以使用 ssh -vT XXX 进行连接测试(https://docs.github.com/en/authentication/troubleshooting-ssh/error-permission-denied-publickey)。 - arsenik
尽管这个评论非常清晰,但我花了很长时间才弄明白,但最终我成功了!关键是Host别名。我现在进行评论,以便下次...大概是一周后能够找到它。 - Soft Bullets
显示剩余4条评论

99

1
“-F /dev/null” 是什么意思?据我所知,这将更改 configFile 的默认值“~/.ssh/config”,但为什么要这样做呢?是为了确保沙盒命令吗? - Dominic
4
http://linuxcommand.org/man_pages/ssh1.html中没有指定配置文件,因此当git运行ssh时,不会传递配置文件(实际上这是一种沙盒模式,只需忽略用户配置的默认选项)。原始线程在superuser中有关于-F的更多信息。 - David
1
我正在寻找的那个。谢谢! - lostcitizen
1
你好,你知道如何将这个传播到子模块吗? - Arka Prava Basu
我该如何将它作为别名放入bash_profile中?总是出现错误。 - z.a.
显示剩余3条评论

65

最快最简单的方法是:

使用ssh克隆你的仓库:

git -c core.sshCommand="ssh -i ~/.ssh/<your_key>" clone git@github.com:<user>/<repo>.git

然后 cd 到克隆的仓库中:

git config core.sshCommand 'ssh -i ~/.ssh/<your_key>'

为了测试它是否工作:

git --git-dir=/path/to/repo/.git pull

所以你可能会想:我创建的ssh密钥为什么在我将.pub放入GitHub并且私有密钥在默认目录之后没有起作用呢

文档(链接)给出了一个命令来解释这个问题:

ssh -vT git@github.com

输出显示了一系列SSH密钥名称Git正在查找。因此,您可以使用其中一个名称创建您的密钥,或者使用上述过程包含您需要的密钥。


此外,您应该使用gitconfig的另一个功能includeif来进行配置。 - airtonix
你可以用以下命令替换这两个命令:git clone -c "core.sshCommand=ssh -i ~/.ssh/<your_key>" git@github.com:<user>/<repo>.git。请注意,-c选项在clone之后而不是之前。 - Flimm
-i仅仅是一个建议,没有进一步的标志。为了避免这种异端邪说,将其扩展为ssh -o IdentitiesOnly=yes -o IdentityFile=~/.ssh/<your_key> --identity_file=~/.ssh/<your_key> - airtonix

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