git Permission denied (publickey) 速查:权限被拒的六个根因
更新时间:2026-10-24。FAQ 速查页,面向 clone/push/pull 时被
Permission denied (publickey)一句话顶回来的同学。git 权限被拒 publickey 的含义一句话:SSH 握手阶段服务端把你出示的所有公钥都试了一遍,没有一个能对上账号——注意这是「服务端不认你的钥匙」,跟网络通不通、仓库在不在无关。收敛在六根轴上:公钥根本没部署到平台、known_hosts 里主机指纹对不上、有读权限没有写权限、.ssh目录或私钥文件权限太宽被 SSH 拒用、换了 sudo/root 身份导致 key 找不到、以及公司换过 SSH 端口或别名。认证链的完整地图(token、HTTPS 链)见 Git 认证失败速查,本篇只把 publickey 这一根的六个坑打穿。
本文要回答的问题
Permission denied (publickey)与Authentication failed的边界在哪里?- 公钥明明传过平台,为什么还是 publickey?
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!是不是同一种拒绝?- 为什么
.ssh目录权限设成 777 反而更连不上? - 只读 deploy key 拉代码没问题,一 push 就拒,差在哪?
一、报错原文与典型现象
publickey 拒绝的几种真实措辞如下(主机名以 git.example.com 占位,路径以 /proj/demo 占位):
$ git clone git@git.example.com:zhangsan/demo.git /proj/demo
Cloning into '/proj/demo'...
git@git.example.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 -T git@git.example.com
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Offending ECDSA key in /home/zhangsan/.ssh/known_hosts:12
Host key verification failed.$ ssh -T git@git.example.com
Bad permissions. Try removing permissions for user: DOMAIN\\Users on file C:/Users/zhangsan/.ssh/id_ed25519
git@git.example.com: Permission denied (publickey).读报错抓三个信号:Permission denied (publickey) 是认证失败,钥匙不匹配;Host key verification failed 是信任失败,客户端不敢连——两者常被混为一谈,处置方向完全不同;Bad permissions 是 SSH 客户端在本地就把你的私钥拒用了,根本没走到服务端。
二、病因处置速查表
| 报错现象 / 场景 | 可能原因 | 快速验证 | 处置 |
|---|---|---|---|
| 新机器首次连接即 publickey | 公钥没生成或没上传平台 | ls ~/.ssh/*.pub;平台 SSH Keys 页 | keygen 生成、.pub 全文粘到平台 |
| 同一把 key 别人能用我不能用 | 传的是私钥或粘贴截断 | 平台指纹与 ssh-keygen -lf 对比 | 重新粘完整 .pub 单行 |
| 报 REMOTE HOST IDENTIFICATION HAS CHANGED | 服务器重装/换机,known_hosts 旧指纹在拦 | 报错行给出 known_hosts 行号 | ssh-keygen -R git.example.com 清旧记录 |
| 拉代码可以、push 被拒 | deploy key 只读或成员权限是 Reporter | 平台仓库成员页/deploy key 的 Write 勾选 | 提权或换有写权限的 key |
| Bad permissions / UNPROTECTED PRIVATE KEY FILE | 私钥或 .ssh 目录权限过宽 | ls -ld ~/.ssh ~/.ssh/id_* | 目录 700、私钥 600 |
| sudo 之后 publickey | root 身份读的是 /root/.ssh,没你的 key | sudo ssh -T 与不带 sudo 对比 | 别用 sudo 跑 git;修目录属主 |
| 突然全部 publickey | 平台侧 key 被删/过期,或账号被封 | 平台后台看 key 列表与账号状态 | 重传 key 或联系管理员 |
三、排查决策图
下面这张图是标准排查路线,颜色按角色区分:红是报错入口与命中的根因,蓝是排查动作,绿是通过分支,橙是修复动作,紫是防复发沉淀。
读这张图抓第一个分岔:报错文案里带 Host key verification failed 的,问题在客户端的 known_hosts,一行 ssh-keygen -R 就能走;只有纯 Permission denied (publickey) 才进「服务端为什么不认我的 key」这条长链。很多人把两者当成一回事,在 known_hosts 问题上白配了半天 key。
四、逐条排查展开
第一条:公钥没部署——占这类报错的七成。 服务端认 key 的逻辑很朴素:平台数据库里存着你账号名下的公钥列表,握手时客户端出示哪把就比对哪把,全不在列表里就 publickey。所以第一步永远是两头对:
ls ~/.ssh/id_ed25519.pub ~/.ssh/id_rsa.pub 2>/dev/null # 本机有没有公钥
ssh-keygen -lf ~/.ssh/id_ed25519.pub # 算出本机公钥指纹把指纹和平台「SSH Keys」设置页里显示的指纹对一眼——平台只显示指纹和后缀注释,不等长即没传对。没有 key 就 ssh-keygen -t ed25519 -C "you@example.com",把 .pub 文件完整单行粘进平台(带 ssh-ed25519 前缀、带注释后缀,中间不断行)。新手常见翻车:把私钥 id_ed25519(没有 .pub 后缀的那个)粘了上去、或者复制时被编辑器折行截断。
第二条:known_hosts 变更——不是 publickey,但长得最像。 服务器重装系统、平台换宿主机后,主机指纹变了,你本地 known_hosts 里存的旧指纹对不上,SSH 直接拒绝继续,报 REMOTE HOST IDENTIFICATION HAS CHANGED! 和 Host key verification failed。这是中间人攻击防护在正常工作,确认平台确实迁移/重装过(官网公告、问管理员)之后清掉旧记录:
ssh-keygen -R git.example.com # 删掉 known_hosts 里的旧指纹
ssh -T git@git.example.com # 重新连接,yes 接受新指纹如果平台没有任何变更公告就弹这个警告,先别急着删——找管理员核实是不是真有中间人在捣鬼,这条警告的设计意义就在于此。
第三条:有读没写——deploy key 与成员权限。 「clone 好好的,push 就拒」是这条的典型指纹。GitLab 的 deploy key 默认只读,推送必须勾 Write;GitHub 的 deploy key 同样有 Allow write access 开关;成员角色是 Reporter/Guest 的也只有读。验证办法是 git ls-remote origin(只读操作)能通而 git push --dry-run 被拒,就去平台仓库的 Members / Deploy keys 页确认写权限。注意还有一种写法上的迷惑:服务端拒写时部分平台的报错是 Permission denied 不带 publickey 字样,或 You are not allowed to push code to this project,性质相同。
第四条:文件系统权限过宽,SSH 客户端拒用私钥。 OpenSSH 有硬性安全策略:私钥文件如果组用户/其他人可读,客户端直接弃用并报 UNPROTECTED PRIVATE KEY FILE(Windows 上是 Bad permissions),于是握手时一把 key 都递不出去,服务端回 publickey。标准权限:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub ~/.ssh/known_hosts ~/.ssh/configWindows 上要对私钥文件「属性 → 安全」里只留当前用户,删掉 Users/Everyone 的继承权限。这条的高发场景是 sudo cp 搬过 home 目录、或从别的机器 tar 解过来,权限位跟着变了。
第五条:sudo/root 身份切换丢 key。 sudo git clone 时进程身份是 root,读的是 /root/.ssh/——那里没有你的 key,必然 publickey。而且 sudo clone 下来的仓库属主是 root,你之后普通身份 git pull 又会撞文件权限错误,一坑带一坑。正确姿势:git 操作一律用自己的身份跑;如果是目录权限逼着你想用 sudo,真正该修的是目录属主 sudo chown -R zhangsan:zhangsan /proj/demo。同理,su - 切到别的用户、docker 容器内操作宿主机挂进来的仓库,都要先想「当前身份读的是谁的 .ssh」。
第六条:key 在 agent 与 config 里的迷航。 本机有多把 key 时,SSH 按默认顺序逐把试,服务端试到上限(通常 6 次)就报 publickey——ssh -v 输出里能看到 Offering public key 一行行尝试。确认你的目标 key 已被出示:没有就 ssh-add ~/.ssh/id_ed25519,多账号机器用 ~/.ssh/config 的 Host 别名加 IdentitiesOnly yes 绑死(写法见 Git 认证失败速查)。另外公司环境把 SSH 挪到非标端口时,config 里要配 Port,端口不对的表现是连接层报错而非 publickey,别混。
第七条:平台侧的「突然死亡」。 昨天还好好的,今天全线 publickey,本机什么都没动过——去平台后台看:key 可能被管理员清理、可能被设置了有效期已过期、账号本身可能被停用。CI 里用的 deploy key 被误删是这条的经典剧本。本机 ssh -v 只能看到服务端拒绝,看不到理由,理由永远要在平台后台找。
五、规范做法与预防
- 新机器三件套:
ssh-keygen→ 传.pub→ssh -T回显正确账号名再干活,把 publickey 消灭在第一公里。 - 权限位当纪律:
.ssh一律 700、私钥一律 600,凡是sudo cp/ tar 搬家之后第一件事查ls -ld。 - git 不碰 sudo:权限问题修目录属主,不用 sudo 跑 git;CI 与服务器上为 git 建专用账号并配好它自己的 key。
- 指纹变更先核实再清除:known_hosts 警告先找官方变更公告,确认是平台行为再
ssh-keygen -R,养成习惯就是安全习惯。 - deploy key 最小权限:CI 只读场景不给 Write,需要推送的机器人单独建 key 并登记用途与负责人。
这一篇在知识体系里的位置
本篇是 Git 排错速查簇的 SSH 权限篇,定位「服务端不认你的钥匙」:HTTPS 链与 token/credential 缓存问题看 Git 认证失败速查;连得上但推不进看 Git push 被拒 non-fast-forward 速查;路径名层面的报错看 pathspec did not match 速查;clone/fetch 挂起无响应看 git clone 卡死超时速查;remote 地址与协议选择看 remote、pull、push 用法;建仓与初始环境看 Git 配置、init 与 clone。
一句话总结
git 权限被拒 publickey 先分清报错是 Permission denied (publickey)(服务端不认 key)还是 Host key verification failed(known_hosts 旧指纹,确认平台变更后 ssh-keygen -R 清掉):前者按「本机有 .pub → 平台指纹对得上 → 私钥权限 700/600 没被拒用 → ssh-add -l 已挂 → 身份不是 sudo/root」五步走,写不进仓库再查 deploy key 的 Write 开关与成员权限。新机器 ssh -T 当体检、git 永不 sudo、私钥权限当纪律,这个报错基本绝迹。