有些时候,我们需要在一台服务器里拉取github的项目,由于安全考虑,github的规定一个deploy key只能用在一个项目里面。 所以多个项目就需要我们用多个ssh key

而我们如果只在一台服务器,除了默认生成的id_rsa,则需要为每个项目加入生成一个独立的ssh key,这样就存在多个秘钥和公钥对,那么我们执行git pull或者git push这样的命令的时候,ssh如何找到正确的key呢?

这个时候需要用到ssh的config以及ssh-agent来解决这个问题,下面我们一步步来说明这个过程。

生成新的密钥对

1
2
ssh-keygen -f ~/.ssh/project1_rsa
ssh-keygen -f ~/.ssh/project2_rsa

上面这个命令,一路回车的话,当然也可以输入密码,如果使用密码,那么在使用的时候则需要输入这个密码(通过ssh-agent这个代理程序可以帮你记住密码,则可以不用每次都输入),这个会在用户的目录下生成2个密码对,生成的内容如下:

1
2
3
4
5
> ls ~/.ssh
project1_rsa # project1 的私钥
project1_rsa.pub # project2 的公钥
project2_rsa
project2_rsa.pub

然后,接下来,为了让ssh知道我们生成的密钥对里面的私钥,需要用ssh-add命令加入

1
ssh-add ~/.ssh/project1_rsa

如果报下面这个错误:

1
Could not open a connection to your authentication agent.

则说明ssh-agent代理程序没有启动,那么再返回来启动这个代理程序,然后再加入

1
2
3
eval `ssh-agent -s` # 启动代理
ssh-add ~/.ssh/project1_rsa
ssh-add ~/.ssh/project2_rsa

这里有个问题,ssh-agent不一定是默认启动的,这个agent在下一个终端会话进来后可能用不了,这个问题我们后面在说。

接下来,为了完成让git pull的时候,自动认得目录,接下来,我们要做两个配置

1. 创建~/.ssh/config

1
touch ~/.ssh/config

把下面内容贴进去

1
2
3
4
5
6
7
8
Host project1
HostName github.com
User git
IdentityFile /home/ubuntu/.ssh/project1_rsa
Host project2
HostName github.com
User git
IdentityFile /home/ubuntu/.ssh/project2_rsa

这个配置的意思是,当ssh碰到要去连接的Host为project1的时候,会把HostName 设置为 github.com,并且使用git用户,私钥使用的是IdentityFile /home/ubuntu/.ssh/project1_rsa

2. 修改git remote

进入你的github项目

1
git remote set-url origin git@project1:<your github name>/project1.git
  • your github name: 是你在github注册的名字

由于这里使用的git ssh协议,那么在你执行git pull这些命令的时候,它会去寻找命令~/.ssh/config里面的配置,然后找到根据git@project1来找到Host project1的配置,然后机会ssh会使用这些配置来执行命令。

ssh-agent自动启动

ssh-agent进程一般来说会自动启动,并会自动加载~/.ssh/id_rsa,它启动的时候,创建一个继承SSH_AUTH_SOCKSSH_AGENT_PID环境变量的进程,那么如果你开的会话并没有这些环境变量,那么你的会话无法正确的链接到ssh-agent,即使你用ps -aux | grep ssh-agent可以看到有这个进程,甚至可能有多个ssh-agent(因为你在多个会话里面执行过多次”eval ssh-agent -s“)也无济于事。

如果没有这些环境变量,你执行ssh-add命令的时候,它连不到已经启动的ssh-agent,所以会报Could not open a connection to your authentication agent.这样的错误。

所以我们确保我们启动会话的时候启动ssh-agent。为了实现这个你可以在~/.bashrc或者~/.bash_profile或者~/.profile这些会话启动的时候会加载shell里面加入启动命令,实现的方法有多种,比如你可以直接加入evalssh-agent -s``. 那么而每次启动,然后同时加入trap 'kill $SSH_AGENT_PID' EXIT,让会话退出的时候kill掉这个启动的经常。 然而这样做,每当你其他一个shell就会创建一个ssh-agent的进程。

为了解决这个问题,可以用封装成下面的命令,加入到~/.bashrc(~/.bash_profile或者~/.profile)

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
29

SSH_ENV="$HOME/.ssh/agent-environment"

function addAdditionSSHKey {
/usr/bin/ssh-add $HOME/.ssh/project1_rsa;
/usr/bin/ssh-add $HOME/.ssh/project2_rsa;
}

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; # add default id
addAdditionSSHKey; # add addition ids
}

# 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

其中addAdditionSSHKey可以配置你要加入的私钥,每次你加入新的私钥的后,都可以直接在新的会话窗口直接执行addAdditionSSHKey帮助你加入新的key

这个段shell会先判断~/.ssh目录下面存储的agent-environment环境变量信息是否存在,如果存在,说明之前已经启动了一个ssh-agent进程,只需要把它的变量export到当前的会话即可,如果没有,则启动一个新的ssh-agent,并把相关环境变量保存下来

这个agent-environment环境变量存储的信息大约如下,

1
2
3
SSH_AUTH_SOCK=/tmp/ssh-qyakUB3GkFmc/agent.9914; export SSH_AUTH_SOCK;
SSH_AGENT_PID=9916; export SSH_AGENT_PID;
#echo Agent pid 9916;

. "${SSH_ENV}" > /dev/null则个语句就是执行命令,把已经保存的环境变量export到当前的会话。

reference: