大多数人读 .ovpn 文件,就像读路由器的设置页:地址、路由、加密套件列表。真正执行代码的部分在靠近文件底部,紧挨着那些看起来像管道工程的指令,并且以客户端已有的权限运行。这正是粗心或恶意的配置文件会利用的缺口,也是把一个有用的 DNS 辅助脚本变成三周后支持问题的缺口。事件指令、安全等级和文件归属决定可能发生什么;脚本内容决定实际发生什么。连接之前,请把这四样都读一遍。

运行辅助脚本的配置文件是程序,不是设置文件

route 和 dhcp-option 这类指令描述客户端应当建立的状态,客户端把它们当作数据处理。up 脚本则不同:客户端把它作为进程启动,并把充满连接细节的环境交给它,于是作者写下的任何内容都以你的账户权限运行。在用户是管理员的笔记本电脑上,对于一个以附件形式或从论坛帖子粘贴而来的文件,这就是一扇敞开的大门。这种区别不是文风问题,而是配置与代码的区别。

值得抛弃的误解是:配置文件只能重定向流量。运行 ip route add 的辅助脚本和向远程主机运行 curl 的辅助脚本是同一套机制,从外面看配置文件一模一样。文件的外观不会告诉你拥有的是哪一种,所以审查必须深入到脚本本身。

究竟什么决定执行,以及启用它的那几行长什么样

有三样东西控制辅助脚本:为它命名的事件指令、允许它的 script-security 级别,以及客户端平台自身的实现。配置文件可以请求一个钩子,却看不到任何事情发生,因为移动客户端或精简的桌面构建可能根本没有实现该事件。同样,要求比自身功能所需更多权限的配置文件,已经说明了它的来历。权限级别是一种能力,不是批准。启用它的那几行长这样,路径处替换成你的路径。

script-security 2
up /etc/openvpn/update-resolv-conf.sh
down /etc/openvpn/update-resolv-conf.sh

级别 0 拒绝外部程序,默认的级别 1 只允许客户端内置的可执行文件,因此钩子需要 2 或更高;级别 3 主要用于传递旧式密码,最好避开。附带钩子的配置文件既需要该指令,也需要那个级别,所以文件底部附近的一行值得像证书块一样被审视。OpenVPN 手册列出了各个权限级别,它的措辞值得与配置文件实际要求的内容对照阅读。

归属、路径,以及辅助脚本被喂了什么

你信任的配置文件,如果指向一个其他账户可以改写的脚本,就不是可信的安排,因为昨天的审查已经不能描述今天运行的东西。检查辅助脚本的所有者和权限位,并确认配置的路径解析到你检查过的文件,而不是解析到某个可能被别的东西遮蔽的名字。参数和环境也值得同样审视:客户端会把连接细节传给钩子,而记录整个环境的辅助脚本可能正在把凭据写进支持工单。优先选择运营商或平台有文档的集成,而不是从无关帖子复制来的 DNS 脚本,因为两个解析器管理器争抢同一个文件,会产生看起来像 VPN 坏了的故障。

报告失败的钩子时,先把环境脱敏,再粘贴到任何地方。有助于调试的转储也可能带着能认证你的令牌。

清理失败会在隧道消失之后才出现

up 辅助脚本添加路由和解析器设置,down 辅助脚本本应把它们再拿掉。当 down 钩子缺失、静默失败,或者因为客户端被杀而从未运行时,残留物会比会话活得更久。于是流量走上没人规划过的路径,名字通过一个本应被移除的解析器来解析,而隧道本身看起来还很健康。这些症状出现在断开之后,所以被归咎的是网络而不是配置文件。

把拆除当作独立一步来测试:建立隧道,记录路由和解析器,断开,然后确认两者都消失。连接干净的配置文件仍可能留下状态,辅助脚本的退出码为零,也不能证明解析器最终指向了你以为的地方。

审查辅助脚本并保留退路

从头到尾读一遍脚本,寻找三样东西:需要权限的命令、在你的系统上可能不存在的硬编码路径,以及它发起的任何网络调用。然后看它如何失败,因为吞掉错误的辅助脚本会把配置失误变成谜团。如果你不能用一句话说清脚本做什么、谁能改动它,就还没准备好启用它。要求比这句话所需更多权限的配置文件,就是停下的信号。

把最后一个能用的配置文件和它引用的辅助脚本保存在受保护的存储里,并记录两个版本,这样回滚就是一次复制,而不是在最糟糕的时刻重建。如果辅助脚本只为某个不再需要它的发行版或客户端版本存在,就在拥有该设置的配置里让它退役,而不是不断抬高权限直到错误停止。这才是出路:更少的活动部件,而不是更多权限。

相关文章