Gerrit SSH 连接测试与代码拉取:从密钥配置到 clone 全流程排错 1. 为什么要先测 SSH 连接再动手拉代码很多人拿到 Gerrit 地址后的第一反应是直接把git clone粘进终端结果卡在密码输入、卡在Permission denied (publickey)、卡在Connection refused然后开始怀疑仓库地址错了、网络不通、权限没开。我在实际带人的过程中发现九成的拉不下来代码其实在 SSH 这一层就已经能定位只是大家跳过了这个前置校验步骤。Gerrit 的代码拉取走的是 SSH 协议它不像普通的公开仓库那样匿名可读你的身份凭证完全依赖一对本地私钥和你在 Gerrit 账号里登记的公钥是否匹配。这条链路上一共有四个环节本机私钥存在且权限正确、SSH 客户端能正确选到这把私钥、网络能连到 Gerrit 的 SSH 端口、Gerrit 侧认可这把公钥对应的账号。任何一环断了git clone都会以一个很含糊的报错结束。所以标题里说的测试 SSH 连接并下载代码本质上是一套分层验证的方法论先用最低成本的方式确认身份和通道都通了再去做耗时的仓库克隆。这么做有两个非常现实的好处。第一排错效率天差地别。git clone失败时你面对的是一个复合错误而ssh -T或ssh -p 端口 主机失败时你能看到明确的拒绝原因比如是密钥没被接受Permission denied (publickey)还是端口根本连不上Connection timed out还是主机名解析失败Could not resolve hostname。这三种错误的排查方向完全不同混在一起看就只能瞎猜。第二避免反复输入密码或者被反复拒绝导致账号被临时限制。有些 Gerrit 实例对认证失败次数是有阈值的连续失败多次会锁定一段时间这时候你再去调反而更被动。这篇内容适合三类人看刚接手一个需要 Gerrit 评审流程的项目、之前一直用 HTTPS 方式拉代码现在要切到 SSH、以及已经能拉代码但想搞清楚背后到底发生了什么的人。我会按准备密钥 → 验证连通性 → 定位失败原因 → 真正拉代码 → 处理多仓库场景的顺序讲每一步都给出可以直接抄的命令和判断依据。文中涉及的主机名、端口、项目名一律用占位符表示你替换成自己环境的实际值即可。这里先给一个整体判断表让你知道走完这套流程后每种现象对应什么结论。验证动作期望结果出现异常说明什么ssh -T -p 端口 用户主机返回欢迎语或明确的账号信息密钥、用户名或端口有问题ssh -v详细日志能看到Offering public key且被接受客户端没选对密钥git ls-remote列出远端分支引用仓库路径或读权限有问题git clone正常落盘前序环节已全部通过需要强调一点Gerrit 的 SSH 端口不一定是 22。很多团队为了避免和系统 SSH 冲突会把 Gerrit 的 SSH 服务放在 29418 这类高位端口上。这是新手最容易忽略的细节后面会专门讲。2. 密钥准备一对能用的钥匙比什么都重要2.1 先确认本机到底有没有可用密钥在动手生成新密钥之前先看看现有的。打开终端执行ls -al ~/.ssh正常情况下你会看到id_rsa、id_ed25519这类文件以及对应的.pub公钥文件。如果目录不存在或者里面空空如也那就说明从来没配过需要生成。如果已经有一对密钥但你不确定它有没有在 Gerrit 上登记过也别急着删先拿它去试连通性试不通再考虑新增。这里有个很实际的提醒不要因为新项目就无脑覆盖已有的id_rsa。很多人第一次配 Gerrit 时直接ssh-keygen一路回车把原来的密钥覆盖了结果原本能访问的其他代码仓库全部失效。正确做法是给新用途单独起名比如id_ed25519_gerrit然后用配置文件指定哪个主机用哪个密钥这一点在 2.3 节展开。2.2 生成密钥时那几个交互提示该怎么答现代环境建议用 ed25519比 RSA 更短更安全兼容性在近几年的服务端上也基本没问题ssh-keygen -t ed25519 -C 你的邮箱或工号标识 -f ~/.ssh/id_ed25519_gerrit执行后会问你两件事。第一件是 passphrase也就是给私钥再加一层密码。从安全角度讲应该设但从日常使用角度讲如果你的机器本身有登录密码、也不是共享环境留空可以让后续的git clone完全不打断。我个人的做法是个人开发机留空公共或共享机器一定设。第二件是确认路径如果-f已经指定了就不会再问。生成完成后两个文件id_ed25519_gerrit是私钥绝对不能外传id_ed25519_gerrit.pub是公钥内容需要贴到 Gerrit 账号设置里。用下面命令把公钥内容打出来复制cat ~/.ssh/id_ed25519_gerrit.pub复制的时候注意要完整复制一整行从ssh-ed25519开头到末尾的注释中间不能断行、不能多空格。我见过好几次粘贴时首尾被截断导致认证失败的案例尤其是用某些终端复制长文本时。粘贴到 Gerrit 的 SSH Keys 设置页后保存通常立即生效不需要重启什么服务。2.3 用 config 文件管理多把钥匙避免选错当你本机有多把密钥比如一把给内部代码平台、一把给个人项目SSH 客户端默认会按顺序尝试~/.ssh下的默认文件名。如果 Gerrit 用的不是默认名就会出现明明确实配了公钥却还是被拒的经典问题。解决办法是写~/.ssh/configHost gerrit-review HostName 代码平台主机地址 Port 29418 User 你的账号名 IdentityFile ~/.ssh/id_ed25519_gerrit IdentitiesOnly yes这里每一行的作用值得说清楚。Host是你自己起的别名后面所有命令都可以用gerrit-review代替一长串地址这也是避免命令写错的实用技巧。HostName是真实地址可以填域名也可以填 IP。Port就是前面强调过的、Gerrit SSH 服务的实际端口千万别想当然写 22。User是你在 Gerrit 上的账号名注意它不一定是邮箱前缀也可能是工号以账号设置页显示为准。IdentityFile指定用哪把私钥。IdentitiesOnly yes这一行非常关键它的意思是只使用我指定的这把钥匙不要去尝试 ssh-agent 里缓存的其他钥匙。没有这一行客户端可能先拿别的钥匙去试服务端拒绝次数一多就会直接断开表现出来就是莫名其妙的Too many authentication failures。如果 config 里已经有同名 Host 段直接修改即可不要重复添加两个同名段SSH 只会读第一个匹配的。2.4 文件权限这件小事错了就是连不上SSH 对私钥文件权限非常敏感。如果私钥对其他用户可读客户端会直接拒绝使用它报bad permissions或者干脆跳过。标准做法chmod 700 ~/.ssh chmod 600 ~/.ssh/id_ed25519_gerrit chmod 644 ~/.ssh/id_ed25519_gerrit.pub chmod 600 ~/.ssh/config700表示只有自己能进这个目录600表示只有自己能读写文件。这个坑在 Windows 上通过 WSL 或者从别处拷贝密钥文件时特别容易踩到复制过来的文件权限往往是 777必须手动收窄。判断方法很简单ls -l看第一列权限位私钥那一行不该出现r或w给 group 和 others 的标记。3. 测试连接一条命令看清四层信息3.1 最直接的连通性测试命令准备好密钥之后第一步不是 clone而是ssh -T -p 29418 你的账号名代码平台主机地址如果配了 config 别名可以简化成ssh -T gerrit-review-T的意思是禁用伪终端分配。这一点很多人不理解为什么要有。普通的 SSH 登录会申请一个终端会话而 Gerrit 的 SSH 服务不是给你一个 shell 用的它只接受特定命令。如果不加-T有些环境会因为没有终端而报错或挂住。加上之后连接建立后 Gerrit 会返回一段欢迎信息通常包含**** Welcome to Gerrit Code Review ****这类字样有时还会带上你的名字和账号信息。看到这段欢迎语就说明身份认证这一步已经彻底通过了。如果返回的是Permission denied (publickey)别慌这恰恰是最有价值的信息它明确告诉你 SSH 通道是通的、端口是开的问题出在密钥不被接受。下一节专门拆解这种情况。如果返回Connection timed out或Connection refused方向就转向网络和端口了与密钥无关。3.2 首次连接时的指纹确认要看清第一次连一台新主机时SSH 会提示类似The authenticity of host ... cant be established. Are you sure you want to continue connecting?并要求输入 yes。这是主机指纹校验属于正常的安全机制。这里的原则是如果团队有公布主机的公钥指纹一定要核对一致再输 yes如果没有公布至少确认你连的地址确实是内部通知里给的那个。输 yes 之后指纹会写入~/.ssh/known_hosts之后再连就不再询问。反面情况是某天突然又提示指纹变了说REMOTE HOST IDENTIFICATION HAS CHANGED。这可能是服务端换了机器或重装了也可能是中间链路有问题。不要习惯性地删掉 known_hosts 了事先确认服务端是否真的变更过问清楚再处理。处理方法是删掉known_hosts里对应那一行重新连接确认新指纹。3.3 用 -v 把认证过程摊开来看当连接失败且原因不明时-v是最好用的工具ssh -vT -p 29418 你的账号名代码平台主机地址加上-v会打印大量调试信息信息量大但关键就那么几行。重点盯三个位置debug1: Connecting to ... port ...这一行确认地址和端口解析正确如果这里显示的端口不是你期望的说明 config 没生效或命令行没覆盖。debug1: Offering public key: ...出现几次就代表尝试了几把钥匙。看它后面紧跟的是哪个文件名如果根本不是你准备的那把说明IdentityFile或IdentitiesOnly没配好。debug1: Server accepts key或Authentication succeeded表示这把钥匙被接受了。反之如果每把都是Server accepts key之后又跟Permission denied那问题在服务端对这个公钥的登记不在本机。信息太多看不清时可以叠加-vvv拿更细的日志但通常-v就够了。实际排查中靠着Offering public key这一行定位到客户端一直在试另一把钥匙的情况非常多这也是我把IdentitiesOnly yes单独拎出来讲的原因。3.4 一条命令同时验证身份和命令通道Gerrit 的 SSH 接口除了认证还支持一些管理命令。认证通过后可以直接跑一个轻量命令验证命令通道是否可用ssh -p 29418 你的账号名代码平台主机地址 gerrit version或者查一下自己的账号信息ssh -p 29418 你的账号名代码平台主机地址 gerrit query --formatJSON status:open limit:1后一条会返回一小段 JSON能正常返回就说明不仅认证通过连 Gerrit 的业务命令都在正常响应。这比只看欢迎语更彻底。注意gerrit后面跟的version、query都是子命令实际可用的命令集合以你那套环境的版本为准不同版本可能有差异。如果不确定支持哪些可以先只跑-T看欢迎语那是最保险的。4. 认证失败的分层排查链路4.1 先分清是哪一类拒绝面对Permission denied (publickey)很多人第一反应是重新生成密钥重新贴公钥这往往是无用功。正确做法是分层定位。下面这张表是我实际排错时按顺序走的检查清单检查项具体动作结论客户端选了哪把钥匙ssh -vT ...看Offering public key选错则改 config公钥是否真的登记对比本地.pub内容与账号设置页不一致则重贴账号名是否正确检查User或命令里的用户名错误则修正服务端是否认这把钥匙日志中是否出现 accepting不认则查账号状态按这个顺序走绝大多数情况在三步内就能收敛。上面每一步都能给出明确的是/否结论而不是模棱两可的猜测。4.2 密钥登记环节最容易出的三种错第一种贴错了密钥。本机有多把公钥复制时拿错了文件。判断方法把账号设置页里登记的公钥内容与本地cat 文件.pub的输出做逐字符比对或者用ssh-keygen -lf算出指纹来比ssh-keygen -lf ~/.ssh/id_ed25519_gerrit.pub同一把密钥的指纹是唯一的比人眼比对长字符串可靠得多。有些平台设置页也会显示指纹直接对指纹最快。第二种粘贴被截断或带入了多余字符。从终端复制时不小心带上了提示符或者复制了多行。公钥必须是一整行。判断方法本地用wc -c看长度ed25519 公钥一般在 80 到 100 字符之间明显偏短基本就是截断了。第三种公钥贴到了错误的账号下。多人共用一台机器或者有多套环境时本地密钥对应的账号和你以为的账号不是同一个。这种问题表现为别的都正常就是没有目标仓库的权限。排查时用-v确认认证通过然后看欢迎语里显示的账号名是不是你以为的那个。4.3 账号名而不是邮箱这个细节坑过很多人Gerrit 的 SSH 认证里用户名指的是你在 Gerrit 系统里的username字段而不是邮箱、不是显示名。有些团队的账号名是工号有些是姓名拼音与邮箱前缀可能一致也可能完全无关。如果你用邮箱去连会得到Permission denied而且日志里看起来像是密钥问题非常容易误判。确认方法登录 Gerrit 的网页端进个人设置页看 Username 那一栏。如果那一栏是空的说明这个账号还没设置用户名SSH 方式就无法使用需要先设置。这一点在自助注册的实例上很常见值得先确认一眼再折腾密钥。4.4 端口不通与密钥失败是两码事如果报错是Connection timed out、Connection refused、No route to host那和密钥毫无关系别再动密钥了。区分技巧很直接认证失败时你至少能看到服务端回了Permission denied说明 TCP 连接是建立的而超时或拒绝说明连接压根没建立。排查顺序先用ping 主机地址或者nc -vz 主机地址 29418看目标端口是否可达。nc在多数 Linux 和 macOS 上都有Windows 上可以用 PowerShell 的Test-NetConnection代替。如果端口不通先确认端口号对不对——再次强调 Gerrit SSH 端口不一定是 22。如果端口对但连不上那就是网络策略层面的事需要找负责网络环境的同事确认不要在本地反复试。还有个隐蔽情况主机名能解析但解析到了错误的地址比如内网和外网两套地址。用nslookup 主机地址或ping看一眼解析结果和团队给的地址对一下。这个坑在切换办公地点或接入不同网络后特别容易出现。4.5 一个完整的手把手排查示例假设现象是ssh -T报Permission denied (publickey)。我会这样走一遍第一步跑ssh -vT -p 29418 账号主机在输出里找到Offering public key那几行。假设发现它尝试的是id_rsa而我的 Gerrit 密钥是id_ed25519_gerrit那问题定位完成客户端没选对钥匙。解决办法是在 config 里加IdentitiesOnly yes并确认IdentityFile指向正确文件。第二步如果日志显示确实尝试了id_ed25519_gerrit但依然被拒那就登网页端核对公钥指纹。假设发现网页上登记的是旧机器的公钥问题同样定位完成重新贴当前这把。第三步如果公钥完全一致、账号名也正确但还是被拒那就查账号状态比如是否被停用、是否用户名未设置。这一步就不是本机能解决的了。走完这三步基本能覆盖所有认证类失败。整个过程的关键是每一步都从日志里找证据而不是靠猜。5. 从验证到真正把代码拿到本地5.1 先确认仓库存在且有读权限SSH 通道验证通过之后先别急着 clone 整个仓库。用一个轻量命令确认仓库路径正确、你有读权限git ls-remote ssh://账号主机:29418/项目路径这条命令只拉取远端的引用列表不下载文件内容速度快输出是分支和标签的哈希值列表。能列出来就说明路径正确、权限足够、SSH 也没问题如果报not found或权限错误问题在仓库路径或权限与 SSH 配置无关。这一步的价值在于把连接问题和仓库问题彻底切开避免 clone 失败时又要重新判断一遍。仓库路径的写法要注意它通常不是简单的一个名字而是带层级的分组路径形如某分组/某项目。这个路径在 Gerrit 网页端的项目页面里能看到直接复制最稳妥。路径写错时错误信息往往也是not found和权限不足不容易区分所以务必从网页端抄准确。5.2 clone 命令的两种写法和该选哪种拿到正确路径后clone 有两种写法# 写法一完整 URL git clone ssh://账号主机:29418/某分组/某项目 # 写法二利用 config 别名配合 scp 风格路径 git clone gerrit-review:某分组/某项目写法一好处是自解释缺点是要重复输入账号、主机、端口命令长且容易打错。写法二依赖 2.3 节的 config 配置命令简短切换项目时只改路径部分实际使用中我更推荐这种。注意写法二里的冒号不能省它表示分隔主机和路径。如果仓库体积很大可以加上--depth 1只拉最近一次提交先快速拿到代码看结构需要完整历史时再git fetch --unshallow补齐。这个技巧在只想看看代码长什么样、并不打算提交的场景下很实用能省掉大量等待时间。5.3 分支与提交规范clone 完只是开始代码落到本地之后还有一步很关键——确认你们团队的提交流程。Gerrit 的核心是评审后合入它不是直接推到分支而是推到refs/for/分支名。很多刚从其他平台转过来的人在这里翻车照常git push origin main结果要么被拒要么绕过了评审。正确的推送形式是git push origin HEAD:refs/for/目标分支名这条命令的含义是把当前提交推到评审队列而不是直接进分支。如果团队配置了 Change-Id 机制还需要在提交信息里带上Change-Id这个通常由提交钩子自动生成。钩子需要手动安装# 从仓库里取钩子脚本放到 .git/hooks 下 scp -p -P 29418 账号主机:hooks/commit-msg .git/hooks/ chmod x .git/hooks/commit-msg注意这里scp的端口参数是大写-P和ssh的小写-p不同这个大小写差异坑过不少人。装好钩子后每次git commit都会自动在提交信息末尾追加一行Change-Id后续的评审更新都靠它来关联同一组修改。这套机制初次接触会觉得繁琐但理解之后就明白它的价值Change-Id让同一个改动的多个版本能在评审系统里聚合成一条记录评审意见、修订历史都串在一起而不是散成一堆互不相关的提交。如果你后续要频繁提交修改这个钩子是必装项别等到推送被拒才回头补。6. 多仓库、多环境下的实用配置心得6.1 多个项目共用一套认证当你需要拉取同一平台下的多个仓库时不要在每条 clone 命令里重复账号和端口。用 config 别名后所有仓库统一写成gerrit-review:分组/项目A、gerrit-review:分组/项目B认证配置只维护一份。这样即便后续端口变了、账号名调整了也只需要改一处。如果不同项目分属不同的 Gerrit 实例地址不同就在 config 里写多个 Host 段各用各的别名和密钥互不干扰。这就是前面强调不要覆盖默认密钥的原因——多实例场景下每个实例配一把独立密钥是最清晰的做法出问题时能一眼看出是哪个实例的配置有问题。6.2 换了机器怎么快速恢复换电脑或者重装系统后最省事的做法是把~/.ssh目录里那把私钥和config文件安全地迁移过去然后重新设置权限chmod 600。私钥不用重新生成公钥也不用重新登记直接可用。但前提是迁移过程本身要安全不要通过聊天工具、邮件这类渠道传输私钥。如果确实拿不到原私钥那就重新生成一对把新公钥贴到账号设置里。旧公钥记得清理掉避免账号上挂着一堆来历不明的密钥——这既是安全习惯也能避免将来排查时被旧密钥干扰。6.3 几个高频小问题的快速对照现象大概率原因处理方式Permission denied (publickey)密钥未登记或选错按第 4 节分层排查Connection timed out端口错或网络不通核对端口测端口可达性Could not resolve hostname主机名写错或解析异常核对地址查解析结果Too many authentication failures客户端试了太多钥匙配IdentitiesOnly yesclone 报 not found仓库路径写错或无权限从网页端复制完整路径push 被拒推到了普通分支而非评审队列改推refs/for/分支6.4 我踩过的两个印象深刻的坑第一个是关于端口。早期我一直以为 SSH 就是 22配置时没写端口结果ssh连上去返回的是另一台服务器的欢迎语根本不是 Gerrit。查了半天才发现 Gerrit 在 29418而我连的 22 是系统 SSH。这个教训是连接成功不等于连对了地方欢迎语的内容一定要看一眼确认是目标服务。第二个是关于密钥选择。有段时间我的机器上装了密钥管理工具它会把多把密钥加载到 ssh-agent 里。结果ssh -T时客户端按顺序试了五六把钥匙全被拒最后报Too many authentication failures看起来完全不像密钥问题。加上IdentitiesOnly yes之后立刻正常。这类问题最迷惑人的地方在于它伪装成了认证失败但真正的问题是客户端太热情试了不该试的钥匙。把测试 SSH 连接这一步单独拿出来做看似多花了一分钟实际上省掉的是后面反复试错的半小时。养成先验证通道、再拉代码、再推评审的顺序后面无论换环境还是帮别人排查思路都会清楚很多。