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」这一失败态。
排查清单
ssh -T git@github.com— 直连测试,错误信息最干净ssh -vT ... | grep Offering— 实际递出了哪把钥匙?ls ~/.ssh/+ssh-add -l— 钥匙存在吗?加载了吗?- 公钥加在目标平台、目标账号(或仓库 deploy key)上了吗?
- 多把钥匙:
~/.ssh/config分 Host 别名 +IdentitiesOnly yes git remote -v— URL 拼写、协议(ssh vs https)核对- Windows:统一 OpenSSH 二进制与 agent;先排除 known_hosts 主机密钥错误,再追用户公钥
排查注记
全文第一步不是查 git 而是绕开它——ssh -T 直接测认证通道,能拿到最干净的错误信息;-v 输出里的 Offering 行是诊断核心,它列出的是实际递出去的钥匙,列表里没有你以为在用的那把,直接跳去多密钥路由那条。多账号场景里 IdentitiesOnly yes 被标成关键,就是为了让 SSH 不把所有钥匙挨个递一遍。Host key 变更单独澄清不是公钥认证失败——改用户密钥永远清不掉 known_hosts 的冲突,两件事搞混会白折腾一场。
感谢阅读这篇 ToolVault 工具匣 技术文章。相关工具:Linux 命令速查、密码生成器、JWT 解码、chmod 权限计算器。相关阅读:npm 网络问题换源指南、端口被占用排查。访问 首页 查看更多开发者工具。
相关工具
相关文章
Linux crontab 定时任务实战:命令、日志与排查
crontab 不只是写表达式:三个必会命令、/etc/cron.d 与用户 crontab 的区别、任务没跑的五步排查法、PATH/时区/% 转义、MAILTO,以及秒级任务的正确解法。
docker run 转 docker-compose 完全指南:为什么转、怎么转、参数对照表
把 docker run 命令迁移到 docker-compose.yml 的完整指南:compose 相比裸 run 的五大优势、参数逐项对照表(端口/卷/环境变量/重启策略/网络)、常见坑(裸 -e 透传、匿名卷、--rm 语义),附在线转换器用法。
npm ERR! ERESOLVE:peer dependency 冲突的四种解决策略
npm install 报 ERESOLVE unable to resolve dependency tree 怎么办?理解 peer dependency 的设计意图,掌握四种解决策略(版本修复 / legacy-peer-deps / overrides / dedupe)及其适用场景与风险。