VPN 已连接却遇 Git 403:找出是谁在拒绝
403 是响应,不是对 VPN 的结论。先确认哪个组件拒绝了请求,再在网络之间逐项对比单一变量,并记录变化。
在办公室 Wi-Fi 上正常、在手机热点上失败,两种情况 VPN 都连接着。Git 返回 403,错误只有三行。人容易把热点当成原因,然后不断改网络设置,直到某个东西动了。HTTP 403 表示服务器理解了这个请求并拒绝了它,所以有用的问题是哪一个服务器在拒绝,而有用的下一步是每次只改一个变量的对比。
先读状态,再看网络
403 不是查询失败,不是 TCP 超时,也不是没做完的 TLS 握手。它是一个决定,由解析过你请求的某个东西做出。
在你知道是谁做了这个决定之前,你尝试的每一次网络改动都只是带着合理故事的猜测。
谁能用 403 回答?
仓库应用只是候选人之一。代理、网关或过滤服务可能挡在它前面先做回答,而客户端看到的都是同一个状态。保留状态码、相关响应头和脱敏后的错误正文,然后拿它们去和授权服务的日志对照。远端 URL 与实际到达的端点也可能因为 DNS 或代理配置而不同,所以要检查配置里的地址和发起请求的进程。
如果拒绝根本没有到达仓库,它的日志就会沉默,而这份沉默本身就是证据。
每次只对比一个变量
热点对比有用,是因为它缩小了问题范围,而不是因为它证明了原因。两个网络可能在同一时刻在寻址、IP 族、代理、TLS 协商和出站策略上都不同。
每次只改一样,其余原样记录。下面这张表是我做这件事时笔记里至少会留的内容:
| Keep constant | Record when it changes |
|---|---|
| Repository, operation and credentials | Resolved address and IP family |
| Git process and configuration | Proxy use and the responding component |
| Intended VPN profile | Actual route and exit seen by the service |
在正常与失败两次运行之间唯一不同的那一行,就是下一个小时该去的地方。
绕过方案是证据,不是解释
强制换用另一个地址族可能改变目标地址、路由或所选中的中间节点,所以它可能改变结果,却不能证明 IPv6 与仓库不兼容。换一个协商出来的 TLS 版本也是同一类观察。两者都不能成为关闭证书校验或削弱连接的理据。顺着拒绝组件的策略和日志追到真正的决定,而不是停在第一个“能用”的改动上。
你解释不了的绕过方案,换个地方还会再失败一次。
写一份会被修掉的报告
把同样的操作、两条路径,以及说“不”的那个组件交给应答组件的负责人。分享之前先删掉授权头、内嵌凭据和私有仓库细节。
有用的报告很短、可复现,并且点出的是层,而不是症状。
我按顺序做的检查
先确认远端 URL 和解析到的端点,再从失败的那次运行里抓取状态码和响应头。在第二个网络上重复完全相同的操作,把两份记录逐项对比。
然后把这个差异拿去问应答的组件:
- 客户端和网络两侧的代理配置。
- 仓库前面的出站策略或过滤规则。
- 令牌或账号的权限范围,包括过期与 SSO 条件。
- 仓库对令牌所代表身份的权限。
状态码的语义本身在RFC 9110里,写报告时值得引用。