依据网络上检索到的步骤,在/etc/ufw/before.rules中添加PREROUTING链规则,重启或者重载UFW会出现重复的iptables条目。
这并不是一个BUG,而是UFW设计如此。它只会管理自己的链,以ufw-*开头。在重启或者重载的时候,ufw清空这些链,然后再执行配置文件中的规则。对于iptables内置的链,考虑到其他应用程序也会使用,所以UFW不会进行重置。
这里简述一下UFW的启动流程,UFW启动时,首先执行/usr/lib/ufw/ufw-init脚本,用于创建UFW所用到的自定义链和一些默认配置,这个脚本还会用于用户配置的/etc/ufw/before.init,/etc/ufw/after.init。
如果需要自定义规则,在/etc/ufw/before.init和/etc/ufw/after.init中添加。before.init会在UFW初始化前执行,after.init会在UFW初始化之后执行。
在执行完成init脚本之后,会再执行before.rules,user.rules,after.rules中定义的规则。
了解了上面的流程,就知道接下来如何处理NAT规则了。
1、通过init脚本添加自定义链,用于创建NAT规则;
2、通过rules配置NAT规则。
开启转发支持
修改文件/etc/default/ufw,将默认转发策略修改为ACCEPT。
DEFAULT_FORWARD_POLICY="ACCEPT"
修改文件/etc/ufw/sysctl.conf,开启系统转发支持。
# Uncomment this to allow this host to route packets between interfaces
net/ipv4/ip_forward=1
net/ipv6/conf/default/forwarding=1
net/ipv6/conf/all/forwarding=1
自定义初始化流程
修改文件/etc/ufw/before.init。
start)块中添加如下内容,创建NAT规则所需要的链。
start)
if iptables -t nat -L -n >/dev/null 2>&1;then
printf "*nat\n"\
":PREROUTING ACCEPT [0:0]\n"\
":POSTROUTING ACCEPT [0:0]\n"\
"COMMIT\n" | iptables-restore -n
fi
# flush the chains (if they exist)
if iptables -t nat -L ufw-nat -n >/dev/null 2>&1; then
iptables -t nat -D PREROUTING -j ufw-nat 2>/dev/null || true
iptables -t nat -F ufw-nat 2>/dev/null || true
iptables -t nat -X ufw-nat 2>/dev/null || true
else
# setup nat chains
printf "*nat\n"\
":ufw-nat - [0:0]\n"\
"-A PREROUTING -j ufw-nat\n"\
"COMMIT\n" | iptables-restore -n
fi
;;
stop)块中添加如下内容,删除自定义规则。
stop)
if iptables -t nat -L ufw-nat -n >/dev/null 2>&1; then
iptables -t nat -D PREROUTING -j ufw-nat 2>/dev/null || true
iptables -t nat -F ufw-nat 2>/dev/null || true
iptables -t nat -X ufw-nat 2>/dev/null || true
fi
;;
给before.init脚本可执行权限,这样脚本才会被ufw-init调用。
chmod a+x /etc/ufw/before.init
配置NAT规则
修改文件/etc/ufw/before.rules,在文件末尾添加如下规则:
#
# ufw-nat
#
*nat
:ufw-nat - [0:0]
-A ufw-nat -i eth0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.20.5:80
-A ufw-nat -i eth0 -p tcp -m tcp --dport 443 -j DNAT --to-destination 192.168.20.5:443
COMMIT
重载UFW
最后进行测试,重启或者重载几次UFW,通过iptables查看规则是否生效,是否有重复。
# 查看规则是否生效
ufw disable && ufw enable
iptables -t nat -L
# 重载查看是否有重复规则
ufw reload
ufw reload
iptables -t nat -L
# 停止后查看规则是否清除
ufw disable
iptables -t nat -L
在before.int和after.init阶段配置其实都是可以的,包括before.rules和after.rules,这取决于你定义的规则的依赖顺序。具体执行顺序应该是before.init -> (ufw-setup) -> after.init -> before.rules -> after.rules -> user.rules。
关于地址伪装
由于我配置了回程路由,因此转发出去的数据包能够正常返回,如果没办法配置路由,则需要开启地址伪装。
在上述的基础上,将ufw-nat链分成ufw-before-nat,ufw-aftar-nat,分别添加到PREROUTING和POSTROUTING链上,然后ufw-before-nat上配置端口转发规则,ufw-after-nat上配置地址伪装。
参考文档:
https://gist.github.com/kimus/9315140?permalink_comment_id=3139569
https://manpages.ubuntu.com/manpages/xenial/man8/ufw-framework.8.html