不停隧道,只把一个 OpenVPN 客户端挡在 LAN 之外
把客户端的隧道地址固定到证书上,在 FORWARD 链首丢弃它的 LAN 流量,然后验证 IPv6、已跟踪会话和重启后的持久性。
你的 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 会话里的幻觉。