添加一条拒绝规则后,却发现下载仍在继续,这不是 UFW 的缺陷。你追加的规则位于已经放行该流的规则之后,而先前建立的连接会继续匹配它们。要修好这一点,就得把规则插到正确位置,并理解前面那些规则承诺了什么。下面给出这个改动的窄版本。

起决定作用的是顺序,而不是规则是否存在

UFW 是一个分阶段写入 netfilter 规则的前端:先运行框架区段,再运行用户规则,最后运行其后的区段。内置的先前规则会接受属于已建立(established)和相关(related)连接的流量,而这些规则会先于你用普通 allow 或 deny 添加的任何内容被评估。UFW 命令参考记录了插入语法以及本次改动所依赖的带编号状态视图。

正是这个顺序解释了意外。你的新规则确实存在,也确实在正确的表里,但它不是数据包遇到的第一个决定。已经按已建立状态跟踪的连接根本到不了它。

这项任务应使用 route 规则,因为数据包是经网关转发的,而不是发给网关自身的服务。输入规则会过滤错误的流量,看起来像什么都没做。

插入到宽泛许可之前

先预览插入结果再提交,并确认该规则没有已经出现在列表的其他位置。复制一条完全相同的策略会让日后删除变得含糊,而删错副本的人几周内都不会察觉。

sudo ufw --dry-run route insert 1 deny in on tun0 proto tcp from 10.88.0.55/32 to 192.0.2.88/32

如果预览显示预期位置正好只有一条规则,就不带 dry-run 标志执行同一条命令。位置 1 会把它放在更宽泛的用户放行之前,而不是前面讨论的框架规则之前。确认接口名与这台网关上的隧道设备一致,因为绑定到已不存在接口的规则会留在列表里,看起来没问题却匹配不到任何流量。要在应用之前改好设备引用,而不是等测试失败之后再改。

该规则没有指定目标端口,因此它会拒绝该地址上的所有 TCP 端口,而不只是 HTTPS。如果意图是只针对一个服务,就写明端口;比策略更宽的规则是等着第一位读者的未来事故。

只有当地址绑定了预期身份时,才把来源限制为单个地址。使用动态分配的 VPN 地址池明天可能把同一地址交给另一个客户端,拒绝就会跟着地址走,而不是跟着人走。

已建立的流会在新规则下存活

连接跟踪正是现有会话得以继续的原因。一旦某条流被记录为已建立,先前规则会在查看你的拒绝之前就接受它的数据包,传输便像什么都没发生一样完成。因此,用会复用已打开连接的浏览器去测试,并不能说明新策略的任何情况。拒绝可能在上线几分钟后显得毫无用处,而对下一个连接的用户却完全有效。

如果需求包含切断正在进行的流量,这项用户规则改动做不到。撤销被跟踪的状态是另一项操作,有它自己的影响范围,而且属于防火墙负责人,而不是添加规则的人。为了一个会话消失而清空网关上的所有连接,正是那种会引发第二次事故的修法。

也要记住边界。这条规则对 ICMP、UDP 或 IPv6 只字未提,所以一次成功的 ping 并不是拒绝失败的证据。一个 IPv4 地址也不等于一个网站:一个名字可能解析到多个地址,只有你点名的那个被覆盖。

用新流进行测试

阅读带编号的用户规则和实际生效的原始视图,然后从所选客户端发起一个新连接:

sudo ufw status numbered
sudo ufw show raw
# from 10.88.0.55, a new TCP connection to 192.0.2.88 should fail

原始视图很重要,因为 status 会省略手写的框架规则,所以顺序看起来可能与内核实际执行的不同。在断定某条规则位置不对之前,先把两者对比一下。

然后检查两个证明规则范围窄而非宽泛的对照:同一客户端到一个被允许的目标仍然可用,而第二个客户端在其策略允许时仍能到达被拒绝的目标。同时让两项测试失败的拒绝属于策略变更,而不是定向措施。

测试结束后,只删除你添加的那条规则,方式是原样重述它。如果这项策略是永久的,就把它连同客户端身份、目标负责人和复核日期交给防火墙负责人,以免重新分配的隧道地址留下过时的拒绝。

相关文章