May 11, 2020

7070 words 15 mins read

多个SSH密钥并存且连接到Github

多个SSH密钥并存且连接到Github

  在这篇文章中,使用Git服务器Github,来进行多SSH密钥的配置,介绍多个SSH密钥同时存在于同一台设备中的方法。

提出需求

  利用Git向Github克隆(clone)或者提交(push)代码时,大概有两种方式:HTTPSssh key。我们都可以使用这两种方法直接克隆(clone)项目,但是在提交(push)的时候:

  • HTTPS方法需要该项目的账号、邮箱和密码授权,在多人协作开发的时候,可能不太方便;

  • ssh key只需要配置一次,不需要每次都输入账户、邮箱和密码,提交(push)代码时显然更加省时省力;

  在官方文档使用 SSH 连接到 GitHub1中关于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,在LinuxMac打开任意Terminal(终端),输入

ls -al ~/.ssh

  检查是否存在SSH密钥。默认情况下,公钥的文件名以.pub结尾。 查看是否有以id_dsaid_rsa 命名的文件,公钥和私钥的前缀名是一样的,其中公钥带有 .pub 扩展名,另一个则是与之对应的私钥,没有扩展名。例如id_rsa.pub是公钥,id_rsa 是私钥。

注意: DSA 密钥 (SSH-DSS) 不再受支持。 现有密钥将继续运行,但不能将新的 DSA 密钥添加到 GitHub 帐户上。

提示: 如果收到错误“~/.ssh 不存在”的信息,不要担心! 在生成新的 SSH 密钥时会创建它。

  如果存在,且想新增一个SSH密钥,则继续阅读下一步骤:生成新 SSH 密钥并添加到 ssh-agent

生成新SSH密钥并添加到ssh-agent

  下列命令适用于WindowsLinuxMac系统。需要注意的是,根目录~在不同的系统下,绝对路径不同,如下:

  • Windows下,我的电脑账号名为:Dell,则~代表的绝对路径为:/c/Users/Dell/
  • Linux下,我的账号名为:adoredee,则~代表的绝对路径为:/home/adoredee/
  • Mac下,我的账号名为:kang,则~代表的绝对路径为:/Users/kang/

  下列命令,所有系统通用,且测试通过,在Windows下进行演示。

生成新 SSH 密钥

  1. Windows中打开Git Bash,在LinuxMac打开任意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。

  2. 紧接着提示“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_rsaid_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目录下。

  3. 提示输入两次密码,需要注意的是,该密码是你进行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 密钥并记住您的密码。

  1. 后台开启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 进程将继续运行,直到您注销、关闭计算机或终止该进程。

  2. 将 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代理。

  3. 查看代理结果

    ssh-add -l
    

    记得添加所有在Github上所需要的SSH密钥。

管理多组密钥

  可以创建 ~/.ssh/config 来管理多组密钥,每一个 SSH 服务器对应一组密钥对,甚至可以对所有的 SSH 服务器使用同一组密钥4

  现在有这么一种情况,我有两个Github账号,需要两组SSH密钥对,一个Github账号经常使用,创建了密钥对,名为id_rsa_normal_githubid_rsa_normal_github.pub,另一个Github账号因为某种原因,也必须使用,为其创建了密钥对,名为id_rsa_hugo_githubid_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)代码时,大概有两种方式:HTTPSssh 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.pubid_rsa,将公钥id_rsa添加到Github账户5

  1. 将 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)中打开该文件,并将其复制到剪贴板。

  2. 登录Github,在Github任何页面的右上角,单击个人资料照片,然后单击 Settings(设置);

  3. 在用户设置侧边栏中,单击 SSH and GPG keys(SSH 和 GPG 密钥)

  4. 单击 New SSH key(新 SSH 密钥)Add SSH key(添加 SSH 密钥)

  5. 在 “Title”字段中,为新密钥添加描述;

  6. 将第一步复制好的密钥粘贴到 “Key"字段中;

  7. 单击 Add SSH key(添加 SSH 密钥)

  8. 如有提示,请确认您的 GitHub 密码;

其它公钥添加到Github里的方法与此相同,只需要改变一下公钥的名称即可。:这一小节的图片来自Github官方

匹配多个Github项目与多个SSH密钥(非常重要)

正常情况下,推送本地项目到Github项目的操作如下:

  1. 配置邮箱和用户名

    还是老问题:为什么要配置邮箱和用户名?其实是记录每一次commit的用户和与之关联的邮箱。

    可以配置一个全局的user.nameuser.email,也可以针对不同的项目配置本地特有不同的user.nameuser.email6当全局配置和本地配置同时存在时,先使用本地配置, 后使用全局配置。

    全局配置:

    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"
    

    正常情况下,可以进行全局配置,特殊情况下再进行本地特有配置。

  2. 首先进入本地的项目目录下,新建本地Git管理:

    git init
    git add somefiles
    git commit -m "commit code"
    
  3. 然后新建Github仓库PointCloud,推送本地项目代码到从仓库,建立本地与远程仓库连接:

    git remote add origin git@github.com:kangzhiheng/PointCloud.git
    
  4. 最后进行推送(前提是添加了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 pullgit clonegit 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的问题,但是涉及到的内容却很丰富,逻辑上的相关性更强,还有借此机会,增补知识盲区,不要养成似懂非懂的习惯。

  总结经验如下:

  1. ~/.ssh/目录下,创建多个SSH密钥,并配置~/.ssh/config
  2. 使用不同的SSH密钥连接不同Github(当然同一个Github下可以有多个SSH密钥);
  3. 找到SSH密钥在~/.ssh/config对应的Host,替换github.com为该Host;

番外

介绍几条在实际操作本文方法可能需要的命令:

  1. 列出当前项目的配置信息,进入到本地项目根目录下,运行:

    git config --list
    
  2. 列出现有远程仓库,进入到本地项目根目录下,假设你的Hostgithub.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)
    
  3. 删除远程连接

    git remote remove origin
    

    其中origin为远程连接的别名,这取决于你为其取的名字。

  4. 远程URL的SSH与HTTP之间的切换7

    git remote set-url 命令可更改现有远程仓库的 URL。

    假设你的Hostgithub.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
    

参考


  1. 使用 SSH 连接到 GitHub ↩︎

  2. SSH Key ↩︎

  3. 服务器上的 Git - 生成 SSH 公钥 ↩︎

  4. 管理多组密钥 ↩︎

  5. 新增 SSH 密钥到 GitHub 帐户 ↩︎

  6. 同一个Mac,配置多个SSH Key ↩︎

  7. 更改远程仓库的 URL ↩︎