三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

解决GitHub SSH连接失败:Host key verification failed的完整指南

解决GitHub SSH连接失败:Host key verification failed的完整指南

1. 问题初探:为什么GitHub会“认不出”你的电脑?

当你满心欢喜地敲下git clone或者git push,准备和远在千里之外的GitHub仓库来一次亲密接触时,终端却冷冰冰地抛出一句Host key verification failed.,这种感觉就像你兴冲冲地去朋友家敲门,结果对方从猫眼里看了你一眼,说“我不认识你,不能开门”。这个错误的核心,是SSH协议在“认人”这件事上出了岔子。

简单来说,SSH(Secure Shell)是一种加密的网络传输协议,它不光管加密通信,还管身份验证。这里的身份是双向的:你的电脑要验证GitHub服务器是不是“真身”(防止中间人攻击),GitHub服务器也要验证你的电脑有没有权限访问。Host key verification failed这个错误,就发生在第一步——你的电脑无法确认它正在连接的服务器是不是真正的GitHub。

你的电脑里有一个“通讯录”,叫做known_hosts文件(通常位于~/.ssh/known_hosts)。里面记录了所有你曾经连接过的远程服务器的“指纹”(即主机密钥)。当你第一次连接GitHub时,你的SSH客户端会收到GitHub服务器的公钥,并询问你是否信任这个指纹。如果你选择了“是”,这个指纹就会被记录在known_hosts里。下次再连接时,客户端会比对收到的指纹和记录的是否一致。如果不一致,它就会果断拒绝连接,并抛出我们看到的这个错误,因为它怀疑可能有“冒牌货”(恶意服务器)在中间捣鬼。

那么,为什么之前好好的,突然就“不一致”了呢?常见的原因有几个:一是GitHub的服务器IP地址发生了变更(云服务商扩容、维护等都会导致),对应的主机密钥也可能更新;二是你的known_hosts文件里的记录因为某些操作(如误删、文件损坏)变得不正确或过时了;三是在某些网络环境下(如公司代理、校园网),流量被中间设备拦截或改写,导致指纹比对失败。搞清楚了“为什么”,我们才能对症下药,而不是病急乱投医。

2. 核心思路:从“删除记录”到“主动信任”

遇到这个问题,网上最常见的解决方案是:“删掉known_hosts文件里对应GitHub的那行记录”。这方法简单粗暴,往往也有效。因为删除后,下次连接时SSH客户端会发现没有记录,就会再次询问你是否信任新的指纹,你回答“yes”,新指纹就被记录进去,连接就恢复了。

但这是一种“被动解决”的思路,相当于把朋友从通讯录里删了,等他下次打电话来你再重新存一遍。它没有去探究为什么指纹会变,而且如果问题根源是网络中间人攻击,盲目信任新指纹会带来安全风险。更稳妥、更专业的思路,应该是“主动验证与更新”。

我们需要主动去获取GitHub官方公布的、正确的SSH主机密钥指纹,然后与我们本地记录或当前连接收到的指纹进行比对。如果确认是GitHub官方更新了密钥,我们就手动更新本地的记录;如果指纹对不上官方公布的,那就要警惕网络环境是否安全。这个主动验证的过程,才是从根本上解决问题并保证安全性的方法。接下来,我们就围绕这个核心思路,展开一系列具体操作。

2.1 理解SSH主机密钥与指纹

在动手之前,我们花两分钟把核心概念捋清楚,这能帮你更好地理解每一步操作的意义。

主机密钥对:GitHub的服务器上存有一对非对称加密密钥,分为私钥和公钥。私钥绝对保密,存放在服务器上;公钥则可以公开分发。当你的客户端连接时,服务器会发送它的公钥。

指纹:公钥本身是一长串字符,不方便阅读和比对。因此,我们通常使用其“指纹”。指纹是通过对公钥进行哈希运算(通常是SHA-256)生成的一串较短的、唯一的字符串,类似于公钥的“摘要”或“身份证号”。比对指纹比直接比对公钥方便得多。

known_hosts文件格式:这个文件里的每一行都记录了一个你信任的主机。一行的基本格式是这样的:[hostname] [key_type] [public_key]。例如,一条旧的记录可能是github.com ssh-rsa AAAAB3NzaC1yc2E...。当连接github.com时,客户端就会用这一行里的[public_key]去计算指纹,并与服务器发来的公钥计算的指纹做比对。

所以,我们解决问题的关键操作,无论是删除还是更新,本质上都是在维护这个known_hosts文件内容的正确性。

3. 实操方案一:快速修复——清除旧记录

这是最快速的解决方法,适用于你确认当前网络环境安全(比如在家用自己的网络),并且只是急于恢复连接的情况。

操作步骤:

  1. 定位并编辑 known_hosts 文件。 打开终端,使用文本编辑器打开这个文件。我习惯用nano,因为它简单:

    nano ~/.ssh/known_hosts
  2. 找到并删除 GitHub 相关条目。 文件打开后,你可以看到很多行。你需要找到所有包含github.com的行。它们可能不止一条,因为可能记录了不同端口或IP地址。仔细查找,将所有这些行都删除。在nano编辑器里,你可以用Ctrl+K来剪切(删除)当前行。删除所有相关行后,按Ctrl+O保存,再按Ctrl+X退出。

    注意:在操作前,你可以先备份一下这个文件,命令是cp ~/.ssh/known_hosts ~/.ssh/known_hosts.backup。这样万一误删了其他重要记录,还能恢复。

  3. 重新尝试连接。 保存退出后,再次执行你的 Git 操作,例如:

    ssh -T git@github.com

    或者直接进行git clone。 这时,客户端因为找不到记录,会提示类似以下的信息:

    The authenticity of host 'github.com (20.205.243.166)' can't be established. ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU. This key is not known by any other names. Are you sure you want to continue connecting (yes/no/[fingerprint])?
  4. 验证并接受新指纹。 这里就是关键了!在输入yes之前,我们应该先验证这个指纹是否属于真正的GitHub。但现在我们为了快速解决,可以先输入yes。连接成功后,这个新的指纹就会被写入known_hosts文件。

方案一的优缺点与风险:

  • 优点:极其简单快速,几乎能立即解决因服务器IP/密钥变更导致的问题。
  • 缺点与风险:这是一种“盲目信任”。如果这个提示是因为你处于不安全的网络(如公共Wi-Fi),有恶意服务器在冒充GitHub,那么你输入yes就等于把家门钥匙交给了骗子。所以,这种方法仅在你完全信任当前网络环境时使用。

4. 实操方案二:安全之道——验证并更新指纹

这是推荐的做法,尤其在公司、咖啡馆等公共或不完全信任的网络环境下。我们需要获取GitHub官方的指纹来比对。

操作步骤:

  1. 获取GitHub官方公布的SSH密钥指纹。 GitHub非常贴心地在其官方文档中公布了他们SSH主机密钥的指纹。你可以通过以下命令之一快速获取(这些域名和IP是GitHub官方公布的):

    # 方法1:通过ssh-keyscan获取github.com的公钥,并计算指纹 ssh-keyscan github.com | ssh-keygen -lf -

    或者,更直接地,访问GitHub的官方文档页面(在搜索引擎搜索“GitHub SSH host keys”即可找到),上面会列出最新的指纹。截至我知识更新时,GitHub的主要指纹如下(请务必以官网最新信息为准):

    • RSA:SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8
    • ECDSA:SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM
    • ED25519:SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU
  2. 比对连接时收到的指纹。 当你尝试连接,收到那个“The authenticity of host ... can‘t be established”的提示时,里面就包含了服务器发来的公钥指纹。比如上面例子中的SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU。 仔细比对这个指纹和你在GitHub官网上找到的对应密钥类型的指纹是否完全一致。区分RSA、ECDSA、ED25519这几种类型。

  3. 如果一致,放心输入yes。 如果完全一致,说明你连接的就是真正的GitHub服务器,可以安全地输入yes。新的正确记录会被写入known_hosts

  4. 如果不一致,立即停止!如果指纹对不上,千万不要输入yes!这很可能意味着你遇到了中间人攻击,或者DNS被劫持,连接到了一个假冒的服务器。此时应检查你的网络,或者换一个安全的网络(如手机热点)再试。

如何手动更新 known_hosts 文件(进阶)?如果你知道GitHub的密钥已经更新,想直接手动写入正确的记录,避免下次连接时再提示,可以这样做:

# 先删除旧的github.com记录(可选,但建议) ssh-keygen -R github.com # 使用ssh-keyscan获取最新的公钥,并追加到known_hosts文件 ssh-keyscan github.com >> ~/.ssh/known_hosts

ssh-keygen -R hostname这个命令非常有用,它能安全地移除known_hosts文件中指定主机的所有条目。

5. 深度排查:当上述方法都失效时

有时候,即使更新了known_hosts,问题依然存在。这时候就需要进行更深层次的排查。

5.1 检查网络代理与防火墙设置

如果你的电脑配置了HTTP/HTTPS代理(比如http_proxy,https_proxy环境变量),请注意,SSH连接通常走这些代理。SSH有自己独立的代理配置,在~/.ssh/config文件中。

  1. 检查 SSH 配置

    cat ~/.ssh/config

    查看是否有针对github.com或全局的ProxyCommand配置。例如,常见的通过ncconnect走HTTP代理的配置可能会引起问题。你可以暂时注释掉相关配置(在行首加#)来测试。

  2. 检查防火墙和杀毒软件:有些企业防火墙或个人安全软件会深度检查甚至干扰SSH流量,导致握手失败。尝试暂时禁用防火墙或安全软件(测试后请记得恢复),看问题是否解决。

  3. 使用详细模式调试:在ssh命令后加上-vvv参数,可以输出最详细的调试信息。

    ssh -Tvvv git@github.com

    在输出的海量信息中,重点关注在认证步骤之前的部分,寻找是否有“host key verification failed”的具体原因,或者连接在哪一步被拒绝。这对于排查网络层面的问题非常有帮助。

5.2 处理 known_hosts 文件权限与格式问题

known_hosts文件本身或~/.ssh目录的权限不正确,也可能导致SSH客户端无法正常读取或写入,从而引发各种诡异问题。

  1. 检查目录和文件权限: SSH协议对权限非常敏感。正确的权限应该是:

    • ~/.ssh目录权限为700(drwx------)
    • ~/.ssh/known_hosts文件权限为600(-rw-------)
    • ~/.ssh/id_rsa(私钥) 文件权限为600

    你可以用以下命令修复:

    chmod 700 ~/.ssh chmod 600 ~/.ssh/known_hosts chmod 600 ~/.ssh/id_rsa # 如果你的私钥文件是这个名字
  2. 检查文件格式与损坏: 如果known_hosts文件格式错乱(例如,在编辑时不小心加入了多余的空格、换行),也可能导致解析失败。一个排查方法是,将known_hosts文件移走,然后重新连接生成一个新的。

    mv ~/.ssh/known_hosts ~/.ssh/known_hosts.old

    然后再次尝试连接GitHub。如果问题解决,说明旧文件确实有问题。你可以对比新旧文件,或者直接使用新文件。

5.3 应对GitHub服务器IP变更

GitHub使用内容分发网络(CDN),其IP地址池可能会发生变化。虽然主机密钥通常绑定域名,但极端情况下,DNS解析到一个全新的、你本地known_hosts文件里没有记录过的IP地址,也可能需要重新验证。

  1. 刷新本地DNS缓存

    • macOS:sudo killall -HUP mDNSResponder
    • Linux(systemd-resolved):sudo systemd-resolve --flush-caches
    • Windows(命令提示符管理员模式):ipconfig /flushdns
  2. 直接使用域名而非IP:确保你在Git操作中使用的远程地址是git@github.com:username/repo.git这种形式,而不是直接使用某个IP地址。这样,SSH会使用github.com作为主机名来查找known_hosts记录,稳定性更高。

6. 防患于未然:最佳实践与配置建议

解决问题固然重要,但建立好的习惯更能让你一劳永逸。

  1. 使用 SSH 配置文件 (~/.ssh/config): 为GitHub创建一个专门的配置,可以简化操作,并集中管理参数。在~/.ssh/config文件中添加:

    Host github.com HostName github.com User git IdentityFile ~/.ssh/id_ed25519 # 指定你的私钥文件,推荐使用Ed25519算法 IdentitiesOnly yes # 如果需要配置代理,可以在这里设置,例如: # ProxyCommand nc -X connect -x proxy.company.com:8080 %h %p

    这样配置后,你只需要用ssh github.com就能连接,并且会自动使用指定的私钥。

  2. 定期验证主机密钥: 养成习惯,每隔一段时间(比如半年),或者当GitHub官方发布通告说更新了SSH主机密钥时,主动用ssh-keyscan获取指纹并与官网核对。你可以写一个简单的脚本来做这件事。

  3. 理解并接受“Host key verification failed”的价值: 这个错误不是一个Bug,而是一个至关重要的安全特性。它保护你免受中间人攻击。每次看到这个错误,都应该把它当作一次安全审计的机会,而不是一个恼人的障碍。

  4. 考虑使用 HTTPS 协议作为备选: 如果你在SSH问题上花费了太多时间,且只是进行简单的克隆、拉取、推送操作(不需要SSH密钥的复杂功能),可以考虑将远程仓库的URL切换到HTTPS格式。

    git remote set-url origin https://github.com/username/repository.git

    HTTPS方式会使用你的GitHub账号密码(或个人访问令牌)进行认证,完全绕开了SSH主机密钥验证这一层。但请注意,对于需要自动化脚本的场景,SSH密钥仍然是更安全、更便捷的选择。

7. 常见问题速查与精讲

这里汇总了在解决Host key verification failed过程中,你可能会遇到的其他连带问题或疑惑。

Q1: 执行ssh-keygen -R github.com后,再次连接依然失败,提示“Offending RSA key in /Users/xxx/.ssh/known_hosts:12”?A1: 这个提示说明在known_hosts文件的第12行,有一个错误的RSA密钥记录。ssh-keygen -R通常能删除所有相关记录,但如果文件格式混乱或有多个重复条目,它可能没删干净。最好的办法是直接打开known_hosts文件,手动删除所有包含github.com的行(以及可能包含其IP地址的行),然后保存。再重新连接。

Q2: 公司网络,必须使用代理,SSH怎么配置?A2: 这需要配置SSH的ProxyCommand。具体命令取决于你使用的代理类型。例如,对于HTTP/HTTPS代理,可以使用nc(netcat) 或connect工具。在你的~/.ssh/config中为github.com添加:

Host github.com HostName github.com User git ProxyCommand connect -H proxy_host:proxy_port %h %p # 或者使用 nc # ProxyCommand nc -X connect -x proxy_host:proxy_port %h %p

你需要将proxy_hostproxy_port替换成公司代理的实际地址和端口。connect工具可能需要单独安装(如macOS的brew install connect)。

Q3: 错误信息里出现了ECDSAED25519RSA,我该信任哪一个?A3: 现代GitHub服务器主要使用ED25519ECDSA密钥,它们比传统的RSA更安全、更高效。在连接提示中,SSH客户端会列出它从服务器收到的所有类型的公钥指纹。你应该核对所有类型的指纹,只要其中一种(特别是ED25519)与GitHub官方公布的匹配,就可以信任。GitHub官方文档通常会列出所有类型的指纹供你核对。

Q4: 除了github.com,还有其他Git托管平台(如GitLab、Gitee)出现同样问题怎么办?A4: 解决思路完全一样。核心步骤都是:1. 从该平台的官方文档获取其公布的SSH主机密钥指纹。2. 比对连接时收到的指纹。3. 决定是更新还是拒绝。只需把“github.com”替换成对应平台的主机名(如gitlab.comgitee.com)即可。同样,使用ssh-keygen -R gitlab.com来清除旧记录。

Q5: 在自动化脚本(如CI/CD流水线)中遇到此问题如何解决?A5: 在无人值守的自动化环境中,不能交互式地输入“yes”。有几种安全做法:

  • 预置已知主机:在构建镜像或运行环境初始化时,提前将正确的主机密钥写入known_hosts文件。可以使用ssh-keyscan命令:
    ssh-keyscan github.com >> ~/.ssh/known_hosts
  • 禁用严格主机密钥检查(不推荐,有风险):通过设置环境变量或SSH配置,但这会降低安全性,仅在最受控的内部环境中考虑。
    export GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no"
    或者修改~/.ssh/config
    Host * StrictHostKeyChecking no
    警告:这会使得SSH自动接受任何主机密钥,容易遭受中间人攻击,请谨慎评估风险。

处理Host key verification failed的过程,本质上是一次对网络安全基础的实践。它提醒我们,在便捷与安全之间,需要保持警惕并采取正确的操作。掌握上述方法后,你不仅能快速解决这个问题,更能深入理解SSH协议的工作机制,在未来遇到类似网络身份验证问题时,都能从容应对。

← 返回列表