如何为 Git 指定 SSH 密钥文件

如果你只希望对单个 git server 配置独立的 ssh key,可以在 ~/.ssh/config 里面加上如下配置即可:

1
2
3
4
5
6
Host github.com
HostName github.com
Port 22
User git
IdentityFile ${HOME}/.ssh/id_rsa.github
IdentitiesOnly yes

以下内容为转载:


如果你在运行 ssh 命令时想指定一个密钥文件,一种很简便的方法就是使用 -i 选项。

ssh -i ~/.ssh/thatuserkey.pem thatuser@myserver.com

这样非常干净利索。既简单、优雅,又十分直观。对 git 命令,我也想这么做:

git -i ~/.ssh/thatuserkey.pem clone thatuser@myserver.com:/git/repo.git

然而,git 命令并没有这样的 -i 选项。真糟糕!

我找了很久都没找到类似的解决办法。我只能想到如下两种备选方案:

  • 使用 GIT_SSH 环境变量
  • 使用包裹脚本(wrapper script)

使用 GIT_SSH 环境变量

此方案中,你可以这样来为 git 命令指定一个密钥文件:

PKEY=~/.ssh/thatuserkey.pem git clone thatuser@myserver.com:/git/repo.git

其中,~/.ssh/thatuserkey.pem 就是你想使用的密钥文件。

为了能让这正常运行,还需要进行一些预配置。首先,要创建包含如下内容的脚本 ~/ssh-git.sh:

1
2
3
4
5
6
7
8
#!/bin/sh

if [ -z "$PKEY" ]; then
# if PKEY is not specified, run ssh using default keyfile
ssh "$@"
else
ssh -i "$PKEY" "$@"
fi

这个脚本必须是可执行的,所以需要对它执行 chmod +x 。

接下来,需要把 GIT_SSH 的值设置为指向上述脚本的路径。并且这个变量需要导出到 shell 环境。

export GIT_SSH=~/ssh-git.sh

现在,你每次运行 git 命令时,用 PKEY 指定的密钥文件就会传递给用 GIT_SSH 指定的脚本。这就会让 git 命令使用该密钥文件进行连接。

PKEY=~/.ssh/thatuserkey.pem git clone thatuser@myserver.com:/git/repo.git

从此,每次运行 git 命令时,只需设置 PKEY 变量,你就可以随意使用你想要的密钥文件。 [1]

如果你在运行 git 命令时未设置 PKEY , GIT_SSH 脚本仍然会被执行,因为它已经被导出到 shell 环境中了。但脚本会自动判断,如果没有设置 PKEY 就不会使用 -i 选项,这样 git 仍然会使用默认密钥文件运行。

在把 PKEY 导出到 shell 环境中时需要谨慎一些,因为无论它被设置成什么值,GIT_SSH 脚本都会使用它,即使你并没有在 git 命令里指定它。把 GIT_SSH 导出到 shell 环境中会带来另一个问题,就是 git 在运行时总会使用它。因此你需要时刻提醒自己你设置了它。你可以在每次运行 git 命令时设置 GIT_SSH 以防止它被导出到 shell 环境,代价就是整个命令将会更长。

设置 PKEY 的用法虽然可行,但在 git 命令中设置 PKEY 总觉得不那么常规。[2]

如果你也这么认为,那这里还有另一个备选项。

使用包裹脚本

git 命令的 -i 选项非常工整且优雅。你可以用 -i 选项来提供你想要使用的密钥文件。如果你没用这个选项,ssh 就会转而用默认的密钥文件。

如果要对 git 命令也是用 -i 选项,你就需要写个包裹脚本。包裹脚本可以让我们以我们想要的方式来设置用法,也就是模拟 ssh 的 -i 选项。

用法应该差不多像这样:

git.sh -i ~/.ssh/thatuserkey.pem clone thatuser@myserver.com:/git/repo.git

其中的 git.sh 就是我们的包裹脚本。

你只需要创建这个脚本,然后把它放到你的 PATH 路径下就搞定了。

你可以直接在这里下载或直接拷贝下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash

# The MIT License (MIT)
# Copyright (c) 2013 Alvin Abad

if [ $# -eq 0 ]; then
echo "Git wrapper script that can specify an ssh-key file
Usage:
git.sh -i ssh-key-file git-command
"
exit 1
fi

# remove temporary file on exit
trap 'rm -f /tmp/.git_ssh.$$' 0

if [ "$1" = "-i" ]; then
SSH_KEY=$2; shift; shift
echo "ssh -i $SSH_KEY \$@" > /tmp/.git_ssh.$$
chmod +x /tmp/.git_ssh.$$
export GIT_SSH=/tmp/.git_ssh.$$
fi

# in case the git command is repeated
[ "$1" = "git" ] && shift

# Run the git command
git "$@"

这个脚本会优雅的处理失败的情况。如果你没指定 -i 选项,它会使用默认密钥文件来运行 git 。

这个包裹脚本使用了与 GIT_SSH 相同的原理。但与手动预配置不同的是,这个包裹脚本会在每次运行真正的 git 命令之前,临时把所有这些设置好。

其他备选项

还有其他方法可以让 git 命令使用不同的密钥文件。那就是使用 $HOME/.ssh/config 文件来为你想要连接的不同的主机映射不同的密钥文件(译者注:请参考这里)。但这种方法不能让你在运行 git 命令时任意的选择密钥文件。那些密钥文件必须得提前在配置文件中定义好才行。

你还可以用 ssh-agent 来把你想用的密钥文件加进去。我还写了一个基于 ssh-agent 的支持 -i 选项的包裹脚本。事实证明,它比 GIT_SSH 的方式还复杂。我可能还会写篇博客来展示一下那种方式是怎么搞的。

在所有这些不同的方法中,并没有哪种更好。这完全要看你使用的环境和你的个人喜好。



  1. 1.在我的工作流中,我比较喜欢这种控制。因为对于不同的服务器我会使用不同的密钥文件。对工作上的服务器我用一个不同的密钥,对我自己的服务器以及一些公开网站(比如 GitHub)我也用不同的密钥。就好比密码一样,你 Facebook 上的密码肯定和你银行账户的密码不一样。
  2. 2.就我个人而言,在我习惯了这种用法之后,我觉得这样也还算不错。我运行着很多脚本和命令需要设置环境变量。我不喜欢这种把它们都导出到 shell 环境中到处传播的方式。所以,我都是在命令行中指定它们。