今天早上刚坐到工位,打开终端习惯性 git pull 准备拉一手最新代码,结果一行红字直接把我整不会了:
code复制git@gitlab.com: Permission denied (publickey).
fatal: Could not read from remote repository.
昨天下午还好好的,晚上也没人动过服务器,怎么就拉不下来了?我当时的反应和大多数同事一样:先怀疑 GitLab 挂了。登网页一看,项目首页、提交记录、分支列表全正常,说明服务本身没问题。再试 SSH 协议、HTTP 协议,发现 HTTP 能通,SSH 拉取必挂。这时候才意识到,问题出在 SSH 这一层的"权限"上。
这篇就是这次排查的全过程记录。里面会有我实际敲过的命令、看过的日志、翻车的位置,以及最后定位到的根因——一个看起来毫不起眼、但杀伤力巨大的本地权限问题。如果你哪天也遇到 GitLab SSH 拉取失败,报错长得像上面这样,这篇文章应该能帮你省下一上午的排查时间。
1. 故障现场:git pull 报错时的三类典型症状
1.1 "Permission denied (publickey)" 的直接打脸
最典型、也最常见的就是这一句:
code复制$ git pull origin main
git@gitlab.com: Permission denied (publickey).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
这串报错里,真正有用的信息其实只有第一句:Permission denied (publickey)。后面两句话完全是 Git 的"兜底提示",告诉你可能没权限、可能仓库不存在,说得模棱两可。很多朋友(包括当年的我)看到 "Please make sure you have the correct access rights and the repository exists" 就去检查仓库路径、确认分支名,甚至去 GitLab 网页上看项目还在不在,方向完全跑偏了。
publickey 这个词值得拆开看。它意味着 GitLab 的 SSH 服务确实收到了你的连接请求,网络层、端口、SSH 服务本身都是通的,但认证阶段没有通过——你提供的公钥不在它的白名单里,或者你压根没把一个有效公钥提供给对方。一句话总结:连接没问题,身份没验上。
1.2 "Could not read from remote repository" 的误导性
这个报错是 Git 的一层"包装错误"。无论 SSH 底层挂掉的原因是什么——密钥没配、权限不对、主机 key 变了、网络代理劫持——Git 统一给你吐一句 fatal: Could not read from remote repository。它把真正的原因藏在前面,但很多人只记住了后半句。
code复制$ git clone git@gitlab.com:somegroup/someproject.git
Cloning into 'someproject'...
git@gitlab.com: Permission denied (publickey).
fatal: Could not read from remote repository.
注意看,真正的线索在 clone 进度下面那一行:Permission denied (publickey)。它和 Could not read from remote repository 之间是"原因和结果"的关系。以后看到这类报错,先往上看报错头部,别盯着最后一句猜。
1.3 另一类容易被误判成SSH问题的账号权限报错
还有一类报错,乍一看和 SSH 有关,其实是账号权限的事。如果你用的是 HTTP 方式,或者习惯从网页直接下载代码压缩包,报错长这样:
code复制remote: HTTP Basic: Access denied
fatal: Authentication failed for 'https://gitlab.example.com/...'
或者网页上点击 Download zip,返回一句 "You are not allowed to download code"。
这种情况跟 SSH 密钥没有任何关系,是 GitLab 账号本身对这个项目没有下载权限。在 GitLab 里,不是登录了网页就能拉所有代码。项目是 internal 或 private 时,你必须是项目成员,且角色在 Reporter 及以上,才能正常 clone 和 pull。Guest 这种角色,即使账号能登录、能看 issue,代码也拉不下来。
所以排查的第一步,永远是先分清"你是用哪种协议在拉代码"。这决定了后续要查的方向是本地 SSH 配置,还是 GitLab 账号权限。这个判断做错,后面全是无用功。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 排查链路:从握手日志一步步追到~/.ssh
2.1 先确认协议,再谈权限
我第一件事是确认仓库的 remote 地址
