本文对比了 linux 环境各类防火墙工具,还展示了 iptables 规则如何保存到文件并翻译成 nftables 规则,并给出了 nftables 与 openvpn 配合对混合云内网用户访问权限的精准控制方案。
先看下目前的架构:
1. 选型与对比
鉴于之前写的 VPN 权限管理项目的缺点,以及对比 iptables(ipset)、nftables、ebpf-iptables 后,确定过滤网络数据包的底层工具还是选用 nftables 而不是 iptables ipset。
理由如下:
- ebpf 很优秀,但需要 C 编程,开发效率低下且目及处无人会 C;其 golang 库小众年久失修;
- 红帽自己的 iptables 和 ipset 替代方案 nftables 纳入考虑;而且有 golang 的比较成熟的库;
- iptables ipset,优点是只需要熟悉一下 ipset,有 golang 的两个库;缺点是既然 1 1=2,为什么要用两个东西,封装学习两个 golang 库?
再次总结下 nftables 的优点:
- 相较于 ebpf,是完整的防火墙实现,有命令行;ebpf 是底层,有特别小众的防火墙应用 【方便能上手】
- 内置查找表而不是线性处理 【处理速度更快】
- 以原子方式应用规则,而不是获取、更新和存储完整的规则集 【这一点对开发管理极其有利】
2. 实践应用考虑
旧的管理只有 iptables,生产上没用过 ipset,现在决定直接用 nftables 替代 iptables;
旧的 iptables 规则有三部分,按照从用户到目标服务器的顺序为:
- openvpn 的基础 iptables 规则,把来自 openvpn 虚拟 IP 网段的用户的请求 全部通过 openvpn 服务器的 eth0 网卡转发出去 也就是我们平时所说的 IPtables 的 NAT 规则,并进行原地址伪装例如
iptables -t nat -A POSTROUTING -s 10.10.0.0/16 -o eth0 -j MASQUERADE
10.10.0.0/16 为 openvpn 用户虚拟 IP 网段【在 VPN 中枢 需要改变为 nft】 - 各个 VPC 中 wireguard 中继器的配置中中继器负责路由整个 VPC 流量的路由【中继器不作改动 还用 iptables】
- 真正作用于用户访问内网地址的业务规则【在 VPN 中枢服务器 需要改变为 nft】
3. openvpn 权限控制原理
VPN 权限管理的核心是 masquerade,即源地址伪装:VPN 用户访问内网的流量全都在 VPN 服务器这里进行路由和转发。
当一个数据包走到 VPN 服务器时,netfilter 将数据包的源 IP 伪装成本机(VPN 服务器)的地址,然后根据规则将数据包送往不同的地址。
例如nft insert rule ip nat postrouting oifname eth0 ip saddr 10.121.0.3 ip daddr 192.168.3.0/24 tcp dport 80 counter jump masquerade
,从 VPN 虚拟 IP10.121.0.3 到 192.168.3.0/24 的 80 端口的流量,前者源地址被 masquerade 成本机地址,然后就成了本机发往 192.168.3.0/24 的数据包,再参考路由表,发现路由表有这样一条路由,于是数据包就从本机的 etho 接口转给了 wg0 接口。wg0 就是 wireguard 服务的接口,于是数据包就发给了 wireguard 内网的对应 peer 上,再由该 peer 通过 eth0 网卡转发到本地网络即可。
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default gateway 0.0.0.0 UG 100 0 0 eth0
192.168.0.0 0.0.0.0 255.255.0.0 U 0 0 0 wg0
因此,我要这里要保证:
- 各个云的 ACL 和安全组配置好、wg 中继器 VPC 的路由配置好
- wireguard 混合云的各个 VPC 与这台 VPN 服务器(中枢)全通
- 所有的控制点都放在 VPN 中枢,用是否有用户到目的地的规则来控制访问权限,粒度可以精细到【哪个用户>>目的 ip-协议-端口】
因此业务场景下我们要先准备好基础规则:
4. nftables 替换 iptables 规则步骤及测试
维护 iptables 基本的规则,未来将此 iptables 规则全部转换为 nftables 规则
代码语言:javascript复制# 先清空规则,然后INPUT OUTPUT FORWARD全接受,如果drop会让22端口也被干掉!
iptables -F
iptables -X
iptables -Z
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT
# 查询nat表所有规则
iptables -t nat -L --line-numbers
# 删除nat表POSTROUTING链规则的第一条
iptables -t nat -D POSTROUTING 1
然后到 ubantu 用翻译工具翻译成 nftables 规则:
代码语言:javascript复制必要:ubantu linux 内核高于 4.8 才能支持 iptables 翻译工具!
# 保存iptables规则
iptables-save > default.rules
# 将文件default.rules发送到一台有iptables翻译nftables规则的ubantu上并输入nft命令
iptables-restore-translate -f default.rules
得到 nft 命令,这个命令在关掉 iptables 以及 iptables 的 nat 后进入 nftables 的交互模式执行:
代码语言:javascript复制# Translated by iptables-restore-translate v1.6.1 on Tue Jul 6 16:30:39 2021
add table ip filter
add chain ip filter INPUT { type filter hook input priority 0; policy accept; }
add chain ip filter FORWARD { type filter hook forward priority 0; policy accept; }
add chain ip filter OUTPUT { type filter hook output priority 0; policy accept; }
add table ip nat
add chain ip nat PREROUTING { type nat hook prerouting priority 0; policy accept; }
add chain ip nat INPUT { type nat hook input priority 0; policy accept; }
add chain ip nat OUTPUT { type nat hook output priority 0; policy accept; }
add chain ip nat POSTROUTING { type nat hook postrouting priority 0; policy accept; }
add rule ip nat POSTROUTING oifname eth0 ip saddr 10.121.6.6 ip daddr 192.168.5.77 counter masquerade
add rule ip nat POSTROUTING oifname eth0 ip saddr 10.121.6.6 ip daddr 10.10.210.11 counter masquerade
# Completed on Tue Jul 6 16:30:39 2021
关闭 iptables 服务&卸载 iptables 的相关 nat 模块,只有先关闭 iptables 的 nat 模块,流量才能走 nft 的 nat
代码语言:javascript复制# 停止iptables服务
systemctl stop iptables.service
# 查看并确定服务是停止状态
systemctl status iptables.service
# 卸载nat模块
modprobe -v -r ip6table_nat
modprobe -v -r iptable_nat
modprobe -v -r ip_nat_ftp
跑 nft 命令生成对应的 nft 规则
代码语言:javascript复制# -i参数进入交互模式
nft -i
# 执行从iptables基础规则转换来的nftables规则
add table ip filter
add chain ip filter INPUT { type filter hook input priority 0; policy accept; }
add chain ip filter FORWARD { type filter hook forward priority 0; policy accept; }
add chain ip filter OUTPUT { type filter hook output priority 0; policy accept; }
add table ip nat
add chain ip nat PREROUTING { type nat hook prerouting priority 0; policy accept; }
add chain ip nat INPUT { type nat hook input priority 0; policy accept; }
add chain ip nat OUTPUT { type nat hook output priority 0; policy accept; }
add chain ip nat POSTROUTING { type nat hook postrouting priority 0; policy accept; }
add rule ip nat POSTROUTING oifname eth0 ip saddr 10.121.6.6 ip daddr 192.168.5.71 counter masquerade
add rule ip nat POSTROUTING oifname eth0 ip saddr 10.121.6.6 ip daddr 10.10.210.18 counter masquerade
# 增加测量流量规则
add rule filter INPUT counter
add rule nat POSTROUTING counter
# 查看目前的nft规则
nft list ruleset -a
table ip filter {
chain INPUT {
type filter hook input priority 0; policy accept;
counter packets 2530 bytes 583977 # handle 4
}
chain FORWARD {
type filter hook forward priority 0; policy accept;
}
chain OUTPUT {
type filter hook output priority 0; policy accept;
}
}
table ip nat {
chain PREROUTING {
type nat hook prerouting priority 0; policy accept;
}
chain INPUT {
type nat hook input priority 0; policy accept;
}
chain OUTPUT {
type nat hook output priority 0; policy accept;
}
chain POSTROUTING {
type nat hook postrouting priority 0; policy accept;
counter packets 335 bytes 22534 # handle 11
oifname "eth0" ip saddr 10.121.6.6 ip daddr 192.168.5.77 counter packets 0 bytes 0 masquerade # handle 12
oifname "eth0" ip saddr 10.121.6.6 ip daddr 10.10.210.11 counter packets 0 bytes 0 masquerade # handle 13
}
}
测试
这边 openvpn 的服务端配置的用户虚拟 IP 网段是 10.121.0.0/16,subnet 拓扑模式
客户端(win10)连通 openvpn 后,ping 192.168.5.77 和 ping 10.10.210.11
代码语言:javascript复制通过查看流量可以看出流量通过了nft的POSTROUTING链的两条规则:
table ip filter {
chain INPUT {
type filter hook input priority 0; policy accept;
counter packets 12481 bytes 4032765 # handle 4
}
chain FORWARD {
type filter hook forward priority 0; policy accept;
}
chain OUTPUT {
type filter hook output priority 0; policy accept;
}
}
table ip nat {
chain PREROUTING {
type nat hook prerouting priority 0; policy accept;
}
chain INPUT {
type nat hook input priority 0; policy accept;
}
chain OUTPUT {
type nat hook output priority 0; policy accept;
}
chain POSTROUTING {
type nat hook postrouting priority 0; policy accept;
counter packets 2318 bytes 156490 # handle 11
oifname "eth0" ip saddr 10.121.6.6 ip daddr 192.168.5.77 counter packets 3 bytes 180 masquerade # handle 12
oifname "eth0" ip saddr 10.121.6.6 ip daddr 10.10.210.11 counter packets 3 bytes 180 masquerade # handle 13
}
}
这里可以看到
代码语言:javascript复制oifname "eth0" ip saddr 10.121.6.6 ip daddr 192.168.5.77 counter packets 3 bytes 180 masquerade # handle 12
oifname "eth0" ip saddr 10.121.6.6 ip daddr 10.10.210.11 counter packets 3 bytes 180 masquerade # handle 13
两条规则有流量经过,我们在 vpn 客户端也得到了 ICMP 的正确返回。
因为实际生产上需要先给大家统一分配,等稳定后再优化为细粒度的权限控制,最终的规则写在 nftables 日志实践一文中了,另外还有 openvpn 服务端的配置也没放出来,未来写上层权限控制平台的时候,一并给出。
5. 单个 iptables 规则翻译成 nftables 规则
VPN 用户的 权限主要依赖于防火墙的 masquerade,因此用 iptables 时候,用程序生成最多的就是地址伪装规则,例如:
代码语言:javascript复制iptables -t nat -I POSTROUTING -s 10.121.0.3 -d 192.168.3.0/24 -o eth0 -p tcp --dport 80 -j MASQUERADE;
含义是虚拟 IP 为 10.121.0.3 的用户到 192.168.3.0/24 网段的流量,伪装成本机发送的流量。
因为已经考虑完全用 nftables 替代 iptables 了所以要考虑用 nftables 来组织规则,该怎么写呢?我尝试执行了自己猜测的语句:
代码语言:javascript复制nft add rule nat postrouting ip saddr 10.121.0.3 tcp sport 80 oifname eth0 counter masquerade
但是还是没有对目标地址进行翻译-d 192.168.3.0/24
于是改写成规则:nft add rule nat postrouting ip saddr 10.121.0.3 tcp sport 80 ip daddr 192.168.3.0/24 tcp dport 80 oifname eth0 counter masquerade
会有语法错误:
同事买了一台 ubantu 安装了翻译工具apt install iptables-nftables-compat
翻译出的句子如下:
代码语言:javascript复制iptables-translate -t nat -I postrouting -s 10.121.0.3 -d 192.168.3.0/24 -o eth0 -p tcp --dport 80 -j masquerade;
nft insert rule ip nat postrouting oifname eth0 ip saddr 10.121.0.3 ip daddr 192.168.3.0/24 tcp dport 80 counter jump masquerade
我的问题在于,在 centos7 内核 5.12 的环境下 yum search 翻译工具没找到,没想到去安装一台 ubantu;其次语法上确实在官方文档没找到这么复杂的案例,因此没遵循语法写错了
6. 参考资料
- Moving from iptables to nftables[1]
脚注
[1]
Moving from iptables to nftables: https://www.oschina.net/action/GoToLink?url=https://wiki.nftables.org/wiki-nftables/index.php/Moving_from_iptables_to_nftables