你的 OpenVPN 服务器上有一个客户端必须失去 LAN 访问,而其他隧道全部保持在线。搜索这个问题,最上面的答案通常说守护进程做不到,这只对了一半:OpenVPN 没有按客户端划分的防火墙,但终结隧道的那台主机本来就在过滤数据包。真正重要的两个决定是你要封的身份和规则的位置。这两点做对,改动就变成一行可以测试、可以撤销的规则。

写规则之前先固定客户端

OpenVPN 从地址池分配隧道地址,所以 10.8.0.6 是一个座位,不是一个人。会话结束后租约归还,可能发给下一个连接,这意味着针对该地址写的规则跟着座位走,而不是跟着人走。所以先决定你真正要封的是什么:一次短实验用的地址,还是某个你想长期挡在外面的人的证书。

要把地址绑定到证书,就在 client-config-dir 指定的目录里建一个文件,文件名必须与证书的 common name 完全一致。里面写一条指令 ifconfig-push 10.8.0.6 255.255.255.0,该客户端重连后仍保持同一隧道地址。要生效必须有两个设置:client-config-dir 必须在服务器配置里声明,推送的地址必须落在服务器能路由的子网里。缺任何一个,文件会被静默忽略而不报错,这会让接下来的防火墙测试得出错误结论。

临时封禁可以用地址池里的地址,只要工单记录当时是哪个会话持有它。

顺序决定丢弃规则是否执行

在隧道与 LAN 之间转发的 Linux 网关上,数据包会经过 FORWARD 链。把下面这条命令当句子读:插入到 FORWARD 的首部,匹配从 tun0 进入、来自那一个主机、发往 LAN 子网的流量,然后丢弃。

iptables -I FORWARD -i tun0 -s 10.8.0.6/32 -d 192.168.1.0/24 -j DROP

让它生效的是 -I。如果用 -A 追加,它会落在针对隧道子网的宽泛放行规则之后,永远看不到数据包。用 iptables -vnL FORWARD --line-numbers 确认位置,也要看计数器:首部的丢弃规则计数为零,说明流量是从另一条路来的。

网关自己的 LAN 地址是另一条路。发给这台机器本身的数据包走 INPUT,不走 FORWARD,所以客户端可能失去某个 LAN 主机,却仍能打开路由器管理页。需要时补上对应的 INPUT 规则。

过滤器看不到的东西

过滤发生在数据包被评估的时候,所以已建立的 TCP 会话会一直持续到关闭或被重置。如果封禁必须立刻见效,就在规则写入后用 conntrack -D -s 10.8.0.6 清掉该来源的跟踪流。如果能接受几秒钟的重叠,就把这个选择写下来,而不是留在隐含状态。无论如何,下一个人需要知道这条规则是切断现有流量,还是只挡新连接。

对等端之间的流量永远不碰主机防火墙。启用 client-to-client 时,OpenVPN 在守护进程内部转发这些数据包,所以 FORWARD 和 INPUT 都看不到它们。唯一的办法是关掉 client-to-client,或者接受对等端可以互相访问;iptables 规则看不到这部分流量。

IPv6、固件与重启后的持久性

IPv4 规则对 IPv6 什么也没说。如果隧道和 LAN 同时承载两族地址,等价规则必须存在于 ip6tables 中,或存在于该主机上掌管策略的 nftables 规则集里。路由器固件常把 netfilter 藏在自己的抽象后面,所以从论坛抄来的语法和表名可能并不适用。

在 OpenWrt 上,有文档的路径是防火墙配置项,而不是手写的 iptables 行;手写的行在防火墙重载时会消失。其他发行版通过 netfilter-persistent、开机单元或厂商存储来恢复规则,这些机制不能互换。你选哪一种都要用一次重启来证明,因为重载走的是另一条路径。

按路径验证,然后干净回滚

每条路径单独测试,因为一次成功的检查会掩盖其他问题。被封的客户端应该仍能经隧道上网,但对 LAN 主机失败;第二个客户端应该能访问同一台主机;网关地址和对等路径各自都需要单独观察。把结果写在规则旁边,让下一次改动有基线。下面这张表是值得留在工单里的那组检查。

路径有规则时的预期失败时
被封客户端访问 LAN 主机无响应规则位于放行之后,或来源不匹配
被封客户端访问互联网经 tun0 正常规则过宽,没有目的地址匹配
第二个客户端访问同一 LAN 主机正常规则匹配了超出预期的主机
被封客户端访问网关 LAN 地址只有配好对应的 INPUT 规则才会被封缺少 INPUT 规则
被封客户端访问对等端隧道地址仍然正常client-to-client 已启用,超出防火墙范围

回滚是按规格删除,而不是清空整条链:iptables -D FORWARD -i tun0 -s 10.8.0.6/32 -d 192.168.1.0/24 -j DROP 只删掉那一条,链的其余部分保持不变。同时从开机恢复规则的文件里删掉同一行,重新加载,并确认客户端又能访问 LAN 主机。清空 FORWARD 会连你没写的规则一起撤销,把回滚变成第二次事故。

关单之前确认三件事:固定的地址与证书一致,规则位于放行之上且计数器非零,持久化文件里就是你测过的那条规则。缺任何一条,这次封禁只是 shell 会话里的幻觉。

相关文章