GitHub suddenly rejected my SSH key (the fix was a .pub file?!)

GitHub 突然拒绝了我的 SSH 密钥(解决方法竟然是一个 .pub 文件?!)

Today, git pull stopped working on my main laptop. Permission denied (publickey), out of nowhere. I had changed nothing, the key was still registered on GitHub, and pulling the same repository from another laptop (with another key) worked fine. 今天,我主力笔记本上的 git pull 命令突然无法使用了。毫无征兆地报错:Permission denied (publickey)。我什么都没改,密钥也依然在 GitHub 上注册着,而且用另一台笔记本(使用不同的密钥)拉取同一个仓库完全正常。

The rabbit hole I’ll spare you the full troubleshooting session. The key was mathematically sound (openssl rsa -check said RSA key ok), the signature algorithm was the modern rsa-sha2-512, my ~/.ssh/config was clean, and GitHub’s status page was all green. Still: Permission denied. 我就不赘述完整的排查过程了。密钥在数学上是完好的(openssl rsa -check 显示 RSA 密钥正常),签名算法是现代的 rsa-sha2-512,我的 ~/.ssh/config 配置也很干净,GitHub 的状态页面也显示一切正常。但结果依然是:Permission denied。

The fix turned out to be embarrassingly small. My ~/.ssh/github_rsa has lived without a companion .pub file since I re-installed my laptop. Generating one made authentication work again: $> ssh-keygen -y -f ~/.ssh/github_rsa > ~/.ssh/github_rsa.pub 解决方法简单得令人尴尬。自从我重装笔记本系统后,我的 ~/.ssh/github_rsa 私钥一直没有配套的 .pub 公钥文件。生成一个公钥文件后,身份验证就恢复正常了:$> ssh-keygen -y -f ~/.ssh/github_rsa > ~/.ssh/github_rsa.pub

I did not believe it either, so I tested it six times with the .pub file and six times without. Twelve out of twelve: no .pub – rejected, .pub – accepted. Why on earth would that matter? 我起初也不敢相信,于是我进行了测试:有 .pub 文件时测试六次,没有时测试六次。结果十二次测试中:没有 .pub 文件就被拒绝,有 .pub 文件就被接受。这到底为什么会有影响?

Because the .pub file changes which authentication flow OpenSSH uses. With a .pub file present, the client first probes (offers the public key, waits for the server’s OK) and only then signs. With only a private key, OpenSSH skips the probe and sends a fully signed authentication request directly. 因为 .pub 文件会改变 OpenSSH 使用的身份验证流程。当存在 .pub 文件时,客户端会先进行探测(提供公钥,等待服务器确认),然后再进行签名。如果只有私钥,OpenSSH 会跳过探测,直接发送一个已签名的身份验证请求。

Both flows are perfectly legal according to RFC 4252, and a stock sshd accepts both. GitHub, as of today, apparently does not. Did something change on GitHub’s side? My money says yes. 根据 RFC 4252 标准,这两种流程都是完全合法的,标准的 sshd 也都接受。但从今天的情况来看,GitHub 显然不再接受后者了。GitHub 那边发生变化了吗?我敢打赌是的。

The server banner in my debug logs reads 6a2c000, where GitHub’s SSH frontend has historically identified itself as babeld-. That smells like new server software – one that rejects direct-signed publickey requests. It would explain perfectly why everything worked in the morning and nothing worked an hour later, on a machine where nothing changed. 我调试日志中的服务器标识显示为 6a2c000,而 GitHub 的 SSH 前端以往通常标识为 babeld-<hash>。这闻起来像是换了新的服务器软件——一种拒绝直接签名公钥请求的软件。这完美解释了为什么早上一切正常,一小时后在没有任何改动的机器上就无法使用了。

Has anyone else hit this? Do you know what 6a2c000 is? Let me know – and in the meantime: make sure your private keys have their .pub siblings. 还有其他人遇到这种情况吗?你知道 6a2c000 是什么吗?请告诉我——在此期间:请确保你的私钥都有对应的 .pub 文件。