有人问 VPN 是否正常,三个不同的人其实指的是三件不同的事。运维人员指的是 Office 配置文件完成了认证。调度器指的是私有 API 会响应。安全评审指的是这一个请求走了受保护的路径。用 grep 在路由表里找接口名的脚本,对其中的任何一个都给不出可靠答案。

所以检查清单在任何命令执行之前就开始:先给检查的对象命名。之后的一切只是选择真正能报告它的接口。

在命令之前先命名对象

把问题写成带主语的谓词。「Office 配置文件是否已连接」可以验证,也有能回答它的负责人。「这个作业能否到达内部 API」是第二个谓词,失败模式不同。「这个请求是否走受保护的路径」是第三个,也是唯一只谈单条流量、而不是整台机器的问题。

把它们混在一起,就会得到自信地说谎的自动化。先查配置文件状态、再假定 API 已就绪的作业,会在错误的层把人叫起来;而 ping 一下 API 就当成隧道证据的作业,分不清真正可用的 VPN 和恰好连在办公网上的笔记本。

一个配置文件:去问拥有它的组件

对原生 Mac 服务来说,scutil 里的网络连接控制项报告的正是这件事。它们读取具名配置的状态,而不是从接口上推断什么。

scutil --nc list
scutil --nc status "Office VPN"

那条命令里的配置文件名称必须是列表输出中的真实名称,不能靠猜。这是原生服务的只读路径,也不保证第三方客户端会出现在其中;自行管理隧道的提供方需要自己受支持的状态接口。先检查列表再写状态调用,这样名称缺失时会显式失败,而不是悄悄匹配不到任何东西。

在 Network Extension 应用内部,从管理器和连接对象读取状态,而不是从系统读取。这里要注意作用范围:加载已保存的配置只会返回调用方应用自己创建的那些,所以空结果意味着「我的一概没有」,而不是「这台 Mac 上没有 VPN」。要区分查询失败与已确认的断开状态,并把过渡状态当作独立结果处理,而不是去重试一个已经在建立的连接。

一个作业:就绪是另一个谓词

如果问的是某个作业能否继续,那就在连接负责人报告预期状态之后,测试该作业真正的依赖。对私有资源做一次有界就绪检查才是正确形式:它只证明作业需要的东西,不多不少。

它仍然无法证明机器上的每个请求都走了隧道,也不应该被当成能证明的样子来汇报。同样,一个能通过普通网络响应的资源并不是 VPN 的证据,所以两项检查即便都成功也仍然分开。

当作业确实需要受保护路径时,这项要求需要部署自身的强制执行设计:路由、分离隧道的排除项、防火墙规则或应用层要求。端点可用性是关于服务器的事实。强制执行是关于客户端配置的事实,二者不能互相证明。

一条流量:检查被选中的路由

对单条流量,用能报告该特定目标所选路径的路由诊断,去看目标、地址族和源上下文。那份输出会点名实际使用的接口和源地址,这比一条默认路由条目要有力得多。

浏览器里的笼统检查回答的是完全另一个问题。公网 IP 结果描述的只是一个请求从一个服务器视角看到的样子,它对私有 DNS、被排除的应用,或另一个进程正在使用的第二套网络上下文一无所知。

返回调用方用得上的结果

选择与谓词匹配的返回形态:具名配置文件状态、资源就绪判定,或带上产生它的证据的显式未知。判定旁边要保留配置身份和失败原因。

正是最后这个习惯,把有用的自动化和掩盖问题的自动化区分开来。「Office VPN:已断开,最后一次尝试在认证处失败」告诉运维该去哪里看。「VPN 挂了」会招来重装,「接口找到了」则会招来虚假的万事大吉。

Network Extension 关于加载已保存隧道配置的参考文档,是在其上构建任何东西之前确认调用方应用作用范围的地方。

相关文章