每当 VPN 遇上 OpenWrt,三件事总会被混在一起:开放端口、允许转发和开启地址伪装。它们回答的是不同问题,通常只有一项才是真正需要的。按流量所扮演的角色来读每一项控制,猜测就会变成判断,也会告诉你改动出错时该期待什么症状。先说出失败的是哪个角色,而不是你记得的那个复选框。

动手改控制项之前,先命名流量角色

区域输入决定什么能到达路由器本身。区域输出覆盖路由器自己产生的流量,区域转发是穿过区域流量的默认值,地址伪装则决定出站时是否改写源地址。这是四个不同的问题,而区域策略用同一个值一次性回答全部,所以整区改动是件粗笨的工具。一条具名规则可以只回答其中一个,而不碰其余。

有用的地图是按流量角色划分的,因为出问题时你能观察到的正是角色。下面每个角色都有一项决定它的控制,以及一个指向它的症状:

Traffic roleControl that decides itWhere it lives in the configSymptom when it is wrong
A client reaching a listener on the routerInput policy of the zone that carries the connection, or a rule for the portoption input in the zone stanza, or a named ruleThe handshake never completes and the tunnel log says nothing
A tunnel client reaching the router itselfInput policy of the tunnel zoneoption input in the VPN zoneA ping to the gateway fails
A tunnel client reaching another LAN hostForwarding from the tunnel zone to the LAN zoneconfig forwarding plus the zone assignmentThe gateway answers and the file share times out
The router's own traffic leaving the WANOutput policy, and translation if the far side needs itoption output and option masqThe tunnel never establishes, or replies vanish

路由器上的监听服务只需要一条狭窄的输入规则

路由器上的 OpenVPN 服务器接收发往路由器本身的流量,因此它的外层连接取决于承载它的区域的输入策略。放行所配置的传输方式与端口,其余一律保持拒绝。单条端口规则比整区放行小得多,而且一年之后你还能解释它。

有两件事让这条规则值得保留:它点名了服务实际使用的端口,而且就写在区域定义旁边,下一个人能看懂它为何存在。下面几行只是形态,端口代指服务器上配置的值。服务迁移时两处都要改,因为留在旧端口上的规则要等到审计才会被发现。

config zone
    option name 'wan'
    option input 'REJECT'
    option forward 'REJECT'
    option output 'ACCEPT'
    option masq '1'

config rule
    option name 'allow-openvpn'
    option src 'wan'
    option proto 'udp'
    option dest_port '1194'
    option target 'ACCEPT'

重载之前先校验,因为防火墙文件里的语法错误可能让路由器失去可用的规则集。使用 fw4 的版本里,检查是独立命令,它会大声失败,而不是默默接受坏掉的一段。确认通过之后再重载,并把上一份文件留在能还原的位置。

fw4 check
service firewall reload

客户端请求走的是转发,不是输入

认证之后,隧道客户端请求 LAN 服务产生的是被转发的流,因此决定它的是隧道区域与 LAN 区域之间的转发关系。把隧道接口指派给目标区域,再声明方向、来源与服务。区域指派才让转发规则有意义,两者必须合起来读。把隧道区域写成来源、LAN 区域写成目的地的转发关系读起来是一个有意的决定,而两次整区放行读起来像事故。

到达路由器本身仍然走输入路径,所以 ping 网关并不能说明它后面的文件共享。这两种失败住在不同的配置块里,要用不同的改动修复。改任何一条之前,把两条路径分别写下来。

地址伪装是路由决定,不是许可

地址伪装改写选定出站流量的源地址。它可以挽救远端没有回程路由的出口设计,也会让按地址记录或授权的服务看不到单个客户端的地址。关掉它是一个关于回程的路由选择,而不是堵住入站漏洞的安全开关。开着转换能工作、一关就失败的隧道,通常缺的是回程路由,不是防火墙规则。

走路由的替代方案需要在远端有回程路由,而不是做转换。两种方案都不提供防火墙许可或应用账户,所以关掉地址伪装几乎从不能修好一个哪儿都到不了的隧道客户端。IPv4 与 IPv6 策略也要分开处理,因为一个转换设置描述不了两个协议族。

用一条放行和一条拒绝的请求来证明判断

发一条应当放行的请求和一条应当拒绝的请求,然后指出做出每一次决定的那项控制。碰巧加载出来的页面不是证据;证据是匹配上的规则,以及它匹配在哪个链里。拒绝和成功一样重要,因为它说明策略在做事,而不是一切都放行。

当前版本上有两条命令能给出这个证据。请求被接受时,匹配规则上的计数器会动;如果你为该规则开了日志,日志会显示丢弃:

nft list chain inet fw4 forward_wan
logread -f | grep -i -E 'DROP|REJECT'

改任何防火墙策略之前,先留一条仍能用的管理通路,并遵循配置负责人的校验与回滚流程。把人锁在路由器之外的改动不是测试结果,而是第二次事故。每次观察都记下区域与流量角色,让这对结果解释策略,而不是描述一次侥幸的页面加载。

相关文章