为什么 OpenVPN 客户端阻断规则从不会命中 FORWARD
客户端之间的流量可能一直留在 OpenVPN 进程内部,从不经过主机的 FORWARD 链,因此计数器为零并不能说明任何问题。
在同一台 OpenVPN 服务器上的两个客户端可以互相访问,即使你已经为它们的地址池添加了 DROP 规则。规则位于 FORWARD 链中,流量明明在流动,可那条规则的计数器却始终为零。最自然的结论是防火墙坏了。但更常见的情况是,数据包根本没有进入这条链。
一个对端数据包经过 OpenVPN 服务器时只有两条路径,而决定走哪条路径的是配置,不是防火墙。知道当前用的是哪条路径,你才能判断主机上的规则是否放在正确的位置。
对端数据包可能经过的两条路径
当内部转发选项关闭时,来自客户端 A 的数据包会从隧道接口进入,其目的地址落在客户端 B 被分配的地址范围内。服务器的路由表会把它从同一个隧道接口再送出去,因为通往 B 地址的路由属于服务器。这是一条穿过主机进出双向的流,由 IPv4 转发路径来评估。
当内部转发选项开启时,服务器进程会把数据包匹配到某个已连接的客户端,并直接写回该客户端。数据包在守护进程内部就被处理掉了,再也不会重新进入隧道设备,于是主机的转发路径被完全跳过。OpenVPN 2.6 的手册把这一选项称为内部客户端互转发,这正是主机防火墙规则看不到它的原因。
还有一种部署形态会把数据通道卸载到内核模块。转发决策和加密都离开了用户空间,而 netfilter 是否能看到这条流,取决于卸载路径是如何构建的。绝不要以为旧规则集在卸载服务器上的行为与在用户空间服务器上相同。确认模式要看正在运行的配置,而不是软件包版本。
当计数器始终不动时
计数器为零意味着数据包在那一点没有匹配到这条规则,原因有好几种。第一种就是上面的绕行:流量根本没有到达这条链。第二种是接口、方向、源或目的不匹配,这在地址池重新编号、而规则仍然写着旧前缀时很常见。
第三种是位置。链中靠前的规则可能在你的 DROP 被评估之前就接受了这条流,而靠后的规则可能才是真正决定结果的那一条。带着可见的计数器按顺序读整条链,然后检查重复测试时是哪条规则的计数在增长。
在两个客户端之间做一次受控的交换,它们的真实地址要从服务器自身的状态中确认,而不是凭记忆里的租约。如果相关计数器仍然不动,在改写匹配条件之前先怀疑路径。如果它动了但连接依然成功,说明丢弃在后面被覆盖,问题出在顺序上。这个答案决定了修复是改规则还是改拓扑,所以在动手编辑任何东西之前先拿到它。
选择一个能真正过滤的边界
如果策略必须放在主机防火墙上,就把流量放到防火墙看得见的地方。关闭内部转发是受支持的做法,它能让对端流量经过隧道接口和转发路径,代价是每个对端数据包都要走一遍主机协议栈。
然后精确匹配这条流。流量在两个方向上都从隧道接口进来、又从隧道接口出去,因此只考虑物理接口的规则不会命中。基于地址的规则需要当前的地址池,而且必须同时考虑回程方向和去程方向。
很多部署更适合在客户端或组这一层过滤,那里身份是已知的,策略跟随用户,而不是跟随一个重连就会变化的地址。这与主机链是两种不同的机制,没有明确理由不应该混用。
不要把旧拓扑的规则集原样带过来。地址池、接口名和转发模式都会影响规则是否命中,而一条复制过来、以前能用的规则是最具误导性的证据。从你希望允许的那条流出发,写出与它匹配的规则。
用测试隔离,而不是相信一条规则
在同一次测试里同时验证被禁止的情形和被允许的情形。从一个客户端向另一个客户端发起新连接并确认它失败,然后确认本应被允许的连接仍然可用。如果只测前者,一个把一切都挡住的防火墙看起来就像成功。
在重新加载服务器配置之后、以及客户端重连之后都要重复一遍,因为内部转发和地址池分配都是运行时状态。两个方向都要检查计数器,因为只命中一个方向、不命中回应的规则会造成部分隔离,而这种隔离在快速测试里很难注意到。
隔离是在真正承载这条流的边界上观察到的结果。主机某处的 DROP 规则不是证据;一次失败的连接,加上导致它的那条规则的计数器发生移动,才是证据。