无界面 VPN 的核验:一次一条流,附上证据
无界面机器需要一个能读懂应答的请求。分别测试每个地址族、上下文与目的地,再记录结果没有覆盖什么。
无界面的机器上没有浏览器可打开,也没有锁形图标可看,所以核验必须是一个能读懂应答的请求。这听起来不值一提,直到你注意到有人用单个出口地址结果去为一台运行十几项服务、两个地址族和一个代理的设备作认证。下面这套流程每次只建立一小块有边界的证据,而且就在工作负载真正运行的上下文里进行。
先决定测试要证明什么
在运行任何东西之前,把预期写成一行:哪条流、经哪个接口、到哪个目的地、在另一端应当看到什么。没有明说预期的测试,只会给出一个谁也无法解释的数字。
最有用的写法是点名一个经批准的端点,由它报告所看到的源地址。
这个端点是你唯一需要安排的部分。其余都是客户端侧的观察,而客户端侧的观察会以有趣的方式出错。
在正确的上下文里执行观察
管理员的 shell 不是工作负载的环境。它可能带着另一个代理变量、另一个命名空间里的另一张路由表,或者另一个用户的环境文件。要在与被测对象相同的主机、容器、命名空间或服务上下文里运行测试。否则你测的只是自己的 shell。
在普通主机上,这意味着同一个用户和同一个单元。在容器环境里,这意味着进入容器的网络命名空间,而不是从主机上测试。在 systemd 服务上,这可能意味着使用配置相同的临时单元。
只有身处正确的上下文,接下来的请求才有意义。
分别检查两个地址族
要显式地请求每一个地址族,因为一次成功的 IPv4 请求根本不会走 IPv6 路径。--ipv4 和 --ipv6 选择器会强制做出选择,而两个结果的差异本身就是信息。
curl --ipv4 https://probe.example/observed-source
curl --ipv6 https://probe.example/observed-sourceIPv6 请求失败并不能证明阻断有效。它可能意味着端点没有 IPv6 地址、DNS 没有返回任何可用的结果,或者设备根本没有 IPv6 路由。下结论之前先读懂失败。只有经隧道成功的 IPv6 请求,才能证明 IPv6 路径已被覆盖。
把两个答案并排记下来。只看其中一个,就只有一半事实。
补上出口检查漏掉的四件事
出口地址只回答一个问题。另外四件事才决定隧道是否按设计工作。
| Check | What it proves | What it does not |
|---|---|---|
| Included and excluded destinations | The split behaves as designed for those flows | Coverage of any other destination |
| Resolver path | Where a fresh lookup actually travelled | Where a cached answer came from |
| Tunnel-down behaviour | The failure policy for a new request | What an already open connection does |
| Reconnect and restart | The requirement survives the lifecycle event | Behaviour under load or partial failure |
目的地策略排在第一位:如果设计里有分流,就分别测一条应当被包含的流和一条应当被排除的流。然后是 DNS,这里要看的是真实的解析路径,而不是配置值,因为缓存里的答案或打印出来的设置,说明不了新的查询去了哪里。
故障行为需要预案,也需要保留一条恢复路径。在隧道不可用时观察一个新请求,并记住:一条早已打开的连接不能作为故障策略的证据。生命周期放在最后:如果重连或重启服务会影响这项要求,就在这些事件之后重复观察。
记录证据,而不是结论
对每个结果,保留进程上下文、目的地、地址族、时间以及观察到的源。这组信息是关于你所跑过的流的有边界证据,它之所以站得住脚,正是因为它说明了覆盖了什么。
它不是对 VPN 运营商记录的审计,也不是对设备上未来每个应用的证明。
把边界写在结果旁边。说明自己没覆盖什么的测试能通过评审,而声称设备已验证的测试不能。把 curl 手册放在所用地址族选择器旁边,并把那张四行表与结果放在一起。