浏览器能用、每条隧道都死掉的校园或酒店网络,不是一个问题,而是四层叠加。路径可能只放行 TCP 443,可能要求认证代理,可能检查握手,也可能干脆用策略禁止远程访问。按顺序排查,就不必为了修一条网络规则去买新客户端。下面的流程大约十分钟,最后会给出一份书面答案:到底是哪一层拒绝了你。

证明是哪一层拒绝了连接

不要从 VPN 客户端开始。先向同一主机发一个普通 HTTPS 请求,读出各计时阶段,因为失败的那一段会告诉你是哪台设备在负责:

curl -sS -o /dev/null -w 'connect=%{time_connect} tls=%{time_appconnect} code=%{http_code}\n' \
  --max-time 8 https://vpn.example.net/

没有响应、也没有 connect 时间,说明 TCP 从未建立,问题在防火墙、代理要求或错误的地址。有 connect 时间却没有 tls 时间,说明中间有设备接受了连接又拖住了握手,这正是检查型代理在客户端眼里的样子。

接着看真正拿到的证书。这是最快分辨你在和服务器说话、还是在和重签会话的中间盒说话的办法:

openssl s_client -connect vpn.example.net:443 -servername vpn.example.net -brief </dev/null

读签发者那一行。你自己的 CA 说明你到达了服务;企业或设备签发者说明有过滤器中断了连接,并且正在决定下一步。把两份输出连时间戳一起记下,因为一小时后在访客网络上做同样的测试,得到的答案可能不同。

认识代理的客户端改的是第一个包,不是策略

如果网络要求通过认证代理发 CONNECT,就必须告诉 VPN 客户端,否则它会尝试向无法路由的地址开套接字。把代理和客户端分开测试,这样失败只有一个解释:

https_proxy=http://proxy.example.edu:3128 curl -v -o /dev/null https://vpn.example.net/
sudo openvpn --config /etc/openvpn/corp.conf --proto tcp-client \
  --remote vpn.example.net 443 --verb 4

第一条命令证明代理会带走目的地。第二条让客户端在 443 上走 TCP,这是大多数只放行 Web 的路径唯一会转发的传输方式。如果第一条成功而第二条仍然失败,那代理不是障碍。

有些客户端读的是操作系统代理而不是覆盖设置,有些两者都不读。在假定全局设置生效之前,先查应用自己的文档。通过代理不服务的解析器去解析端点的客户端,同样会在这里失败,所以改 DNS 看起来像修复,其实只是绕路。

顺便留意引导模式。被过滤的网络可以对厂商的检测主机名给出自己的答案,从而改变客户端选择哪个代理配置。不要围绕这个怪现象去建流程。它因站点而异,会无通知地变化,也不是你能为别人支持的东西。

443 端口可达,不等于被允许

把隧道包进 443 的 TLS 只解决可达性,别的都不解决。检查端口的过滤器会放行;取握手指纹的过滤器不会,而加密客户端问候只有在两端都支持时才藏得住服务器名。去做测试,弄清自己处在哪类过滤器后面,而不是靠猜。

还有一个人们后来才发现的性能代价。可靠传输套在可靠传输里会放大丢包,因为内层栈会重传外层栈已经重传过的分段。链路干净时没人察觉。校园无线拥塞时,隧道偏偏在你最需要它的时候不可用,图上看起来像丢包,其实并没有人故意丢。

UDP 通常第一个牺牲,这一点值得确认而不是推断。如果 TCP 443 能通,而 WireGuard 或默认 OpenVPN 从没完成握手,那就是网络在丢 UDP,再怎么换密钥也没用:

sudo wg show
dig +short vpn.example.net
dig +short vpn.example.net @1.1.1.1

昨天还能用的对端旁边出现空的握手时间戳,就是 UDP 的答案。那两条 DNS 查询回答的是另一个问题:如果本地解析器和公共解析器对端点地址说法不一,你正在被引导,传输方式的争论就是错的。

确认到底有什么离开了本机

客户端日志讲的是客户端相信什么。抓包讲的是网络看到了什么,两者之差正是大多数这类案件的解决之处:

sudo tcpdump -ni any -c 20 'tcp port 443 and host 203.0.113.10'
sudo ss -tunp | grep -E ':(443|1194|51820)\b'

如果看到 SYN 包却没有 SYN-ACK,阻断在上游,客户端是无辜的。如果看到完成握手的目标地址不是服务器地址,你是在和代理或拦截者说话。如果什么都看不到,先确认你抓的是正确的网卡,再下结论。

套接字列表能抓到更微妙的一种:第二条隧道已经占着默认路由。两个客户端争路由表,会制造出看起来和网络被封一模一样的间歇性故障,而抓包会显示流量从你没想到的网卡出去。

回滚,以及值得留下的那行记录

按你改动的相反顺序撤销。取消代理变量,停掉你手动启动的客户端,删掉你加的任何路由或解析器覆盖:

unset https_proxy http_proxy
sudo systemctl stop openvpn-client@corp
sudo ip route show default

把手动启动的客户端留着继续跑,就是笔记本下次去办公室时网络坏掉的原因。留在 shell 配置里的代理变量会在几周后弄坏无关工具,而排查它的人不会把两件事联系起来。

然后写四行:网络、时间戳、失败的阶段、以及你看到的签发者。在一个校园能用、到下一个校园就失败的路径很正常,下一个人需要知道差别在代理、解析器还是过滤器。如果答案是网络禁止远程访问,就停在那里,向管理员询问受支持的路径,而不是把绕行方案带进一份你没读过的策略里。

相关文章