在办公室 Wi-Fi 上正常、在手机热点上失败,两种情况 VPN 都连接着。Git 返回 403,错误只有三行。人容易把热点当成原因,然后不断改网络设置,直到某个东西动了。HTTP 403 表示服务器理解了这个请求并拒绝了它,所以有用的问题是哪一个服务器在拒绝,而有用的下一步是每次只改一个变量的对比。

先读状态,再看网络

403 不是查询失败,不是 TCP 超时,也不是没做完的 TLS 握手。它是一个决定,由解析过你请求的某个东西做出。

在你知道是谁做了这个决定之前,你尝试的每一次网络改动都只是带着合理故事的猜测。

谁能用 403 回答?

仓库应用只是候选人之一。代理、网关或过滤服务可能挡在它前面先做回答,而客户端看到的都是同一个状态。保留状态码、相关响应头和脱敏后的错误正文,然后拿它们去和授权服务的日志对照。远端 URL 与实际到达的端点也可能因为 DNS 或代理配置而不同,所以要检查配置里的地址和发起请求的进程。

如果拒绝根本没有到达仓库,它的日志就会沉默,而这份沉默本身就是证据。

每次只对比一个变量

热点对比有用,是因为它缩小了问题范围,而不是因为它证明了原因。两个网络可能在同一时刻在寻址、IP 族、代理、TLS 协商和出站策略上都不同。

每次只改一样,其余原样记录。下面这张表是我做这件事时笔记里至少会留的内容:

Keep constantRecord when it changes
Repository, operation and credentialsResolved address and IP family
Git process and configurationProxy use and the responding component
Intended VPN profileActual route and exit seen by the service

在正常与失败两次运行之间唯一不同的那一行,就是下一个小时该去的地方。

绕过方案是证据,不是解释

强制换用另一个地址族可能改变目标地址、路由或所选中的中间节点,所以它可能改变结果,却不能证明 IPv6 与仓库不兼容。换一个协商出来的 TLS 版本也是同一类观察。两者都不能成为关闭证书校验或削弱连接的理据。顺着拒绝组件的策略和日志追到真正的决定,而不是停在第一个“能用”的改动上。

你解释不了的绕过方案,换个地方还会再失败一次。

写一份会被修掉的报告

把同样的操作、两条路径,以及说“不”的那个组件交给应答组件的负责人。分享之前先删掉授权头、内嵌凭据和私有仓库细节。

有用的报告很短、可复现,并且点出的是层,而不是症状。

我按顺序做的检查

先确认远端 URL 和解析到的端点,再从失败的那次运行里抓取状态码和响应头。在第二个网络上重复完全相同的操作,把两份记录逐项对比。

然后把这个差异拿去问应答的组件:

  • 客户端和网络两侧的代理配置。
  • 仓库前面的出站策略或过滤规则。
  • 令牌或账号的权限范围,包括过期与 SSO 条件。
  • 仓库对令牌所代表身份的权限。

状态码的语义本身在RFC 9110里,写报告时值得引用。

相关文章