能连上 GitHub(SSH 验证成功),却 push 失败?常见原因与逐步解决方案

能连上 GitHub(SSH 验证成功),却 push 失败?常见原因与逐步解决方案

在无法通过 HTTPS 或普通 Git 方式推送代码到远程仓库时,很多人会尝试改用 SSH 密钥的方式来连接与推送。本文先说明为什么网络策略或端口限制会导致无法 push、以及在什么场景下使用 SSH(尤其是将连接改为 ssh.github.com:443)可以绕过这些限制;随后分析为何有时即便 SSH 验证成功,git push 仍然失败,并给出可直接执行的解决方案与诊断命令,便于快速恢复推送能力。

一、为什么会出现"能连上但无法 push"的矛盾?

在继续分析具体原因之前,先简要说明"为什么有时改用 SSH 密钥可以绕过网络限制":

  • 端口策略:很多内网/防火墙会屏蔽外部 SSH 的默认端口 22,但允许 443(HTTPS)端口通过。将 SSH 连接指向 ssh.github.com:443,可以利用被允许的 443 端口建立 SSH 连接,从而避开对 22 端口的阻断。
  • 协议与拦截:公司代理/中间人通常针对 HTTP/HTTPS 做流量检查或凭证劫持。SSH 使用不同的协议和密钥认证,代理通常不对 SSH 做相同拦截,因此 SSH key 的认证流更难被代理截获或替换。
  • 认证方式:使用密钥认证避免了在通过代理的 HTTPS 基于表单/密码的交互,从而减少凭证被代理处理或缓存的风险。 常见场景与原因总结:
  1. 网络端口与代理策略差异
    • 许多公司封锁外部 22 端口,允许 443(HTTPS)。通过把 ssh 目标指向 ssh.github.com:443 可以通过防火墙,但这需要 SSH 客户端实际使用该端口。
  2. SSH 验证成功只是说明公钥在 GitHub 上被接受,但 git push 失败可能是因为:
    • Git 在执行 push 时没有使用你测试时用的同一把私钥(例如你用 ssh -i 测试成功,但 git 调用的 ssh 没有读取该 key)。
    • Git 调用了不同的 ssh 可执行文件(系统自带的、Git for Windows 自带的或 Putty/plink),而这些客户端对 ~/.ssh/config 或端口声明的支持不同。
    • remote URL 使用了 ssh://...:443 这类带端口的写法时,有的 Git 的 ssh 变体("simple")不支持在 URL 中带端口,会报 "ssh variant 'simple' does not support setting port"。
    • ssh-agent 未加载或私钥权限不当,导致 push 时没有合适的私钥被提供给远端(即使单次 ssh -i 可用)。
  3. 本地路径/配置格式不兼容(Windows 路径在 Git Bash 下需用 POSIX 形式),导致 Git 调用 ssh 时失败或不读取 config

结论:ssh 验证成功说明"密钥对 + GitHub"是正确的;失败通常出在"git 调用 ssh 的方式"或"remote URL 与客户端组合"的不匹配上。


二、优先的检查与调试命令(先执行这些,便于定位)

在能访问命令行的机器上按顺序执行(把输出保存/粘贴用于进一步分析):

  • 验证 SSH 单次连接(指定私钥和端口)

    • Git Bash:

      bash 复制代码
      ssh -i /c/Users/youruser/.ssh/id_ed25519_project -T -p 443 git@ssh.github.com
    • PowerShell:

      powershell 复制代码
      ssh -i C:\Users\youruser\.ssh\id_ed25519_project -T -p 443 git@ssh.github.com
  • 查看尝试密钥的详细信息(verbose)

    bash 复制代码
    ssh -vvv -i /c/Users/youruser/.ssh/id_ed25519_project -T -p 443 git@ssh.github.com
  • 检查 Git 用哪个 ssh(可能为空)

    powershell 复制代码
    git config --get core.sshCommand
    git config --global --get core.sshCommand
    git var GIT_SSH
    where.exe ssh
  • 查看远程 URL 与当前 remote

    bash 复制代码
    git remote -v
  • 在 push 时用 verbose 查看 ssh 行为(临时,不持久)

    bash 复制代码
    # Git Bash 临时
    export GIT_SSH_COMMAND="ssh -vvv -F /c/Users/youruser/.ssh/config"
    git push origin master
    unset GIT_SSH_COMMAND
    
    # PowerShell 临时
    $env:GIT_SSH_COMMAND = "ssh -vvv -F C:\Users\youruser\.ssh\config"
    git push origin master
    Remove-Item Env:\GIT_SSH_COMMAND

三、逐条可执行方案(按便利性/长期性排序)

方案 1 --- 推荐:在 ~/.ssh/config 映射 host(一次配置,透明生效)

  • C:\Users\<user>\.ssh\config 写:

    bash 复制代码
    Host github.com
      HostName ssh.github.com
      Port 443
      User git
      IdentityFile C:/Users/youruser/.ssh/id_ed25519_project
      IdentitiesOnly yes
  • 优点:你可以继续使用 git@github.com:OWNER/REPO.git 形式的 remote,SSH 会透明走 443 并用指定 key,适合大量仓库。

方案 2 --- 推荐备选:在当前会话临时指定 ssh(立刻生效,适合不想改配置时)

  • Git Bash:

    bash 复制代码
    export GIT_SSH_COMMAND="/c/Windows/System32/OpenSSH/ssh.exe -F /c/Users/youruser/.ssh/config"
    git push origin master
    unset GIT_SSH_COMMAND
  • PowerShell:

    powershell 复制代码
    $env:GIT_SSH_COMMAND='C:\Windows\System32\OpenSSH\ssh.exe -F C:\Users\youruser\.ssh\config'
    git push origin master
    Remove-Item Env:\GIT_SSH_COMMAND
  • 优点:无需改全局设置或每个仓库,适合临时使用或测试。

方案 3 --- 持久化:让 Git 永久使用指定 OpenSSH(写入 core.sshCommand)

  • 在 Git Bash(或 PowerShell)执行:

    bash 复制代码
    git config --global core.sshCommand "/c/Windows/System32/OpenSSH/ssh.exe -F /c/Users/youruser/.ssh/config"
  • 优点:一次配置后所有仓库自动使用该 ssh 客户端,适合你最终希望长期生效的场景。

方案 4 --- 把 remote 改成不带端口的 SSH(仅当前仓库)

  • 在仓库目录执行:

    bash 复制代码
    git remote set-url origin git@github.com:OWNER/REPO.git
    git push origin master
  • 说明:如果方案 1 的 ~/.ssh/config 已正确设置,这种写法会通过 config 映射到 ssh.github.com:443。适合逐仓切换的做法。

方案 5 --- 全局 URL 重写(大量仓库且 remote 多为 HTTPS)

  • https://github.com/... 自动走 ssh://git@ssh.github.com:443/...

    bash 复制代码
    git config --global url."ssh://git@ssh.github.com:443/".insteadOf "https://github.com/"
  • 优点:不需改每个仓库的 remote,适合公司内大量已有 HTTPS remote 的情形。

方案 6 --- 离线/替代推送(当所有外网被限制)

  • 使用 git bundle 在内网打包,然后在能上网的机器上 clone/bundle 并 push:

    bash 复制代码
    # 内网机器
    git bundle create ../repo.bundle --all
    # 在能上网的机器上
    git clone repo.bundle repo-from-bundle
    cd repo-from-bundle
    git remote add origin https://github.com/OWNER/REPO.git
    git push origin --all
    git push origin --tags

四、常见错误与针对性修复

  • 错误 "ssh variant 'simple' does not support setting port"

    • 原因:remote 使用了 ssh://...:PORT 形式,但 Git 内置 ssh 变体不支持 URL 里显式端口。
    • 解决:去掉 URL 中的端口,改用 git@github.com:owner/repo.git 并依赖 ~/.ssh/config 做端口映射,或设置 core.sshCommand 强制使用系统 OpenSSH。
  • 报 "Permission denied (publickey)." 在 push 时,但 ssh -i ... 验证成功

    • 原因:git push 调用了不同的 ssh(或未加载该私钥),或者 ssh 在 push 时未读取你的 config
    • 解决:用 GIT_SSH_COMMAND 指定同一 ssh 可执行文件或在 ~/.ssh/config 中声明 IdentityFile;也可把私钥放到默认位置并用 ssh-agent 加载。
  • 在 Windows 下设置 core.sshCommand 出现 "command not found" 或路径问题

    • 原因:Git Bash 解释 Windows 路径不兼容(需要 POSIX 路径 /c/Windows/...)。
    • 解决:在 Git Bash 使用 /c/Windows/System32/OpenSSH/ssh.exe 形式;在 PowerShell 使用双反斜杠转义路径。

五、安全与运维建议

  • 私钥保护:私钥必须保密,放到 ~/.ssh/ 并限制访问权限;若私钥可能泄露,立即在 GitHub 上删除对应公钥并重新生成。
  • 不要把私钥或含密信息提交到仓库。
  • 若你在公司内重复遇到网络限制,建议和网管沟通开放特定目标(例如允许 ssh.github.com:443),或申请企业级代理支持。
  • 对于长期维护,优先把常用私钥加入 ssh-agent(或 Windows OpenSSH agent),并把 agent 设置为随系统可用,避免每次都输入/设置。

六、快速决策指南(选择一个最快恢复办法)

  • 想最快解决并不改任何仓库:在当前终端临时执行 GIT_SSH_COMMAND(方案 2)。
  • 想一次性对所有仓库透明生效:写 ~/.ssh/config 并把 remote 保持 git@github.com:...(方案 1)。
  • 仓库多且为 HTTPS:使用全局 insteadOf 自动重写(方案 5)。
  • 完全无外网访问:使用 git bundle 离线转发(方案 6)。
相关推荐
豆苗学前端13 分钟前
vue3+TypeScript 实现一个图片占位符生成器
前端·面试·github
neon120414 分钟前
Vue 3 父子组件通信核心机制详解:defineProps、defineEmits 与 defineExpose 完全指南
前端·javascript·vue.js·前端框架
Juchecar31 分钟前
Vue3 开发环境搭建及循序渐进学习指南
前端·javascript
Data_Adventure1 小时前
@scqilin/phone-ui手机外观组件库
前端
一点一木1 小时前
Vue Vapor 事件机制深潜:从设计动机到源码解析
前端·vue.js·vapor
FSHOW1 小时前
记一次开源_大量SVG的高性能渲染
前端·react.js
小牛.7931 小时前
Web第二次作业
前端·javascript·css
二闹2 小时前
都2025了还要用Layui做下拉控件-我只能说你有水平
前端
Pikachu8032 小时前
揭秘 tyarn:一个为大型 TypeScript Monorepo 优化的 Yarn 性能猛兽
前端·javascript
用户49430538293802 小时前
大规模建筑自动贴图+单体化效果,cesium脚本
前端·javascript·算法