Technical note
RouterOS 7的IPv4与IPv6防火墙基线
RouterOS的防火墙不难写,真正容易出错的是规则顺序。前面一条过于宽泛的drop,会让后面的黑名单规则永远没有机会命中;一条看似普通的FastTrack,也可能绕过策略路由、队列或IPsec。
最终保留的是一套很朴素的基线:先放行已经建立的连接,再处理必要的控制流量和内网流量,最后统一丢弃。端口扫描识别、MSS调整和FastTrack都不塞进基础规则,需要时再单独增加。
动手前先留退路
防火墙改错以后,最现实的问题不是网络不通,而是远程管理连接也会一起断掉。先导出IPv4和IPv6配置:
/ip firewall export file=firewall-ipv4-before
/ipv6 firewall export file=firewall-ipv6-before
远程修改时建议打开Safe Mode。终端里按Ctrl+X或F4,提示符出现<SAFE>以后再逐段添加规则;连接异常中断时,本次改动会自动回滚。一次不要改太多,RouterOS用于回滚的历史记录数量有限。
下面的规则使用LAN和WAN接口列表。应用前必须先确认接口归属正确,尤其不要把当前管理入口漏在LAN之外。已有防火墙也不要直接重复粘贴,应该按相同顺序合并到现有规则中。
IPv4:区分访问路由器和穿过路由器
input链处理的是发给路由器本身的流量,例如WinBox、SSH、DNS和VPN入口。这里默认只信任LAN,公网不开放任何管理端口:
/ip firewall filter
add chain=input action=accept connection-state=established,related,untracked \
comment="base: accept established, related and untracked"
add chain=input action=drop connection-state=invalid \
comment="base: drop invalid"
add chain=input action=accept protocol=icmp \
comment="base: accept ICMP"
add chain=input action=accept in-interface-list=LAN \
comment="base: accept traffic from LAN"
add chain=input action=drop \
comment="base: drop everything else"
如果确实要从公网接入WireGuard或其他服务,应当在最后一条drop之前增加精确的协议和目标端口规则。WinBox、SSH这类管理服务仍然更适合只从内网或VPN访问,而不是为了方便直接暴露到WAN。
forward链处理经过路由器转发的流量。内网可以主动访问外部;公网只能进入已经做过dstnat的服务,其余新连接全部落到最后的丢弃规则:
/ip firewall filter
add chain=forward action=accept connection-state=established,related,untracked \
comment="base: accept established, related and untracked"
add chain=forward action=drop connection-state=invalid \
comment="base: drop invalid"
add chain=forward action=accept in-interface-list=LAN \
comment="base: accept connections from LAN"
add chain=forward action=accept in-interface-list=WAN connection-state=new \
connection-nat-state=dstnat \
comment="base: accept destination NAT from WAN"
add chain=forward action=drop \
comment="base: drop everything else"
这组规则有一个刻意的取舍:LAN里的设备彼此信任。如果网络里还有访客Wi-Fi、IoT或服务器VLAN,就不应把它们一股脑放进同一个可信接口列表,而要分别写清楚允许访问的目标。
IPv6:不要照搬IPv4思路
IPv6通常没有IPv4家用网络里那层NAT遮挡,终端拿到公网地址后,是否允许外部访问主要取决于forward链。因此IPv6防火墙不能因为“暂时没用IPv6”就留空。
路由器本身需要保留ICMPv6。它不只是用来ping,邻居发现、路径MTU发现等机制也依赖ICMPv6。DHCPv6前缀委派则允许来自链路本地地址、发往客户端546端口的UDP报文:
/ipv6 firewall filter
add chain=input action=accept connection-state=established,related,untracked \
comment="base: accept established, related and untracked"
add chain=input action=drop connection-state=invalid \
comment="base: drop invalid"
add chain=input action=accept protocol=icmpv6 \
comment="base: accept ICMPv6"
add chain=input action=accept protocol=udp dst-port=546 src-address=fe80::/10 \
comment="base: accept DHCPv6 prefix delegation"
add chain=input action=accept in-interface-list=LAN \
comment="base: accept traffic from LAN"
add chain=input action=drop \
comment="base: drop everything else"
如果运营商使用DHCPv6 Relay,服务器报文的源地址未必属于fe80::/10,这时需要按实际中继地址调整源地址条件,不能只为了“先通再说”而长期删除限制。
转发规则继续保留ICMPv6,只允许内网发起新连接:
/ipv6 firewall filter
add chain=forward action=accept connection-state=established,related,untracked \
comment="base: accept established, related and untracked"
add chain=forward action=drop connection-state=invalid \
comment="base: drop invalid"
add chain=forward action=accept protocol=icmpv6 \
comment="base: accept ICMPv6"
add chain=forward action=accept in-interface-list=LAN \
comment="base: accept connections from LAN"
add chain=forward action=drop \
comment="base: drop everything else"
需要从公网访问某台IPv6设备时,可以在最后一条drop之前增加目标地址、协议和端口都明确的放行规则。相比直接放开整个地址段,这样更容易看懂,也更容易从计数器发现异常。
FastTrack为什么没有放进基线
FastTrack适合普通的IPv4家庭路由,但它会让流量绕过部分防火墙处理、队列、IPsec、流量统计和VRF分配,而且路由查找只使用main表。配置了策略路由、路由标记或特殊队列时,直接FastTrack所有established,related连接很容易得到时好时坏的结果。
只使用主路由表、确认没有这些依赖时,可以把下面两条放在IPv4forward链前部:
/ip firewall filter
add chain=forward action=fasttrack-connection \
connection-state=established,related hw-offload=yes \
comment="optional: fasttrack established and related"
add chain=forward action=accept connection-state=established,related \
comment="optional: accept established and related"
第二条不能省略,因为并非一个FastTrack连接中的每个包都会走快速路径。IPv6的fasttrack-connection也不应该照抄:RouterOS当前的过滤器文档明确将这个动作标为仅支持IPv4。
端口扫描识别不是第一道防线
只要最后的WAN丢弃规则生效,端口探测本身已经无法进入路由器或内网。使用psd把来源加入地址列表,更多是为了分类和观察,而不是替代基础防火墙。
这里还有一个容易忽略的细节:psd根据同一来源访问不同的目标端口来判断扫描,额外写src-port=!443,80过滤的是扫描方的源端口,并不能排除对本机80和443端口的探测。与其维护一组作用可疑、还会持续写日志的黑名单规则,不如先看清是否真的需要这份统计。
规则调整完成后,可以从计数器确认流量实际命中了哪里:
/ip firewall filter print stats
/ipv6 firewall filter print stats
重点看最后几条drop是否有计数、正常业务的放行规则是否命中,以及某条规则是否长期为零。防火墙最终靠顺序生效,注释写得再漂亮,也不能让一条排错位置的规则重新获得机会。