共计 3986 个字符,预计需要花费 10 分钟才能阅读完成。
当我们有多个 SSH 账号时,会遇到需要使用不同 SSH 账号登录不同的远程服务器,或 push 不同的远程仓库的情况,比如:
- github 的用户名为 user_gh, 邮箱地址为 [email protected],私钥为 id_rsa_gh
- gitlab 的用户名为 user_gl,邮箱地址为 [email protected],私钥为 id_rsa_gl
如何在每次 push/pull/远程登录时自动匹配这些信息呢?笔者说下常用的解决方案。
多个用户信息的配置
在使用 git 推送代码到远程仓库时,若不同仓库的用户名不同,可将最常用的用户名设置为全局用户名,不常用的用户名单独在指定仓库进行设置。
全局设置
执行如下命令进行全局设置:
git config --global user.name "user_gh"
git config --global user.email "[email protected]"
也可以编辑 ~/.gitconfig
,设置如下:
[user]
name = user_gh
email = [email protected]
指定设置
在指定仓库下执行如下命令:
git config user.name "user_gh"
git config user.email "[email protected]"
也可以编辑该仓库下的 .git/config
,设置如下:
[user]
name = user_gh
email = [email protected]
多个 SSH key 的配置
如何让不同的 SSH key 自动识别不同的目标地址呢?笔者以开头的需求为例,说下配置 git 的过程,若需求是访问远程服务器,配置相似。
生成 SSH key
使用 ssh-keygen
命令生成公私钥,一般常用的参数如下:
-b
:指定 key 的 bit 数,对于 RSA key 而言,默认是 3072 bits-f
:指定 key 的文件名,若需要指定路径,则文件名需要和路径一直,如~/.ssh/id_rsa_gh
-C
:指定新的 comment,即公钥末尾的字符串,一般是邮箱地址,若不指定会是username@hostname
的形式-t
:指定私钥类型,一般有 “dsa”, “ecdsa”, “ecdsa-sk”, “ed25519”, “ed25519-sk” 和 “rsa” 等几种,若指定为 rsa 还可以指定签名类型,默认为 “rsa-sha2-512”
如为 github.com 生成新 SSH key,key 名为 id_rsa_gh:
> ssh-keygen -f ~/.ssh/id_rsa_gh -C [email protected]
> ls -l
id_rsa
id_rsa.pub
id_rsa_gh
id_rsa_gh.pub
注意:若已有 ~/.ssh/id_rsa
和 ~/.ssh/id_rsa.pub
,建议先对这两个文件进行备份,防止误操作覆盖了这两个文件。
将生成的 SSH 公钥(~/.ssh/id_rsa_gh.pub
)内容添加到 github(Settings > SSH and GPG keys > New SSH key)中。若是要登录远程服务器,将公钥添加到远程服务器的 ~/.ssh/authorized_keys
文件中。
使用 ssh-agent 管理 SSH key
在使用上述 SSH key push 文件到 github 时可能会提示 ERROR: Permission to qileq/x.git denied to some-username
,这里的用户名 “some-username” 可能不是 .git/config
设置的用户名。类似的若是 ssh 登录远程服务器,可能仍需要输入密码。这两个问题的原因可能是未使用正确的 SSH 私钥,这个问题可以通过 ssh-agent 来解决。
ssh-agent 是一个管理 SSH key 的后台辅助程序,它将私钥加载到内存中,避免每次使用私钥时都需要输入密码,自动加载 ~/.ssh/id_rsa
、 ~/.ssh/id_dsa
和 ~/.ssh/id_ecdsa
等各类型的默认私钥,这就是为什么我们不需要显式将 ~/.ssh/id_rsa
添加到 ssh-agent,却能访问远程服务器的原因。当 client 通过 ssh 连接到远程服务器时(或请求 github 时),服务器端会发送验证请求来识别 client 的身份,此时 client 会将验证请求转发给 ssh-agent,ssh-agent 根据配置查找已加载的私钥,然后将私钥处理过后的验证数据发送给 server 进行验证。从某种程序上来讲,ssh-agent 是 SSO 的一种实现。
ssh-agent 通过 ssh-add
来管理私钥,如下是 ssh-add
的一些习惯用法:
ssh-add filename
:添加指定私钥,如ssh-add ~/.ssh/id_rsa_gh
将~/.ssh/id_rsa_gh
添加到 ssh-agent 内存中ssh-add -l
:列出加载的私钥指纹ssh-add -L
:列出加载的私钥对应的公钥ssh-add -D
:删除所有加载的私钥ssh-add -d filename
:删除指定的私钥
macOS 系统是开机自动启动 ssh-agent,而 Linux 需要执行 eval $(ssh-agent -s)
来启动 ssh-agent。在启动 ssh-agent 后,执行 ssh-add ~/.ssh/id_rsa_gh
将新生成的私钥添加到 ssh-agent,然后执行 ssh -T [email protected]
测试私钥是否正确。
这里有个问题,前面说到 ssh-agent 将私钥保存在内存中,那么当机器重启后,这些私钥信息都将失效,需要把所有需要的私钥再加载一遍。如何解决这个问题呢?这里有两种方案:
- 每次执行
ssh-add filename
添加私钥时,都将操作命令保存到~/.zshrc
或~/.bashrc
等终端工具中。 - 使用 keychain 来管理私钥。使用
ssh-add -K filename
可将私钥添加到用户的 keychain 中。- macOS 自带 keychain,若 ssh 未使用 keychain,尝试在
~/.ssh/config
中添加UseKeychain yes
,如:Host github.com UseKeychain yes
- Linux 通过包管理器可安装 keychain。
- macOS 自带 keychain,若 ssh 未使用 keychain,尝试在
使用 ssh-agent 还能实现 SSH 代理转发的功能,更多内容可参考 An Illustrated Guide to SSH Agent Forwarding 和 使用 SSH 代理转发,由于这些内容和本文关系不大,故此不赘述。
配置 ssh config
编辑 ~/.ssh/config
,针对不同的仓库地址配置不同的 Host
、HostName
、User
和 IdentityFile
,如下:
Host github.com
HostName github.com
User user_gh
IdentityFile ~/.ssh/id_rsa_gh
Host gitlab.com
HostName gitlab.com
User user_gl
IdentityFile ~/.ssh/id_rsa_gl
这样不同的仓库即使用不同的信息了。
修改仓库配置
这里还有个问题,如果 github.com 上两个仓库使用的用户信息不同该如何处理呢?假设一个用户是 user_gh,一个用户是 qileq,分别有各自的私钥。配置过程如下:
- 修改
~/.ssh/config
,添加 User qileq 相关的信息,如下:
Host github.com
HostName github.com
User user_gh
IdentityFile ~/.ssh/id_rsa_gh
Host github-qileq.com
HostName github.com
User qileq
IdentityFile ~/.ssh/id_rsa_qileq
Host gitlab.com
HostName gitlab.com
User user_gl
IdentityFile ~/.ssh/id_rsa_gl
- 修改仓库的 git 信息,将仓库配置 url 中的
github.com
替换为 ssh config 中设置的Host
值。
以 Java 教程 java-roam-guide 为例,可以使用如下两种方式来修改仓库地址:- 执行命令
git remote set-url origin [email protected]:qileq/java-learning.git
- 将该仓库
.git/config
中的url = [email protected]:qileq/java-learning.git
修改为url = [email protected]:qileq/java-learning.git
修改完成后,执行git remote -v
查看仓库地址是否正确。
注:这里的仓库设置为 git 协议,非 https 协议。
- 执行命令
-
若是某仓库想覆盖默认设置的用户和邮箱的话,可在该仓库的
.git/config
文件中增加如下name
和email
的配置,如下:
[user]
email = [email protected]
name = qileq
若不设置的话,会使用 ~/.gitconfig
中配置的默认用户信息。
问题
Repository not found
在 push/pull 部分仓库的数据时,有如下报错:
ERROR: Repository not found.
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
除了检查完仓库是否输入错误外,若有多个 ssh 账户,可以执行 ssh-add -l
确认下是否相应的私钥未加载到内存中。若未加载,通过 ssh-add
加载后再次 push/pull 重试。
可以看到 SSH 有一套强大而又复杂的规则体系,通过正确的设置能节省我们不少时间。笔者这里抛砖引玉,对多账号的设置进行了一个总结,若有不当之处,请多指正。