Mac 上的 GitHub SSH Key:多账号时为什么总是认错人
工作仓库 push 时报「Repository not found」,可密钥明明已经加到工作账号上了。用
ssh -G打印出最终配置,就能看到原因:写在前面的Host *让工作仓库也带上了个人密钥,而且排在第一位。GitHub 按个人账号完成了认证,自然找不到工作仓库。
本文先把 SSH 认证链路讲清楚,再给出单账号和多账号的配置方法。文中命令在 macOS 27、OpenSSH 10.3p1、Git 2.55 上验证。实验使用临时目录下的测试密钥,没有改动真实的 ~/.ssh。
一、先说结论
- GitHub 只看公钥。
ssh-keygen -C后面的邮箱只是写在公钥末尾的注释,不参与认证;提交记录里显示的作者由git config user.email决定,两者互不相关。 - 给私钥设置口令,再交给钥匙串保管。
--apple-use-keychain和UseKeychain保存的是口令,不是密钥本身,所以口令为空时它们什么也不做。 - 配置写成
Host github.com,不要写成Host *。IdentityFile会累加:Host *里的密钥会被带到每一台主机,多账号时顺序一错就认错人。 - 多账号用 Host 别名,每个别名只给一个密钥,加上
IdentitiesOnly yes。 - 排查先用
ssh -G <host>看最终生效的配置,再用ssh -vT看实际尝试了哪些密钥。
二、认证链路
一次 git push 走 SSH 时,大致经过这几步:
- Git 把远程地址
git@github.com:owner/repo.git交给ssh; ssh读取~/.ssh/config,按Host匹配出要用的主机名、用户和私钥;- 私钥如果有口令,由
ssh-agent持有解锁后的私钥,macOS 上口令可以存进钥匙串; - GitHub 收到签名后,根据公钥找到它属于哪个账号,再检查这个账号有没有仓库权限。
第 4 步是理解多账号问题的关键:一把公钥只能添加到一个 GitHub 账号上。连上之后你是谁,完全取决于 ssh 先递过去的是哪把私钥。
三、单账号配置
3.1 生成密钥
ssh-keygen -t ed25519 -C "macbook-2026"-t ed25519:GitHub 文档推荐的算法。实测注释为macbook-2026时,Ed25519 公钥 94 字节,4096 位 RSA 公钥 738 字节。只有目标系统不支持 Ed25519 时,才退回-t rsa -b 4096。-C:注释,写成能让你认出这把密钥的名字就行,比如设备名。GitHub 的密钥列表里显示的是你添加时填的 Title,不是这个注释。- 口令建议设置。私钥文件一旦泄露(备份、同步盘、误提交),没有口令就等于直接交出了账号权限。
生成后,~/.ssh/ 下会多出两个文件:id_ed25519(私钥,权限 600)和 id_ed25519.pub(公钥)。
3.2 配置 ~/.ssh/config
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
AddKeysToAgent yes
UseKeychain yes| 配置项 | 作用 |
|---|---|
IdentityFile | 这台主机使用的私钥 |
IdentitiesOnly yes | 只用这里指定的密钥,不去尝试 agent 里的其他密钥 |
AddKeysToAgent yes | 第一次使用时自动加进 ssh-agent,之后不用反复输入口令 |
UseKeychain yes | macOS 专有:从钥匙串读取口令,输入过的口令也会存进去 |
然后把口令存进钥匙串,重启后也不用再输入:
ssh-add --apple-use-keychain ~/.ssh/id_ed25519man ssh-add 对这个参数的说明是:添加密钥时,同时把口令保存到用户的钥匙串。所以它解决的是「每次开机都要输口令」,而不是「密钥会丢失」。老教程里的 -K 是它的旧写法。
3.3 添加公钥并验证
pbcopy < ~/.ssh/id_ed25519.pub # 复制公钥,粘贴到 GitHub → Settings → SSH and GPG keys
ssh -T git@github.com第一次连接会提示确认 GitHub 的主机指纹,要和 GitHub 文档公布的指纹逐字比对后再输入 yes,这一步用来防止中间人攻击。认证成功会看到 Hi <用户名>! You've successfully authenticated,这里的用户名就是 GitHub 认定的账号,排查多账号问题时很有用。
已有的 HTTPS 仓库,改一下远程地址即可:
git remote set-url origin git@github.com:owner/repo.git四、多账号:个人与工作分开
4.1 问题复现
很多教程会给出这样的配置:
Host *
IdentityFile ~/.ssh/id_ed25519_personal
Host github-work
HostName github.com
IdentityFile ~/.ssh/id_ed25519_work看起来 github-work 用的是工作密钥。用 ssh -G 打印最终配置,结果是:
$ ssh -G github-work | grep identityfile
identityfile ~/.ssh/id_ed25519_personal
identityfile ~/.ssh/id_ed25519_workssh_config 的规则是:大多数配置项取第一次匹配到的值,IdentityFile 例外,它会累加。 所以两个密钥都在列表里,个人密钥还排在前面。个人密钥是有效的,GitHub 用它完成认证,把你识别成个人账号,再去找工作仓库,结果是「Repository not found」或者权限不足。这个报错很容易让人以为是仓库地址写错了。
Host * 的另一个副作用是,个人密钥会被递给所有主机,包括公司内网的 GitLab 和任何你 SSH 过去的服务器。
4.2 正确的配置
每个账号一个别名,每个别名只有一个密钥:
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_personal
IdentitiesOnly yes
AddKeysToAgent yes
UseKeychain yes
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_work
IdentitiesOnly yes
AddKeysToAgent yes
UseKeychain yes实测 ssh -G github-work 只剩一个 identityfile ~/.ssh/id_ed25519_work,hostname 解析为 github.com。工作仓库的远程地址要写成别名:git@github-work:acme/order-service.git。
IdentitiesOnly yes 也很重要:没有它,即使配置里只写了一个密钥,ssh 仍然可能尝试 agent 里已经加载的其他密钥,只要其中一把属于别的账号,就又回到了认错人的问题。
4.3 按目录自动切换
每次 clone 都要记得改成别名,迟早会忘。可以让 Git 按目录自动处理:工作项目统一放在 ~/code/work/ 下。
~/.gitconfig:
[user]
name = Your Name
email = me@personal.example
[includeIf "gitdir:~/code/work/"]
path = ~/.gitconfig-work~/.gitconfig-work:
[user]
email = me@work.example
[url "git@github-work:"]
insteadOf = git@github.com:实测效果(Git 2.55):
| 位置 | user.email | 实际连接的 Host |
|---|---|---|
~/code/work/order-service | me@work.example | github-work |
~/code/personal/blog | me@personal.example | github.com |
在 ~/code/work/ 下执行 git clone git@github.com:acme/payment.git | — | github-work |
这样提交作者和认证身份都跟着目录走。注意两点:gitdir: 末尾的 / 表示匹配这个目录下的所有仓库;在 ~/code/work/ 下但还不是 Git 仓库的目录里,条件不成立,读到的仍然是个人配置。
五、排查顺序
ssh -G <host>:看最终生效的hostname、user、identityfile。多账号问题大多在这一步就能看出来。ssh -T git@<host>:看 GitHub 把你认成了哪个账号。ssh -vT git@<host>:看实际按什么顺序尝试了哪些密钥,哪一把被接受。ssh-add -l:看 agent 里当前加载了哪些密钥。git remote -v与git config --show-origin user.email:确认仓库用的是哪个地址、哪份配置。
六、常见误区
- 「
-C要填 GitHub 账号的邮箱」:它只是注释。 - 「
--apple-use-keychain让 Mac 记住密钥」:它记住的是口令,口令为空时不起作用。 - 「
Host *写一个默认密钥最省事」:IdentityFile会累加,多账号时会认错人,还会把密钥递给不相关的主机。 - 「Repository not found 就是地址写错了」:先用
ssh -T确认 GitHub 认出来的是哪个账号。 - 「第一次连接的指纹提示直接 yes」:应该和官方公布的指纹比对。
小结
SSH 认证只认公钥,所以多账号问题归根结底是「ssh 先递出了哪把私钥」。配置上的原则就三条:按主机写配置,不写 Host *;每个别名只配一个密钥并打开 IdentitiesOnly;用 includeIf 让目录决定身份。私钥配上口令,再交给钥匙串保管,安全和便利可以兼得。
配套实验
- codesphere-labs/engineering/ssh-and-git-identity:离线验证公钥大小与私钥权限、
ssh -G下IdentityFile的累加、按目录切换 Git 身份与远程地址(验证记录)
参考资料