2025-05-06  2025-05-07    1449 字  3 分钟
UFW
  • 端口A转发到本地的端口B
  • 端口A转发到另一台机器的端口B(需借助DNAT)

一般情况下, 我们配置ufw来实现端口转发时会在修改 /etc/ufw/before.rules 文件, 增加*nat部分.

但当ufw reload或重启ufw时, ufw并不会自动帮我们清除原本的*nat规则, 导致每次刷新ufw都会产生一份新的*nat, 重启一次增加一份.

除非手动执行iptables -F -t nat来清除旧规则, 但这又会导致另一个问题, docker创建的规则也会被这个命令清理.

再除非你创建一个新Chain……

上面的操作不管怎么套娃, 都避免不了手动执行某个命令, 因此我期望寻找一种让ufw自动管理的方案, 于是就有了这篇文章.

思路构建

  • 首先我不想让ufw默认允许转发, 也就是说我不想修改/etc/default/ufw文件
  • 其次, 我不想修改/etc/ufw/sysctl.conf文件, 因为我在/etc/sysctl.conf中开启了net.ipv4.ip_forward

我们知道MASQUERADE规则需要配置在POSTROUTING中, REDIRECTDNAT规则需要配置在POSTROUTING中.

所以我们需要自定义两个新的Chain, 分别附加到 PREROUTINGPOSTROUTING 两个部分.

我们仍然将规则编写在/etc/ufw/before.rules中, 并附加在自己的这两个Chain中

每次ufw创建规则前, 自动删除我们自定义的这两个Chain的所有规则, 之后ufw会再次将/etc/ufw/before.rules中自定义的规则添加

before.init 自动删除

首先我们要修改的是 /etc/ufw/before.init 这个文件, 虽然ufw并没有自动帮我们删除的功能, 但是它贴心的准备了这个文件, 用于在ufw初始化之前执行一些指令.

我们需要为这个文件添加执行权限, 这样ufw才会执行这个文件

1chmod a+x /etc/ufw/before.init

在这个文件中, 我们主要修改的就是casestartstop这两个部分.

  • 创建了 ufw-nat-pre 附加到 PREROUTING
  • 创建了 ufw-nat-post 附加到 POSTROUTING

我的完整case如下, 如果想重新起名直接替换即可

 1case "$1" in
 2start)
 3    # typically required
 4    if iptables -t nat -L -n >/dev/null 2>&1;then
 5        printf "*nat\n"\
 6":PREROUTING ACCEPT [0:0]\n"\
 7":POSTROUTING ACCEPT [0:0]\n"\
 8"COMMIT\n" | iptables-restore -n
 9    fi
10    # flush the chains (if they exist)
11    if iptables -t nat -L ufw-nat-pre -n >/dev/null 2>&1; then
12        iptables -t nat -D PREROUTING -j ufw-nat-pre 2>/dev/null || true
13        iptables -t nat -F ufw-nat-pre 2>/dev/null || true
14        iptables -t nat -X ufw-nat-pre 2>/dev/null || true
15    else
16        # setup nat chains
17        printf "*nat\n"\
18":ufw-nat-pre - [0:0]\n"\
19"-A PREROUTING -j ufw-nat-pre\n"\
20"COMMIT\n" | iptables-restore -n
21    fi
22    if iptables -t nat -L ufw-nat-post -n >/dev/null 2>&1; then
23        iptables -t nat -D POSTROUTING -j ufw-nat-post 2>/dev/null || true
24        iptables -t nat -F ufw-nat-post 2>/dev/null || true
25        iptables -t nat -X ufw-nat-post 2>/dev/null || true
26    else
27        # setup nat chains
28        printf "*nat\n"\
29":ufw-nat-post - [0:0]\n"\
30"-A POSTROUTING -j ufw-nat-post\n"\
31"COMMIT\n" | iptables-restore -n
32    fi
33    ;;
34stop)
35    # typically required
36    if iptables -t nat -L ufw-nat-pre -n >/dev/null 2>&1; then
37        iptables -t nat -D PREROUTING -j ufw-nat-pre 2>/dev/null || true
38        iptables -t nat -F ufw-nat-pre 2>/dev/null || true
39        iptables -t nat -X ufw-nat-pre 2>/dev/null || true
40    fi
41    if iptables -t nat -L ufw-nat-post -n >/dev/null 2>&1; then
42        iptables -t nat -D POSTROUTING -j ufw-nat-post 2>/dev/null || true
43        iptables -t nat -F ufw-nat-post 2>/dev/null || true
44        iptables -t nat -X ufw-nat-post 2>/dev/null || true
45    fi
46    ;;
47status)
48    # optional
49    ;;
50flush-all)
51    # optional
52    ;;
53*)
54    echo "'$1' not supported"
55    echo "Usage: before.init {start|stop|flush-all|status}"
56    ;;
57esac

before.rules 自定义规则

首先我们要修改的是 /etc/ufw/before.rules 这个文件, 直接在文件的末尾添加即可

如何添加iptables规则需要自行搜索或询问AI, 我这里仅提供示例便于读者知道自定义规则怎么添加, 以及添加在什么地方

注意端口转发到另一个IP的端口需要在两个部分都配置

 1*nat
 2:ufw-nat-pre - [0:0]
 3# 本机的 20000 端口的 tcp 和 udp 转发到本机的 22 端口
 4-A ufw-nat-pre -p tcp --dport 20000 -j REDIRECT --to-port 22
 5-A ufw-nat-pre -p udp --dport 20000 -j REDIRECT --to-port 22
 6# 本机的 20001 端口的 tcp 和 udp 转发到 1.1.1.1 的 22 端口 (注意: 还需要配置 ufw-nat-post
 7-A ufw-nat-pre -p tcp --dport 20001 -j DNAT --to-destination 1.1.1.1:22
 8-A ufw-nat-pre -p udp --dport 20001 -j DNAT --to-destination 1.1.1.1:22
 9# end
10COMMIT
11
12*nat
13:ufw-nat-post - [0:0]
14# 本机的 20001 端口的 tcp 和 udp 转发到 1.1.1.1 的 22 端口 (注意: 还需要配置 ufw-nat-pre
15-A ufw-nat-post -d 1.1.1.1 -p tcp --dport 22 -j MASQUERADE
16-A ufw-nat-post -d 1.1.1.1 -p udp --dport 22 -j MASQUERADE
17# end
18COMMIT

完成

注意: 如果先前就创建了规则且没有删除, 或者不小心把docker的规则删掉了, 可以重启下系统来刷新

这样我们就可以愉快的在 before.rules 添加规则, 然后使用 ufw reload 重载规则了

除另有声明外本博客文章均采用 知识共享 (Creative Commons) 署名 4.0 国际许可协议 进行许可转载请注明原作者与文章出处