多个SSH密钥并存且连接到Github
在这篇文章中,使用Git服务器Github,来进行多SSH密钥的配置,介绍多个SSH密钥同时存在于同一台设备中的方法。
提出需求
利用Git向Github克隆(clone)或者提交(push)代码时,大概有两种方式:HTTPS
和ssh key
。我们都可以使用这两种方法直接克隆(clone)项目,但是在提交(push)的时候:
-
HTTPS
方法需要该项目的账号、邮箱和密码授权,在多人协作开发的时候,可能不太方便; -
ssh key
只需要配置一次,不需要每次都输入账户、邮箱和密码,提交(push)代码时显然更加省时省力;
在官方文档《使用 SSH 连接到 GitHub》1中关于SSH
的介绍如下:
使用 SSH 协议可以连接远程服务器和服务并向它们验证。 利用 SSH 密钥可以连接 GitHub,而无需在每次访问时提供用户名或密码。
OK,这是ssh key
的好处。ssh key
,也称“SSH密钥”,SSH 密钥对总是成双出现的,一把公钥,一把私钥。公钥可以自由的放在您所需要连接的 SSH 服务器上,而私钥必须在自己的设备上保管好。2
所谓"公钥登录",就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录 shell,不再要求密码。这样子,即可保证了整个登录过程的安全,也不会受到中间人的攻击。
也就是说,我们在自己的电脑上生成一对SSH密钥,把其中的公钥
配置到Git服务器(Github、Gitlab、Gitee等)的项目上,私钥
在自己的电脑上配置好,此时就可以在自己的电脑上向Git服务器上的某个项目提交代码了。
在实际操作下,我遇到过这么几种情况:
-
在自己电脑上提交代码到不同的Github账号下;
-
在实验室电脑上提交到Github账号下;
这时就需要多个SSH密钥存在于同一台设备中,并且提交代码时不存在矛盾。在这篇文章中,使用Git服务器:Github,来进行多SSH密钥的配置,首先我们向简单了解下Github中关于SSH的描述。
关于SSH
使用 SSH 协议可以连接远程服务器和服务并向它们验证。 利用 SSH 密钥可以连接 GitHub,而无需在每次访问时提供用户名或密码。
在设置 SSH 时,将会生成 SSH 密钥并将其添加到 ssh-agent,然后将该密钥添加到 GitHub 帐户。 将 SSH 密钥添加到 ssh-agent,通过使用密码确保 SSH 密钥增加一层保护。 更多信息请参阅“使用 SSH 密钥密码”。
建议定期查阅 SSH 密钥列表,撤销任何无效或安全受到威胁的密钥。
注意:如果 SSH 密钥一年未使用,则作为安全预防措施,GitHub 会自动删除非活动的 SSH 密钥。 更多信息请参阅“删除或缺失的 SSH 密钥”。
检查现有的SSH密钥
在生成 SSH 密钥之前,检查电脑上是否存在任何现有的 SSH 密钥。假设已经安装Git。
默认情况下,用户的 SSH 密钥存储在其 ~/.ssh
目录下3 。 在Windows
中打开Git Bash
,在Linux
和Mac
打开任意Terminal
(终端),输入
ls -al ~/.ssh
检查是否存在SSH密钥。默认情况下,公钥的文件名以.pub
结尾。 查看是否有以id_dsa
或 id_rsa
命名的文件,公钥和私钥的前缀名是一样的,其中公钥带有 .pub
扩展名,另一个则是与之对应的私钥,没有扩展名。例如id_rsa.pub
是公钥,id_rsa
是私钥。
注意: DSA 密钥 (SSH-DSS) 不再受支持。 现有密钥将继续运行,但不能将新的 DSA 密钥添加到 GitHub 帐户上。
提示: 如果收到错误“~/.ssh 不存在”的信息,不要担心! 在生成新的 SSH 密钥时会创建它。
如果存在,且想新增一个SSH密钥,则继续阅读下一步骤:生成新 SSH 密钥并添加到 ssh-agent。
生成新SSH密钥并添加到ssh-agent
下列命令适用于Windows
、Linux
和Mac
系统。需要注意的是,根目录~
在不同的系统下,绝对路径不同,如下:
Windows
下,我的电脑账号名为:Dell,则~
代表的绝对路径为:/c/Users/Dell/
;Linux
下,我的账号名为:adoredee,则~
代表的绝对路径为:/home/adoredee/
;Mac
下,我的账号名为:kang,则~
代表的绝对路径为:/Users/kang/
;
下列命令,所有系统通用,且测试通过,在Windows
下进行演示。
生成新 SSH 密钥
-
在
Windows
中打开Git Bash
,在Linux
和Mac
打开任意Terminal
(终端),输入ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
其中,
-
-t
,设置密钥类型,默认是rsa
,可以省略; -
-b
,设置指定要创建的密钥中的位数,即密钥长度,对于RSA密钥,最小为768位,默认是2048位,Github建议设置为4096位; -
-C
,设置注释文字,比如邮箱;
提示: 要想查看id_rsa.pub的密钥长度,输入
ssh-keygen -l -f ~/.ssh/id_rsa.pub
。上述命令只需要更改引号里的邮件地址即可(引号是必须输入的),建议your_email@example.com设置为注册Github时所使用的邮件地址。按下回车后,将创建以所提供的电子邮件地址(your_email@example.com)为标签的新 SSH 密钥,在此我的邮件地址设置为kangzhiheng@live.com。
-
-
紧接着提示“Enter a file in which to save the key(输入要保存密钥的文件)”:
Generating public/private rsa key pair. Enter file in which to save the key (/c/Users/Dell/.ssh/id_rsa):
这时可以不输入文件名,使用默认文件名
id_rsa
,那么就会生成id_rsa
和id_rsa.pub
两个秘钥文件。但一般情况下,不建议这么做,不利于管理,在这里命名格式建议为id_rsa_用途_Git服务器名称
,比如我新建的SSH密钥用于hugo博客搭建,并且代码上传到Github上,所以取名为id_rsa_hugo_github
。如果~/.ssh
目录不存在,上述命令会自动新建~/.ssh
目录。在上述提示后面,不要直接输入密钥名字,应该加上完整路径,如下:
Enter file in which to save the key (/c/Users/Dell/.ssh/id_rsa): ~/.ssh/id_rsa_hugo_github
否则密钥会被创建在根目录
~
下,而不是~/.ssh
目录下。 -
提示输入两次密码,需要注意的是,该密码是你进行
git push
操作时输入的密码,而不是Github账号的密码。当然,你也可以不输入密码,直接按回车。建议设置密码保证安全性,密码太短可能会创建失败,建议使用字母+数字的组合方式,且长度不要低于8位。Enter passphrase (empty for no passphrase): Enter same passphrase again:
接下来提示:
Your identification has been saved in id_rsa_hugo_github. Your public key has been saved in id_rsa_hugo_github.pub. The key fingerprint is: SHA256:Yp6f5vpeaCY/7yXuMuCpxbmHQm+iVamb1qlRpj8nkYA kangzhiheng@live.com The key's randomart image is: +---[RSA 4096]----+ | | | | | . | | E . . | | .B.S | | .X++ . | | .=+B*+ o . | | .+*@BB+ o | | .o=*+@BB= | +----[SHA256]-----+
看到上面这段代码时,说明,SSH 密钥已经创建成功。
id_rsa_hugo_github.pub
为公钥,id_rsa_hugo_github
为私钥,它们均保存在~/.ssh
目录下。
将 SSH 密钥添加到 ssh-agent
如果不想在每次使用 SSH 密钥时重新输入密码,您可以将密钥添加到 SSH 代理,让它管理您的 SSH 密钥并记住您的密码。
-
在后台开启ssh代理
在
Windows
下,根据官方文档上的方法,打开Git Bash
,输入vim ~/.profile
在
~/.profile
里输入:env=~/.ssh/agent.env agent_load_env () { test -f "$env" && . "$env" >| /dev/null ; } agent_start () { (umask 077; ssh-agent >| "$env") . "$env" >| /dev/null ; } agent_load_env # agent_run_state: 0=agent running w/ key; 1=agent w/o key; 2= agent not running agent_run_state=$(ssh-add -l >| /dev/null 2>&1; echo $?) if [ ! "$SSH_AUTH_SOCK" ] || [ $agent_run_state = 2 ]; then agent_start ssh-add elif [ "$SSH_AUTH_SOCK" ] && [ $agent_run_state = 1 ]; then ssh-add fi unset env
保存并退出后,输入
source ~/.profile
更新文件,这时Windows将自动运行ssh-agent
。注:在Windows下开启
ssh-agent
的操作步骤仅供了解,先不要操作,请继续阅读。在
Linux/Mac
下,输入eval $(ssh-agent -s)
成功开启后,会提示SSH代理线程号,如
Agent pid 1107
。ssh-agent
进程将继续运行,直到您注销、关闭计算机或终止该进程。 -
将 SSH 私钥添加到 ssh-agent。 如果创建了不同名称的密钥,或者要添加不同名称的现有密钥,将命令中的
id_rsa
替换为私钥文件的名称,如id_rsa_hugo_github
。SSH服务器默认是去找id_rsa
,现在需要把这个密钥添加到ssh-agent
中,这样ssh服务器才能认识id_rsa_hugo_github
。在
Mac
下,输入ssh-add -K ~/.ssh/id_rsa_hugo_github
因为在Mac上,当系统重启后会“忘记”这个密钥,所以通过指定
-K
把SSH密钥导入到密钥链中。在
Windows/Linux
下,输入ssh-add ~/.ssh/id_rsa_hugo_github
然而,在Windows下,
ssh-agent
进程将继续运行,直到您注销、关闭计算机或终止该进程。 重启Windows后,如果按照第一步中的方法进行设置,可能每次打开Git Bash,需要手动添加私钥到ssh-agent。所以为了使我们每一次打开Git Bash时自动启动ssh-agent,进行如下设置:找到Git的安装目录,再在安装目录下找到
etc
文件夹,进入etc
,打开bash.bashrc
文件,在文件末尾添加# ssh-add private-key eval "$(ssh-agent -s)" ssh-add ~/.ssh/私钥1 ssh-add ~/.ssh/私钥2
其中私钥1、私钥2换成自己私钥的名称即可。保存文件并退出,这样每次打开Git Bash就会自动执行
ssh-agent
代理。 -
查看代理结果
ssh-add -l
记得添加所有在Github上所需要的SSH密钥。
管理多组密钥
可以创建 ~/.ssh/config
来管理多组密钥,每一个 SSH 服务器对应一组密钥对,甚至可以对所有的 SSH 服务器使用同一组密钥4。
现在有这么一种情况,我有两个Github账号,需要两组SSH密钥对,一个Github账号经常使用,创建了密钥对,名为id_rsa_normal_github
和id_rsa_normal_github.pub
,另一个Github账号因为某种原因,也必须使用,为其创建了密钥对,名为id_rsa_hugo_github
和id_rsa_hugo_github.pub
,为此编辑配置文件~/.ssh/config
vim ~/.ssh/config
复制以下内容,在Vim里按下小写字母p
,然后再按下Esc
键,输入:wq
即可保存并退出。
Host github.com
HostName github.com
User git
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_rsa_normal_github
Host adoredee.github.com
HostName github.com
User git
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_rsa_hugo_github
解释:
- Host:Git识别名,是一个别名,如果使用Github上传下载代码,正常情况下是
github.com
,如果是多个Github账号,则需要起一个别名,建议命名规则为项目名/账户名.git服务器
,比如adoredee.github.com
第二个Host,第一个Host为正常命名:github.com
; - HostName:服务器地址,Github地址为
github.com
、GitLab地址为gitlab.com
、Gitee地址为gitee.com
; - IdentityFile: 公钥文件所在的绝对路径;
验证SSH密钥,注意@后面是在~/.ssh/config
文件中指定的"Host"项
先验证第一个Host:github.com
,运行
ssh -T git@github.com
显示
Hi kangzhiheng! You've successfully authenticated, but GitHub does not provide shell access.
再验证第二个Host:adoredee.github.com
,运行
ssh -T git@adoredee.github.com
显示
Hi adoredee! You've successfully authenticated, but GitHub does not provide shell access.
新增SSH密钥到GitHub帐户
首先要问一下自己为什么需要这一步。在小节提出需求中已经讲过,利用Git向Github克隆(clone)或者提交(push)代码时,大概有两种方式:HTTPS
和ssh key
。我们都可以使用这两种方法直接克隆(clone)项目,但是在提交(push)的时候:
HTTPS
方法需要该项目的账号、邮箱和密码授权,在多人协作开发的时候,可能不太方便;ssh key
只需要配置一次,不需要每次都输入账户、邮箱和密码,提交(push)代码时显然更加省时省力;
比如我没有设置SSH密钥连接到我的Github上,那么在使用git clone
命令从Github上同步Github上的代码库时,如果使用SSH链接,比如
git clone git@github.com:kangzhiheng/GitLocalDoc.git
可能会出现错误
Permission denied (publickey).
fatal: Could not read from remote repository.
如下图:
这时就需要新增SSH密钥到Github账户上。
假设已经在~/.ssh
里新建了SSH密钥对:id_rsa.pub
和id_rsa
,将公钥id_rsa
添加到Github账户5
-
将 SSH 密钥复制到剪贴板
如果 SSH 密钥文件与示例代码不同,请修改文件名以匹配您当前的设置。 在复制密钥时,请勿添加任何新行或空格。
Windows
下:clip < ~/.ssh/id_rsa.pub
Linux
下:# 安装xclip sudo apt-get install xclip # 复制公钥 xclip -sel clip < ~/.ssh/id_rsa.pub
Mac
下:pbcopy < ~/.ssh/id_rsa.pub
注意: 如果clip、xclip、pbcopy不可用,可找到隐藏的
.ssh
文件夹,在常用的文本编辑器(例如VScode)中打开该文件,并将其复制到剪贴板。 -
登录Github,在Github任何页面的右上角,单击个人资料照片,然后单击 Settings(设置);
-
在用户设置侧边栏中,单击 SSH and GPG keys(SSH 和 GPG 密钥);
-
单击 New SSH key(新 SSH 密钥) 或 Add SSH key(添加 SSH 密钥);
-
在 “Title”字段中,为新密钥添加描述;
-
将第一步复制好的密钥粘贴到 “Key"字段中;
-
单击 Add SSH key(添加 SSH 密钥);
-
如有提示,请确认您的 GitHub 密码;
其它公钥添加到Github里的方法与此相同,只需要改变一下公钥的名称即可。注:这一小节的图片来自Github官方。
匹配多个Github项目与多个SSH密钥(非常重要)
正常情况下,推送本地项目到Github项目的操作如下:
-
配置邮箱和用户名
还是老问题:为什么要配置邮箱和用户名?其实是记录每一次
commit
的用户和与之关联的邮箱。可以配置一个全局的
user.name
和user.email
,也可以针对不同的项目配置本地特有不同的user.name
和user.email
6。当全局配置和本地配置同时存在时,先使用本地配置, 后使用全局配置。全局配置:
git config --global user.name "your github name" git config --global user.email "your email"
本地配置:
git config user.name "your github name" git config user.email "your email"
正常情况下,可以进行全局配置,特殊情况下再进行本地特有配置。
-
首先进入本地的项目目录下,新建本地Git管理:
git init git add somefiles git commit -m "commit code"
-
然后新建Github仓库
PointCloud
,推送本地项目代码到从仓库,建立本地与远程仓库连接:git remote add origin git@github.com:kangzhiheng/PointCloud.git
-
最后进行推送(前提是添加了SSH密钥到Github上,参考小节[新增 SSH 密钥到 GitHub 帐户](#新增 SSH 密钥到 GitHub 帐户)):
git push -u origin master
假设这么一种情况,我需要推送本地项目GitlocalDoc到另一个新的Github仓库GitlocalDoc中,经常使用的Github名称是kangzhiheng
,另一个新的Github名称是adoredee
,如果按照上述1、2、3个步骤来进行操作(第1步进行本地配置),在进行第4步git push
的时候,会出现错误
ERROR: Permission to adoredee/GitlocalDoc.git denied to kangzhiheng.
fatal: Could not read from remote repository.
Please make sure you have the correct access rights and the repository exists.
已经在新的名为adoredee
的Github中新增了SSH密钥id_rsa_hugo_github
,也在本地项目根目录下设置了相应的邮箱和用户名,依然出现了这个错误,使用Git Bash打开本地Git配置文件
cat 项目目录/.git/config
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
ignorecase = true
[user]
email = adoredeek@gmail.com
name = adoredee
[remote "origin"]
url = git@github.com:adoredee/GitlocalDoc.git
fetch = +refs/heads/*:refs/remotes/origin/*
在多次尝试与更改中,终于发现了问题所在。在上述配置中的[remote "origin"]
,url
的组成是:
url = git@Host:GitHubName/Repo.git
其中,Host即为小节管理多组密钥里的~/.ssh/config
中设置的Host,此前已经在新的名为adoredee
的Github中新增了SSH密钥id_rsa_hugo_github
,其对应的Host
应该为adoredee.github.com
,因此更换Host即可,即将
url = git@github.com:adoredee/GitlocalDoc.git
更改为
url = git@adoredee.github.com:adoredee/GitlocalDoc.git
该项目只需修改这一次,就可以正常使用git push
命令想Github账户adoredee
下的仓库提交代码了。
知道了原因,就要学会提前亮防止错误发生,在连接远程仓库时,将正常的代码中的github.com
修改为对应的该Github账号里SSH密钥对应的Host
git remote add origin git@adoredee.github.com:adoredee/GitlocalDoc.git
在克隆远程仓库时,也将正常的代码中的github.com
修改为对应的该Github账号里SSH密钥对应的Host
git clone git@adoredee.github.com:adoredee/GitlocalDoc.git
至此问题解决,可以正常使用git pull
、git clone
、git push
等操作。
总结
这篇文章的初衷,主要是遇到了多SSH与多Github对应问题,即遇到了
ERROR: Permission to adoredee/GitlocalDoc.git denied to kangzhiheng.
fatal: Could not read from remote repository.
Please make sure you have the correct access rights and the repository exists.
这个问题,真的花费非常多的时间去解决,记得18年也碰到这个问题,我翻了翻笔记竟然没有记录,像这种疑难杂症,我都会记录防止以后再碰到。上次真的是失误,这次果断记录,其实就是改个url
的问题,但是涉及到的内容却很丰富,逻辑上的相关性更强,还有借此机会,增补知识盲区,不要养成似懂非懂的习惯。
总结经验如下:
- 在
~/.ssh/
目录下,创建多个SSH密钥,并配置~/.ssh/config
; - 使用不同的SSH密钥连接不同Github(当然同一个Github下可以有多个SSH密钥);
- 找到SSH密钥在
~/.ssh/config
对应的Host,替换github.com
为该Host;
番外
介绍几条在实际操作本文方法可能需要的命令:
-
列出当前项目的配置信息,进入到本地项目根目录下,运行:
git config --list
-
列出现有远程仓库,进入到本地项目根目录下,假设你的Host为
github.com
,运行:git remote -v
提示远程URL为SSH:
origin git@github.com:USERNAME/REPOSITORY.git (fetch) origin git@github.com:USERNAME/REPOSITORY.git (push)
或者远程URL为HTTPS:
origin https://github.com/USERNAME/REPOSITORY.git (fetch) origin https://github.com/USERNAME/REPOSITORY.git (push)
-
删除远程连接
git remote remove origin
其中origin为远程连接的别名,这取决于你为其取的名字。
-
远程URL的SSH与HTTP之间的切换7
git remote set-url
命令可更改现有远程仓库的 URL。假设你的Host为
github.com
。SSH —> HTTPS
git remote set-url origin https://github.com/USERNAME/REPOSITORY.git
HTTPS —> SSH
git remote set-url origin git@github.com:USERNAME/REPOSITORY.git