你刚坐下,准备把代码推送到工作用的 GitHub 账号。没过多久,又需要更新个人 GitHub 账号下的仓库。这时终端却返回 “Permission denied (publickey).”。在一台笔记本电脑上管理不同的 Git 账号时,这种挫败感非常常见。

你或许会手动切换密钥,或者干脆使用不同的机器。这些权宜之计既浪费时间,也容易出错。其实有更好的方案:你可以把多个 SSH 密钥配置为协同工作。为每个服务生成一把独立的新 SSH 密钥;然后创建一个统一的配置文件。这个文件会告诉系统,针对不同主机应当使用哪一把密钥。这样设置之后,就无需再手动切换。你的所有远程连接都将拥有一套长期稳定、更加顺畅的工作流。

如何在你的系统上配置多个 SSH 密钥

所有认证文件都应存放在你本地机器的 ~/.ssh 目录中。这个目录用于保存私钥和公钥文件对。将所有内容集中管理,不仅更方便,也能确保 SSH 客户端自动找到正确的凭证。

为每个服务生成独立密钥

首先,为你使用的每个远程服务创建一组独立的密钥对。下面这条命令会生成一个带有自定义文件名的新 SSH 密钥:

ssh-keygen -t ed25519 -C "work@email.com" -f ~/.ssh/gitlab_work

-f 参数用于指定输出文件名。如果不使用它,系统会默认生成到 id_ed25519,这可能会覆盖你已有的密钥。建议使用像 github_personalgitlab_work 这样的描述性名称,便于一眼识别每把密钥的用途。

对于新密钥而言,Ed25519 是推荐使用的算法。与 RSA 相比,它通常拥有更好的安全性、更快的性能以及更小的密钥体积。大多数现代 Linux 发行版和 OpenSSH 6.5 及以上版本都已完整支持它。不过,某些较旧的系统或特定云服务商可能仍要求使用 RSA。如果你出于兼容性需要选择 RSA,请至少使用 3072 位,最好是 4096 位。

当命令提示你输入 passphrase 时,请务必设置一个。这层额外保护可以在他人获得你机器访问权限时,进一步保护你的私钥。除非你稍后配置了 agent,否则每次连接时都需要输入这个 passphrase。

将公钥添加到远程主机

在生成每组密钥后,你还必须把对应的公钥注册到相关服务中。对于你的工作 GitHub 账号,可以进入 Settings → SSH keys,然后粘贴对应 .pub 文件中的内容。对于托管在公司服务器上的工作项目仓库,你可能需要将该公钥追加到目标机器的 ~/.ssh/authorized_keys 文件中。

ssh-copy-id 工具可以大大简化这一过程。运行 ssh-copy-id -i ~/.ssh/gitlab_work.pub user@server.com,即可在不覆盖现有条目的前提下,把公钥追加到远程主机中。当你需要在多台机器之间管理多把密钥时,这个命令会非常高效。

在开始配置统一的 config 文件之前,建议先使用 -i 选项分别测试每一把密钥。只要看到成功提示,就说明该密钥本身是可用的。这一步能在你搭建完整配置前,提前定位问题。

使用 Config 文件管理多个 SSH 密钥

~/.ssh/config 文件就是整套配置的指挥中心。这个纯文本文件会明确告诉系统:连接不同远程主机时,应该出示哪一把密钥。你可以创建与主机名匹配的规则,并为这些规则指定对应的身份文件。这样一来,就不再需要猜测或手动选钥匙。

设置主机别名与 IdentityFile

使用任意文本编辑器打开 config 文件。你需要创建若干配置块来定义连接参数。每个配置块都以一行 Host 开头,用来创建你连接时输入的别名。HostName 指定实际域名。User 指定登录用户名。IdentityFile 则指定要使用的私钥路径。

假设你同时管理工作 GitHub 账号和个人 GitHub 账号,你的 config 文件可以写成这样:

# --- 用于工作 GitHub ---
Host github.com-work
HostName github.com
User git
IdentityFile ~/.ssh/github_work
IdentitiesOnly yes

# --- 用于个人 GitHub ---
Host github.com-personal
HostName github.com
User git
IdentityFile ~/.ssh/github_personal
IdentitiesOnly yes

IdentitiesOnly yes 这个指令非常关键。当你的 agent 中加载了多把 SSH 密钥时,建立连接时系统可能会依次尝试多把密钥,直到找到正确的一把。服务器会拒绝错误的密钥,而多次失败甚至可能触发安全限制。这个指令会确保系统只提供当前配置块中指定的那一把密钥,从而避免误发密钥和认证失败。

你也可以在同一个主机配置中写多个 IdentityFile。SSH 会按顺序依次尝试这些密钥,直到服务器接受其中某一把。这种做法在你轮换密钥或通过多种凭证保留访问能力时非常有帮助。

还有一点需要注意:避免在配置文件后面又重新覆盖之前已经修改过的主机规则。比如你先创建了一个 Host github.com-work 别名并修改了 hostname,后面又新增了一个针对 Host github.com 的配置并指定了不同的密钥,agent 可能会覆盖你前面的规则。建议让别名组织清晰、规则足够具体。

使用通配符设置默认配置

当你需要管理很多服务器时,通配符可以显著简化 SSH 配置。星号(*)可以匹配零个或多个字符,问号(?)则匹配恰好一个字符。借助这些模式,你可以为整组主机应用统一设置,而不必为每台机器都单独写一个配置块。

模式语法:一个模式由零个或多个非空白字符、*(匹配零个或多个字符的通配符)或 ?(匹配恰好一个字符的通配符)组成。

例如,你可以为某个域名下的所有主机设置统一参数:

Host *.company.com
User deploy
IdentityFile ~/.ssh/company_deploy
IdentitiesOnly yes

这条规则会应用到 staging.company.com、production.company.com 以及其他任何子域名。你也可以匹配 IP 范围。例如,Host 192.168.0.? 这个模式会匹配 192.168.0.0 到 192.168.0.9 范围内的任意主机。

通配符还支持继承式匹配。比如主机名 acmereplica 可能会先匹配一个通用的 Host acme* 配置块以获得用户设置,再匹配更具体的 Host acmereplica* 配置块以获取其 hostname。SSH 会按照自上而下的顺序读取配置,并对每个参数采用第一个匹配到的值。

这种方式的扩展性非常好。新增一台服务器时,借助通配符往往只需要补充两行配置即可。你无需重复书写大量公共参数,通配模式会替你处理剩余工作。

Host * 配置块可以作为你的默认配置。请将它放在文件底部。所有没有匹配到更具体规则的主机,都会继承这里的设置。比如你可以在这里统一设置 AddKeysToAgent yes,以自动加载密钥。

你的 SSH config 文件会彻底改变你管理远程连接的方式。你只需要把多个 SSH 密钥配置一次,之后就可以不再关心手动选钥匙的问题。系统会自动把每次连接路由到正确的配置。创建好文件后,请记得测试,确认每个主机都使用了预期的密钥。

将多组 SSH 密钥加入 Agent

SSH agent 是一个后台程序,用于在内存中保存已经解密的私钥。它可以让你无需在每次连接远程主机时都重复输入 passphrase。当你同时使用多组 SSH 密钥时,agent 几乎是必不可少的。它可以统一管理所有密钥,并在认证时自动提供正确的那一把。

使用 ssh-add 实现自动认证

首先,在终端会话中运行 eval "$(ssh-agent -s)" 来启动 agent。这个命令会启动 agent,并设置它所需的环境变量。然后,使用 ssh-add 命令把你的私钥加入进去。

agent 会为每把密钥提示你输入一次 passphrase。之后,它就会把解密后的私钥保存在内存中。你可以运行 ssh-add -l 来确认当前加载了哪些密钥。这个命令会列出 agent 当前持有的所有指纹。

关于 SSH agent forwarding 的安全建议:Agent forwarding 可以让你通过堡垒机使用本地密钥。但是,如果转发目标主机被攻破,攻击者就可能劫持你的密钥。你管理的密钥越多,风险也越大,因为被攻破的目标可能暴露你所有被转发的密钥。因此,只有在完全信任目标服务器时才应启用 agent forwarding。更安全的替代方案是使用 ProxyJump-J),因为它无需转发密钥本身:ssh -J bastion.example.com user_x@internal.example.com

让密钥在重启后继续可用

agent 只会把密钥保存在内存中。当你重启机器后,agent 中加载的密钥会被清空。除非你额外配置持久化机制,否则每次开机后都需要手动重新添加。

在 macOS 上,你可以将 passphrase 保存在 Keychain 中。运行 ssh-add --apple-use-keychain ~/.ssh/github_personal,即可在添加密钥的同时安全保存其 passphrase。然后,在你的 ~/.ssh/config 文件中 Host * 这类通用配置块下加入以下内容:

UseKeychain yes
AddKeysToAgent yes

UseKeychain yes 指令会告诉 SSH 自动从 Keychain 中读取 passphrase。这样在机器重启后,agent 也能自动加载你的密钥,而无需再次手动输入。

在 Linux 上,做法会略有不同。有些发行版支持 ssh-add -K,但这个参数并不通用。更可靠的方式是使用像 gnome-keyringKWallet 这样的密钥链管理工具。它们可以与你的桌面环境集成,并在你登录时自动解锁密钥。或者,你也可以创建一个开机启动脚本,在每次系统启动后自动加载所需密钥。

ssh-agent 能显著改善你的工作流。你只需把密钥配置一次,后续认证过程就会在后台悄然完成。这样,在多个 Git 服务之间来回切换时,你会节省大量时间,也能减少不必要的挫败感。

测试你的多 SSH 密钥配置

你已经生成了独立密钥、编写了 config 文件,也把所有内容加载进了 agent。现在就到了验证配置是否真正生效的时候。逐一测试每一条连接,可以确保配置完全符合预期。这一步能在问题打断你的工作流之前,提前把它们找出来。

使用 -T 参数验证连接

-T 参数会告诉 SSH 禁用伪终端分配。对于 Git 托管平台来说,这非常合适,因为你只需要验证认证是否成功,而不需要真正打开一个交互式 shell。请针对每个已配置的主机分别运行测试命令。

例如,针对你的个人 GitHub 账号,执行:

ssh -T git@github.com-personal

注意,这里的主机名使用的是你在 config 文件中定义的别名。SSH 会读取这个别名,找到对应的配置块,并使用正确的身份文件。成功时,返回信息通常类似于:“Hi username! You’ve successfully authenticated, but GitHub does not provide shell access.” 这表示 GitHub 已识别你的密钥,并将其关联到正确的账号。

对于你的工作 GitLab 实例,可以运行:

ssh -T git@gitlab.com-work

GitLab 会返回一条包含你用户名的欢迎消息。每一次成功提示,都说明你的 config 文件已经正确地将连接路由到了目标配置。系统无需任何手动干预,就自动选择了合适的密钥。

请把你定义过的每个主机都测一遍。你可以根据 config 文件中的别名列一个清单,然后逐项验证。这样可以第一时间发现拼写错误、路径不正确或配置不匹配等问题。

如果测试失败,请仔细查看错误提示。出现 “Permission denied (publickey)” 通常意味着 SSH 未能完成认证。这时请检查 IdentityFile 路径是否正确;确认公钥是否已经添加到远程主机;并核对你输入的别名是否与 config 中完全一致。

通过克隆仓库确认配置生效

-T 测试可以证明认证链路没有问题。但在真实场景中,你最终还是要克隆仓库。因此,做一次实际克隆测试,才能证明整套配置在正常使用中也能稳定工作。

先切换到本地机器上的一个临时目录,然后克隆你个人 GitHub 账号下的私有仓库:

git clone git@github.com-personal:username/private-repo.git

Git 会使用这个主机别名来查找对应配置,并自动选择你的个人密钥。仓库应能顺利下载,无需额外提示你输入 passphrase 或手动选择密钥。这说明你的个人配置已经运行正常。

接着,测试工作环境。切换到另一个目录,并从公司 GitLab 服务器克隆工作项目仓库:

git clone git@gitlab.com-work:company/work-project.git

同样,别名会把 SSH 引导到正确的身份文件。工作项目仓库也应该能够无冲突地顺利克隆下来。至此,你就验证了两套密钥路径可以同时正常工作。系统已经能够区分不同主机,并在每次连接时自动应用正确的凭证。

如果你还配置了其他服务,也请重复这个流程。为每个主机都克隆一个测试仓库。这样全面的验证方式,能确保每个别名都真正可用,从而避免在日常工作中遭遇意外。

成功完成克隆,就意味着你已经实现了:只需配置一次多个 SSH 密钥,之后即可长期稳定使用。整个认证过程会在后台静默完成。你可以在个人项目和工作项目之间自由切换,无需手动切换密钥,也不会再被权限错误打断,更不会浪费多余时间。

现在,你的多 SSH 密钥已经能够无缝协同工作。config 文件会精确地将连接路由到正确目标,agent 则会在后台自动提供凭证。你的工作流将因此变得顺畅而高效。

排查多个 SSH 密钥之间的冲突

即使配置过程已经足够谨慎,问题仍然可能出现。最常见的通常有两类:权限错误,以及系统使用了错误的密钥。只要理解问题根源,这两类故障一般都很容易修复。

修复 “Permission Denied (publickey)” 错误

OpenSSH 会在本地机器上严格检查权限。如果你的私钥文件权限过于宽松,SSH 会直接拒绝使用它。你可能会看到类似 “UNPROTECTED PRIVATE KEY” 或 “Permissions are too open.” 的提示。这些报错其实是在保护你,防止使用不安全的配置。

在 macOS 或 Linux 上,运行 chmod 600 ~/.ssh/your-key-file 即可修复私钥权限。Windows 用户则需要通过属性界面的 Security 选项卡进行调整。右键点击密钥文件,选择 Properties,然后进入 Security,再点击 Advanced。关闭继承,并移除除你自己之外其他用户的权限。

解决系统使用了错误密钥的问题

有时 SSH 会把错误的密钥发给远程主机。在没有其他明确指示的情况下,系统会默认尝试 ~/.ssh/id_rsaid_ed25519。你的 config 文件本应覆盖这种默认行为,但实际配置时难免会有疏漏。

此时可以用详细模式进行诊断。运行 ssh -v -T git@github.com-personal,查看系统到底出示了哪一把密钥。输出信息会按顺序展示每一次认证尝试,你可以很快发现其中的错配问题。

你还可以核对指纹。例如,GitHub 会公开发布其 RSA 主机密钥指纹。你可以把连接时显示的指纹与官方公布值进行比较。若两者一致,就说明你连接到的是正确的服务器。

最后,再确认一次:对应的公钥是否已经确实添加到了远程服务中。进入你的 Git 托管平台 SSH 设置页面,检查正确的公钥是否存在。请记住,私钥永远不应离开你的本地机器。ssh-agent 只会在本地持有私钥,并仅在认证时出示它。绝不要与任何服务器共享或复制你的私钥。

至此,你已经完成了整个过程。每个服务都有独立密钥;结构清晰的 ~/.ssh/config 文件负责为每次连接精准路由;所有测试也都表明你的配置工作正常。这整个流程,就是如何将多个 SSH 密钥配置为可同时使用的完整方法。

从现在开始,你不必再手动切换密钥。正确的密钥会始终匹配正确的主机。ssh-agent 会在后台静默管理所有凭证。这种配置方式不仅适用于 Git 服务,也同样适用于任何远程服务器。

请始终妥善保管私钥。每一把密钥都应设置 passphrase。保持目录整洁,删除不再使用的条目,并在需求变化时及时更新 config 文件。这些习惯将帮助你长期高效地维护整套工作流,也会让你每天都节省更多时间。

常见问题

我可以为多个服务共用一把 SSH 密钥吗?

从技术上说可以,但并不推荐。为不同服务使用独立密钥,可以在某一把密钥泄露时把影响范围降到最低,也更便于后续进行控制和吊销。

如果以后我要新增一把 SSH 密钥,会发生什么?

只需用一个清晰易懂的文件名生成新密钥;将公钥添加到对应远程主机;在 config 文件中新增一个配置块;然后运行 ssh-add 把它加载进 agent 即可。

为什么 SSH 总是反复要求我输入 passphrase?

因为系统重启后,agent 中加载的密钥会被清空。你可以在 config 文件中添加 AddKeysToAgent yes。如果你使用 macOS,请配合 ssh-add --apple-use-keychain;如果你使用 Linux,则可以配置相应的 keychain 管理工具。

这套方案能在 Windows 上使用吗?

可以。Windows 版 OpenSSH 支持相同的 config 文件结构。你的目录路径应使用 %USERPROFILE%\.ssh\。这些命令在 PowerShell 中也可以直接使用,无需修改。