GitHub SSH 连接失败排障实录
在本地 Git 仓库执行 git pull / git fetch / git push 时,若 SSH 远程地址配置为 git@github.com:...,可能先报 主机密钥变更 ,清理后又变成 索要 git@github.com 密码 并最终 Permission denied。这类问题通常出在 SSH 连 GitHub 的两层校验,而不是仓库本身损坏。
速览
- 第一层 :
known_hosts校验「是不是真 GitHub」。- 第二层:私钥公钥配对,证明「是不是你」。
- 典型坑 :IPv6 路径解析到非官方端点 → 错误指纹被
yes写入 → 公钥认证失败 → SSH 误问密码。- 修复 :删错记录、写入官方主机密钥、强制 IPv4 (
AddressFamily inet)。
目录
- [1. 现象](#1. 现象)
- [2. 两层校验与根因](#2. 两层校验与根因)
- [3. 事件链条](#3. 事件链条)
- [4. 为何以前正常、现在突然不行](#4. 为何以前正常、现在突然不行)
- [5. 修复步骤](#5. 修复步骤)
- [6. 经验要点](#6. 经验要点)
- [7. 延伸阅读](#7. 延伸阅读)
1. 现象
先后出现两类报错:
主机密钥变更警告
text
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
Host key verification failed.
清理 known_hosts 后变成索要密码
text
git@github.com's password:
Permission denied (publickey,password).
注意:git@github.com's password 不是 GitHub 网页登录密码;出现这行说明 SSH 公钥认证已失败 ,客户端退回到密码方式,而 GitHub 早已不支持 SSH 密码登录。
2. 两层校验与根因
| 层级 | 作用 | 本次出了什么问题 |
|---|---|---|
| 主机密钥校验 | 确认连接端是 GitHub 官方服务器 | 本地 known_hosts 与远端返回的密钥不一致 |
| 用户密钥认证 | 用 ~/.ssh/ 下私钥对应公钥证明身份 |
连到错误端点后,假服务器上没有你的公钥 → 认证失败 |
IPv6 路径与错误指纹
通过 IPv6 连接时,若网络环境存在 DNS 污染、错误路由或中间代理,可能拿到 非 GitHub 官方 的主机密钥。
| 对比项 | 说明 |
|---|---|
| 错误端点返回的指纹(示例) | SHA256:pN0ui3RlOr6UsAGbPAV09ewnytROetwVvMGjUl+Lo6Q |
| GitHub 官方 Ed25519 指纹 | SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU |
两者不一致时,说明当前 TCP 连接 未必连到 GitHub 。若在提示 Are you sure you want to continue connecting? 时未核对就输入 yes,会把 错误主机密钥永久写入 ~/.ssh/known_hosts,问题从「连不上」恶化为「连错服务器还要密码」。
官方指纹查阅:GitHub SSH key fingerprints
3. 事件链条
#mermaid-svg-NwQvuN4eicEkO1mL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NwQvuN4eicEkO1mL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NwQvuN4eicEkO1mL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NwQvuN4eicEkO1mL .error-icon{fill:#552222;}#mermaid-svg-NwQvuN4eicEkO1mL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NwQvuN4eicEkO1mL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NwQvuN4eicEkO1mL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NwQvuN4eicEkO1mL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NwQvuN4eicEkO1mL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NwQvuN4eicEkO1mL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NwQvuN4eicEkO1mL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NwQvuN4eicEkO1mL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NwQvuN4eicEkO1mL .marker.cross{stroke:#333333;}#mermaid-svg-NwQvuN4eicEkO1mL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NwQvuN4eicEkO1mL p{margin:0;}#mermaid-svg-NwQvuN4eicEkO1mL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NwQvuN4eicEkO1mL .cluster-label text{fill:#333;}#mermaid-svg-NwQvuN4eicEkO1mL .cluster-label span{color:#333;}#mermaid-svg-NwQvuN4eicEkO1mL .cluster-label span p{background-color:transparent;}#mermaid-svg-NwQvuN4eicEkO1mL .label text,#mermaid-svg-NwQvuN4eicEkO1mL span{fill:#333;color:#333;}#mermaid-svg-NwQvuN4eicEkO1mL .node rect,#mermaid-svg-NwQvuN4eicEkO1mL .node circle,#mermaid-svg-NwQvuN4eicEkO1mL .node ellipse,#mermaid-svg-NwQvuN4eicEkO1mL .node polygon,#mermaid-svg-NwQvuN4eicEkO1mL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NwQvuN4eicEkO1mL .rough-node .label text,#mermaid-svg-NwQvuN4eicEkO1mL .node .label text,#mermaid-svg-NwQvuN4eicEkO1mL .image-shape .label,#mermaid-svg-NwQvuN4eicEkO1mL .icon-shape .label{text-anchor:middle;}#mermaid-svg-NwQvuN4eicEkO1mL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NwQvuN4eicEkO1mL .rough-node .label,#mermaid-svg-NwQvuN4eicEkO1mL .node .label,#mermaid-svg-NwQvuN4eicEkO1mL .image-shape .label,#mermaid-svg-NwQvuN4eicEkO1mL .icon-shape .label{text-align:center;}#mermaid-svg-NwQvuN4eicEkO1mL .node.clickable{cursor:pointer;}#mermaid-svg-NwQvuN4eicEkO1mL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NwQvuN4eicEkO1mL .arrowheadPath{fill:#333333;}#mermaid-svg-NwQvuN4eicEkO1mL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NwQvuN4eicEkO1mL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NwQvuN4eicEkO1mL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NwQvuN4eicEkO1mL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NwQvuN4eicEkO1mL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NwQvuN4eicEkO1mL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NwQvuN4eicEkO1mL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NwQvuN4eicEkO1mL .cluster text{fill:#333;}#mermaid-svg-NwQvuN4eicEkO1mL .cluster span{color:#333;}#mermaid-svg-NwQvuN4eicEkO1mL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NwQvuN4eicEkO1mL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NwQvuN4eicEkO1mL rect.text{fill:none;stroke-width:0;}#mermaid-svg-NwQvuN4eicEkO1mL .icon-shape,#mermaid-svg-NwQvuN4eicEkO1mL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NwQvuN4eicEkO1mL .icon-shape p,#mermaid-svg-NwQvuN4eicEkO1mL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NwQvuN4eicEkO1mL .icon-shape .label rect,#mermaid-svg-NwQvuN4eicEkO1mL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NwQvuN4eicEkO1mL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NwQvuN4eicEkO1mL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NwQvuN4eicEkO1mL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 与远端不一致
git fetch / pull / push
known_hosts 中的 GitHub 密钥
主机密钥校验失败
ssh-keygen -R github.com
git 重连,可能走 IPv6
提示 trust host,输入 yes
写入错误的非官方密钥
公钥认证失败
SSH 询问 password
GitHub 拒绝 → Permission denied
4. 为何以前正常、现在突然不行
大概率是 网络路径变化(尤其 IPv6 解析或路由),而不是本地 SSH 密钥突然失效。
- 此前
known_hosts中可能是正确或仍可用的记录。 - 路径切换后,IPv6 解析到错误端点,主机密钥对不上,触发连锁反应。
- 执行
ssh-keygen -R github.com后若 未核对官方指纹 就接受新密钥,会把错误记录写回本地。
5. 修复步骤
5.1 清理错误主机记录
bash
ssh-keygen -R github.com
ssh-keygen -R "[github.com]:443" # 若曾用 HTTPS 端口走 SSH 代理,可一并清理
5.2 写入 GitHub 官方主机公钥
从 GitHub 文档 复制官方 SSH 主机公钥 ,追加到 ~/.ssh/known_hosts(Windows 通常在 C:\Users\<用户名>\.ssh\known_hosts)。
也可在 确认网络可信 时,用官方指纹手动验证后写入;切勿 在未核对指纹的情况下对陌生提示一律 yes。
5.3 强制 GitHub 走 IPv4
编辑 ~/.ssh/config(无则新建):
sshconfig
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519
AddressFamily inet
| 配置项 | 说明 |
|---|---|
IdentityFile |
改为你实际使用的私钥路径(常见 id_ed25519 或 id_rsa) |
AddressFamily inet |
仅使用 IPv4,降低 IPv6 错误解析风险 |
5.4 验证
bash
ssh -T git@github.com
成功时类似:Hi <username>! You've successfully authenticated...
再在任意本地仓库执行:
bash
git fetch
不应再出现 git@github.com's password 提示。
5.5 若仍公钥失败
与主机密钥无关时,检查:
- GitHub 账户 Settings → SSH and GPG keys 是否已添加对应 公钥 (
.pub文件内容,非私钥)。 - 本地私钥权限是否正确(Linux/macOS:
chmod 600 ~/.ssh/id_*)。 - 远程 URL 是否为 SSH 格式:
git@github.com:owner/repo.git。
6. 经验要点
- 看到
REMOTE HOST IDENTIFICATION HAS CHANGED时,先查官方指纹 ,不要盲目yes。 git@github.com's password表示 密钥认证已失败;GitHub 不支持 SSH 密码登录。- 国内或复杂网络环境下,对 GitHub 配置
AddressFamily inet或预先写入官方known_hosts,可降低 DNS/IPv6 劫持风险。 - 私钥 (无
.pub后缀)切勿提交到仓库、粘贴到博客或发给他人;排障文档只写路径与公钥指纹即可。 - 长期可改用 HTTPS + 凭据管理器 或 SSH over 443(GitHub 文档有说明)作为备选路径。
7. 延伸阅读
| 资源 | 说明 |
|---|---|
| GitHub SSH key fingerprints | 官方主机密钥与指纹 |
| Connecting to GitHub with SSH | 生成密钥与添加公钥 |
| Using SSH over the HTTPS port | 443 端口 SSH 备选路径 |
一句话 :主机密钥与用户密钥是两道门;IPv6 连到非官方端点时,第一道门被骗、第二道门必然失败,才会出现「要密码但永远登不上」。清理 known_hosts、对齐官方指纹并强制 IPv4 后,SSH 与 git fetch 通常即可恢复。