如何配置firewalld使内部网络及路由器本地发起的连接应用公网端口转发规则?
这个问题其实是典型的**发夹NAT(Hairpin NAT)**场景——当内部设备或者路由器自身访问网关的公网IP时,流量默认会被网关识别为发往自身的请求,走INPUT链而不是FORWARD链,所以你配置在external zone的端口转发规则根本不会生效。加上公网IP是DHCP动态分配的,不能硬编码写死,得用更灵活的配置方式,我给你分步解决:
一、搞定内部网络设备的访问需求
针对trusted zone里的内部设备,我们需要让发往网关公网IP的流量触发转发规则,同时做源地址转换(SNAT)确保回包能正确回到内部设备:
添加firewalld富规则,匹配trusted zone源、external zone目标的流量,自动转发到内部服务器,同时开启masquerade实现SNAT:
# 转发80端口 sudo firewall-cmd --permanent --zone=trusted --add-rich-rule='rule family="ipv4" source zone="trusted" destination zone="external" forward-port port="80" protocol="tcp" to-port="80" to-addr="192.168.0.2" masquerade' # 转发443端口 sudo firewall-cmd --permanent --zone=trusted --add-rich-rule='rule family="ipv4" source zone="trusted" destination zone="external" forward-port port="443" protocol="tcp" to-port="443" to-addr="192.168.0.2" masquerade' # 转发8443端口 sudo firewall-cmd --permanent --zone=trusted --add-rich-rule='rule family="ipv4" source zone="trusted" destination zone="external" forward-port port="8443" protocol="tcp" to-port="8443" to-addr="192.168.0.2" masquerade'这里用
destination zone="external"就不用硬编码公网IP了,firewalld会自动匹配external zone接口(也就是wan)的所有地址,完美适配DHCP动态IP的情况。重新加载firewalld规则生效:
sudo firewall-cmd --reload
这时候内部设备再访问gateway.mydomain.tld或者网关公网IP,应该就能正常转发到192.168.0.2了。
二、解决路由器自身的访问问题
路由器本地(包括用公网IP、localhost访问)的流量默认走INPUT链,需要单独在nat表的OUTPUT链添加DNAT规则,而且同样要适配动态公网IP:
方案1:用脚本动态更新规则(推荐)
因为公网IP会变,我们写个脚本自动获取当前wan接口的IP,更新DNAT规则:
创建脚本
/usr/local/bin/update_local_dnat.sh:#!/bin/bash # 获取当前wan接口的公网IP WAN_IP=$(ip -br addr show wan | awk '{print $3}' | cut -d/ -f1) # 先删除旧的规则(避免重复) sudo firewall-cmd --direct --remove-rule ipv4 nat OUTPUT 0 -d 0.0.0.0/0 -p tcp --dport 80 -j DNAT --to-destination 192.168.0.2:80 2>/dev/null sudo firewall-cmd --direct --remove-rule ipv4 nat OUTPUT 0 -d 0.0.0.0/0 -p tcp --dport 443 -j DNAT --to-destination 192.168.0.2:443 2>/dev/null sudo firewall-cmd --direct --remove-rule ipv4 nat OUTPUT 0 -d 0.0.0.0/0 -p tcp --dport 8443 -j DNAT --to-destination 192.168.0.2:8443 2>/dev/null # 添加新的DNAT规则 sudo firewall-cmd --direct --add-rule ipv4 nat OUTPUT 0 -d $WAN_IP -p tcp --dport 80 -j DNAT --to-destination 192.168.0.2:80 sudo firewall-cmd --direct --add-rule ipv4 nat OUTPUT 0 -d $WAN_IP -p tcp --dport 443 -j DNAT --to-destination 192.168.0.2:443 sudo firewall-cmd --direct --add-rule ipv4 nat OUTPUT 0 -d $WAN_IP -p tcp --dport 8443 -j DNAT --to-destination 192.168.0.2:8443 # 保存规则到永久配置 sudo firewall-cmd --runtime-to-permanent给脚本加执行权限:
sudo chmod +x /usr/local/bin/update_local_dnat.sh配置定时任务自动更新:
比如用systemd定时器每小时执行一次,或者在NetworkManager的钩子脚本里添加,当wan接口IP变化时自动触发脚本。
方案2:添加localhost访问的固定规则
如果你需要用localhost访问,还要单独添加针对127.0.0.1的规则:
sudo firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 0 -d 127.0.0.1 -p tcp --dport 80 -j DNAT --to-destination 192.168.0.2:80 sudo firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 0 -d 127.0.0.1 -p tcp --dport 443 -j DNAT --to-destination 192.168.0.2:443 sudo firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 0 -d 127.0.0.1 -p tcp --dport 8443 -j DNAT --to-destination 192.168.0.2:8443 sudo firewall-cmd --reload
三、验证配置
内部设备测试:
curl -sS -D - https://gateway.mydomain.tld -o /dev/null -L应该返回200状态码,和访问hq.mydomain.tld的结果一致。
路由器本地测试:
# 用公网IP测试 curl -sS -D - https://$(ip -br addr show wan | awk '{print $3}' | cut -d/ -f1) -o /dev/null -L # 用localhost测试 curl -sS -D - https://localhost -o /dev/null -L同样应该返回正常的200响应。
备注:内容来源于stack exchange,提问作者3dg3

