你添加一条分支路由,它能工作,然后一次固件更新或网页界面的保存就把它删掉了。那一刻,两种修法看起来同样合理:把路由放进设备受支持的配置源,或者从启动脚本把它追加到生成的文件里。它们在一周内表现一致,并在第二次升级时分道扬镳。本文比较两者实际各自拥有什么。

两个候选者拥有的东西不同

受支持的配置源是设备视为输入的任一位置:GUI 字段、导入的配置文件、按客户端记录的目录、管理包读取的设置数据库。生成的 server.conf 是输出。编辑输出在生成器再次运行之前有效,而生成器会在保存、重启和升级时运行。

脚本的做法是在生成器结束后给那份输出打补丁。对实验室来说这是正当手法,对分支机构来说却是错误的默认做法,因为它让路由依赖文本匹配,而不是依赖受记录的字段。

QuestionSupported config sourcePost-generation script
Survives a GUI saveYesOnly if the save does not regenerate the file
Survives a package upgradeUsually, when the field is documentedNo guarantee; the wrapper can be replaced
Supportable by the vendorYesNo
Obvious to the next adminYes, it is in the UINo, it lives in a wrapper

设备会在何时重新生成什么

管理包通常把设置保存在数据库或模板集里,并在启动时渲染守护进程配置。网页界面的保存往往重写整个文件,而不是改一行。如果你的改动原本在这个文件里,现在它就没了,而且没有任何警告记录,因为从设备的角度看什么都没丢。

启动包装器还有第二种失效方式。针对特定间距指令的 grep 在模板改动后就匹配不上,脚本于是静默地什么也不做。更糟的是,固件更新可能用新版本替换包装器,把补丁一并带走。于是你有一条存在了一年的路由,在没人把它和路由联系起来的维护期间消失了。

受支持的所有者必须表达什么

一条分支前缀需要三条陈述,而不是一条。客户端必须学到路由,服务器必须知道哪个通过认证的客户端拥有该前缀,分支一侧必须把流量送回分配给它的隧道地址。用 OpenVPN 的术语,这意味着一条被推送的 route、与之匹配且绑定到客户端的 iroute,以及远端的返回路径。

设备界面就是在这里误导人。一个标着“允许客户端访问服务器 LAN”的复选框,描述的是通往服务器自身网络的流量。你的分支是相反方向:某个客户端背后的一张网络,从服务器一侧可以到达。同一个标签并不意味着支持这件事,文件里一条合法指令也不是软件包会保留它的承诺。

当不存在受支持的控件时

有时已安装的版本确实无法表达针对单个对端的路由。这时诚实的选项是换一个你能控制的网关,或者在两端都支持站点到站点模式。两端都必须说这套话。产品页写着站点到站点,并不让任意 OpenVPN 配置文件与另一种协议的隧道可以互换;强行配对通常得到一条半连接状态的隧道,在状态页上却看着不错。

选择那个你能用一句话向网络继任者描述清楚的方案。如果答案需要提到脚本路径和文本模式,设计就已经脆弱了。

让意图可审计

把契约记录在设备之外:生效的客户端身份、前缀、返回网关,以及分别提供它们的受支持字段。通过受支持的机制导出配置,让重建有参照,并在重连、重启和升级之后验证服务,而不只是改动之后。

出问题时,把生成结果与该记录对照,而不是恢复一行没人解释的配置然后指望它。OpenVPN 2.6 参考文档 是你在与界面争论之前确认 route 与 iroute 如何交互的地方。

下次改动前的检查清单:指明所有者,陈述三段式契约,导出意图,并在设备自身的生命周期事件之后重新测试,而不是在你编辑之后。

相关文章