跳到主要内容
DevOps2026-09-086 分钟阅读

Permission denied (publickey) 怎么解决?Git 推送失败的 6 种原因与修复

现象:git 操作直接被拒

git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

一行结论:SSH 认证失败——服务器不认识你提供的公钥。这个错误和密码无关(publickey 方式不用密码),是「你手里的钥匙」和「服务器登记的锁」对不上,或者你根本没递出钥匙。

先做一件事:ssh -T 直接测认证

绕过 git,直接测 SSH 通道,能拿到最干净的错误信息:

ssh -T git@github.com
# 成功:Hi yourname! You've successfully authenticated...
# 失败:git@github.com: Permission denied (publickey).

# 加 -v 看详细过程:尝试了哪些密钥、服务器接受哪些认证方式
ssh -vT git@github.com 2>&1 | grep -E 'Offering|identity file|denied'

-v 输出里的 Offering public key: /Users/you/.ssh/id_xxx 是关键——它列出的是实际递出去的钥匙。如果列表里没有你以为在用的那把,直接跳到原因 2。

六种原因,按概率排序

1. 本机没有密钥(新电脑最常见)

ls ~/.ssh/
# 有 id_ed25519 / id_ed25519.pub 或 id_rsa / id_rsa.pub → 已有
# 什么都没有 → 生成一把
ssh-keygen -t ed25519 -C "your_email@example.com"
# 一路回车(或设置口令),然后启动 agent 并加载
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

生成后把 .pub 文件内容(只有公钥可以外发,私钥永远不离开本机)添加到 GitHub:Settings → SSH and GPG keys → New SSH key。

权限要求顺带一提:~/.ssh 目录必须是 700、私钥必须是 600(组/其他人可读会被 OpenSSH 直接拒用,报 unprotected private key file)。记不准权限数字时可用 chmod 权限计算器 勾选后复制命令。

2. 密钥在,但 agent 没加载

ssh-add -l
# "The agent has no identities." → 没加载
ssh-add ~/.ssh/id_ed25519

macOS 可以把密钥信息写进 ~/.ssh/config 免去每次手动 add:

Host github.com
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_ed25519

3. 公钥没加到平台(或加错了账号)

GitHub 的公钥是账号级的:加在 A 账号下的钥匙,推 B 账号的仓库当然被拒。还有一种情况:同一把公钥已经绑在另一个 GitHub 账号上——GitHub 不允许同一公钥绑定多个账号,部署密钥同理。多账号场景见原因 4。

公司 GitLab / Gitee / 自建 Git 服务器各有各的公钥添加入口,确认你加对了平台。

4. 多账号/多密钥:递出去的不是那把钥匙

本机有公司钥匙 + 个人钥匙时,SSH 默认按顺序尝试,经常递错。用 ~/.ssh/config 按_host_ 别名路由:

# 个人 GitHub
Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_personal
  IdentitiesOnly yes

# 公司 GitHub 账号走别名
Host github-work
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_work
  IdentitiesOnly yes

IdentitiesOnly yes 是关键——强制只用指定的钥匙,不把其他钥匙都递一遍。然后 remote URL 用别名:

git remote set-url origin git@github-work:company/repo.git

5. Deploy key 的权限边界

仓库 Settings → Deploy keys 添加的公钥只对当前仓库有效:用它 clone 别的仓库、或想 push 但添加时没勾选 write access,都会 publickey 被拒。CI 机器上常见的坑:一台 runner 用同一把 deploy key 想拉多个仓库——用 organization 级的 Deploy keys 或 machine user 账号代替。

6. remote URL 写错(https 和 ssh 混着用)

git remote -v
# origin  git@github.com:user/repo.git     → 走 SSH,用本文方法排查
# origin  https://github.com/user/repo.git → 走 HTTPS,报错会要求密码/token,不是 publickey

大小写、组织名拼写、.git 结尾缺失都会导致「仓库不存在」和「认证失败」混淆。另外 GitHub 组织开启了 SSO 的,个人钥匙还需要对该组织授权一次(SSO → Authorize)。

为什么有时密码登录也被提示 publickey?

错误括号里的 publickey 是服务器允许的认证方式列表。如果显示 Permission denied (publickey,password),说明两种方式都试过且都失败了——SSH 会按服务端允许的方式依次尝试,全挂了才报错。看括号内容能知道服务端开了哪些门。

Windows OpenSSH:同一协议,两套 agent

Git for Windows 自带 OpenSSH;Windows 10/11 也有系统 OpenSSH。混用是经典「幽灵公钥」:ssh-add 进了 A agent,git 却调用 B 二进制。核对:

where.exe ssh
Get-Service ssh-agent   # 需要时启动并设为 Automatic
ssh-add -l

让 Git 固定一套(例如 git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"),密钥加载进同一 agent。Pageant/PuTTY 的钥匙对 OpenSSH 默认不可见——优先统一到一种 agent。

Host key 变更 ≠ publickey 拒绝

REMOTE HOST IDENTIFICATION HAS CHANGED 不是公钥认证失败——是服务器主机密钥与 ~/.ssh/known_hosts 对不上。机器重建或 IP 复用后,只有在确认新指纹可信时才 ssh-keygen -R hostname 删旧行。把两者搞混会白折腾:改用户密钥永远清不掉 host key 冲突。

Agent 转发:方便与爆炸半径

ssh -A / ForwardAgent yes 让跳板机用你本机钥匙连下一跳。对堡垒机很方便;共享跳板被攻破时,对方可向你的 agent 要签名。更稳的做法是 ProxyJump + 跳板上的专用 deploy key,或短时证书。若 -v 在第二跳出现你没料到的 Offering,先查是否开了转发。

密钥类型预期(优先 ed25519)

多数平台已接受 ssh-ed25519;少数老设备仍要 ≥3072 位的 rsa。若 -v 显示客户端在递 ed25519、服务端直接关掉且未试 RSA,为该主机单独生成 RSA,并用 IdentityFile + IdentitiesOnly yes 钉死——不要为了一台老机器削弱全局默认。硬件密钥(ed25519-sk)登记后行为与普通密钥相同(未加载 / 未登记),另加「没碰 FIDO」这一失败态。

排查清单

  1. ssh -T git@github.com — 直连测试,错误信息最干净
  2. ssh -vT ... | grep Offering — 实际递出了哪把钥匙?
  3. ls ~/.ssh/ + ssh-add -l — 钥匙存在吗?加载了吗?
  4. 公钥加在目标平台、目标账号(或仓库 deploy key)上了吗?
  5. 多把钥匙:~/.ssh/config 分 Host 别名 + IdentitiesOnly yes
  6. git remote -v — URL 拼写、协议(ssh vs https)核对
  7. Windows:统一 OpenSSH 二进制与 agent;先排除 known_hosts 主机密钥错误,再追用户公钥

排查注记

全文第一步不是查 git 而是绕开它——ssh -T 直接测认证通道,能拿到最干净的错误信息;-v 输出里的 Offering 行是诊断核心,它列出的是实际递出去的钥匙,列表里没有你以为在用的那把,直接跳去多密钥路由那条。多账号场景里 IdentitiesOnly yes 被标成关键,就是为了让 SSH 不把所有钥匙挨个递一遍。Host key 变更单独澄清不是公钥认证失败——改用户密钥永远清不掉 known_hosts 的冲突,两件事搞混会白折腾一场。


感谢阅读这篇 ToolVault 工具匣 技术文章。相关工具:Linux 命令速查、密码生成器、JWT 解码、chmod 权限计算器。相关阅读:npm 网络问题换源指南、端口被占用排查。访问 首页 查看更多开发者工具。