CentOS 8 都上生产了,你还在用 iptables 吗,是时候拥抱下一代防火墙 nftables 了!
Posted 奇妙的Linux世界
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CentOS 8 都上生产了,你还在用 iptables 吗,是时候拥抱下一代防火墙 nftables 了!相关的知识,希望对你有一定的参考价值。
什么是 nftables?
nftables
是一个新式的数据包过滤框架,旨在替代现用的 iptables
的新的包过滤框架。nftables
诞生于 2008 年,2013 年底合并到 Linux 内核
,从 Linux
内核 3.13
版本开始大多数场景下 nftables
已经可以使用,但是完整的支持(即:nftables
优先级高于 iptables
)应该是在 Linux 内核 3.15
版本。
nftables
旨在解决现有 {ip/ip6}tables
工具存在的诸多限制。相对于旧的 iptables
,nftables
最引人注目的功能包括:改进性能、支持查询表、事务型规则更新、所有规则自动应用等等。
nftables
主要由三个组件组成:内核实现、libnl netlink
通信和 nftables
用户空间。其中内核提供了一个 netlink
配置接口以及运行时规则集评估,libnl
包含了与内核通信的基本函数,用户空间可以通过新引入的命令行工具 nft
和用户进行交互。
nft
可以通过在寄存器中储存和加载来交换数据。也就是说,它的语法与 iptables
不同。但 nft
可以利用内核提供的表达式去模拟旧的 iptables
命令,维持兼容性的同时获得更大的灵活性。简单来说,nft
是 iptables
及其衍生指令(ip6tables
和 arptables
)的超集。
nftables 的特点
-
-
不同于
iptables
,nftables
并不包含任何的内置表,需要哪些表并在这些表中添加什么处理规则一切由管理员决定。 -
表包含规则链,规则链包含规则。
nftables 相较于 iptables 的优点
-
更新速度更快
在 iptables
中添加一条规则,会随着规则数量增多而变得非常慢。这种状况对 nftables
而言就不存在了,因为 nftables
使用原子的快速操作来更新规则集合。
-
内核更新更少。
使用 iptables
时,每一个匹配或投递都需要内核模块的支持。因此,如果你忘记一些东西或者要添加新的功能时都需要重新编译内核。而在 nftables
中就不存在这种情况了, 因为在 nftables
中,大部分工作是在用户态完成的,内核只知道一些基本指令(过滤是用伪状态机实现的)。例如,icmpv6
支持是通过 nft
工具的一个简单的补丁实现的,而在 iptables
中这种类型的更改需要内核和 iptables
都升级才可以。
nftables 基础操作
nftables
和 iptables
一样,由表(table)、链(chain)和规则(rule)组成,其中表包含链,链包含规则,规则是真正的动作。
在 nftables
中,表是链的容器。所以开始使用 nftables
时你首先需要做的是添加至少一个表。然后,你可以向你的表里添加链,然后往链里添加规则。
nftables 的表管理
nftables 簇 | 对应 iptables 的命令行工具 |
---|---|
ip | iptables |
ip6 | ip6tables |
inet | iptables 和 ip6tables |
arp | arptables |
bridge | ebtables |
ip
(即 IPv4)是默认簇,如果未指定簇,则使用该簇。如果要创建同时适用于 IPv4
和 IPv6
的规则,请使用 inet
簇 。inet
允许统一 ip
和 ip6
簇,以便更容易地定义规则。
注意:
inet
不能用于nat
类型的链,只能用于filter
类型的链。
下面我们来看看 nftables
是如何进行表管理操作的,以下为 nftables
创建表的基本命令语法。
nft list tables [<family>]
nft list table [<family>] <name> [-n] [-a]
nft (add | delete | flush) table [<family>] <name>
这里我们以创建一个 inet
簇的表为例,演示如何创建和管理一个新的表。
-
创建表
# 创建一个新的表
$ nft add table inet mytable
-
列出表
# 列出所有表
$ nft list tables
# 列出指定族的所有表
$ nft list tables inet
# 列出 inet 簇中 mytable 表中的所有规则
$ nft list table inet mytable
-
删除表
# 删除一个表
$ nft delete table inet mytable
注意:只能删除不包含链的表。
-
清空表
# 清空一个表中的所有规则
$ nft flush table inet mytable
nftables 的链管理
链是用来保存规则的,与 iptables
中的链不同,nftables
没有内置链。这意味着和表一样,链也需要被显示创建。链有以下两种类型:
-
常规链 : 主要用来做跳转,不需要指定钩子类型和优先级。从逻辑上对规则进行分类,支持所有的
nftables
簇。 -
基本链 : 来自网络栈数据包的入口点,需要指定钩子类型和优先级,支持
ip
和ip6
簇。
nftables 链支持钩子的类型
nftables
和 iptables
类似,依然使用 netfiler
中的 5 个 钩子。
不同的是 nftables
在 Linux Kernel 4.2
中新增了 ingress
钩子。
nftables 链支持钩子的作用
-
prerouting
:刚到达并未被nftables
的其他部分所路由或处理的数据包。 -
input
:已经被接收并且已经经过prerouting
钩子的传入数据包。 -
forward
:如果数据报将被发送到另一个设备,它将会通过forward
钩子。 -
output
:从本地传出的数据包。 -
postrouting
:仅仅在离开系统之前,可以对数据包进行进一步处理。
nftables 链支持钩子的适用范围
-
ip
、ip6
和inet
簇支持的钩子有:prerouting
、input
、forward
、output
、postrouting
。 -
arp
簇支持的钩子有:input
、output
。
nftables 链支持的优先级
优先级采用整数值表示,数字较小的链优先处理,并且可以是负数。可以使用的值有:
-
NF_IP_PRI_CONNTRACK_DEFRAG (-400)
-
NF_IP_PRI_RAW (-300)
-
NF_IP_PRI_SELINUX_FIRST (-225)
-
NF_IP_PRI_CONNTRACK (-200)
-
NF_IP_PRI_MANGLE (-150)
-
NF_IP_PRI_NAT_DST (-100)
-
NF_IP_PRI_FILTER (0)
-
NF_IP_PRI_SECURITY (50)
-
NF_IP_PRI_NAT_SRC (100)
-
NF_IP_PRI_SELINUX_LAST (225)
-
NF_IP_PRI_CONNTRACK_HELPER (300)
nftables 链对报文数据支持采取的动作
-
accept
-
drop
-
queue
-
continue
-
return
nftables 创建链的基本命令语法
nft (add | create) chain [<family>] <table> <name> [ { type <type> hook <hook> [device <device>] priority <priority> ; [policy <policy> ;] } ]
nft (delete | list | flush) chain [<family>] <table> <name>
nft rename chain [<family>] <table> <name> <newname>
nftables 创建链的基本操作
-
创建链
-
创建一个常规链
# 将名为 tcpchain 的常规链添加到 inet 簇中名为 mytable 的表中
$ nft add chain inet mytable tcpchain
-
创建一个基本链
添加一个基本链,你必需指定钩子和优先级。基本链的类型可以是 filter
、route
或者 nat
。
# 添加一个筛选输入数据包的基本链
$ nft add chain inet mytable input { type filter hook input priority 0; }
注意:命令中的反斜线
()
用来转义,这样Shell
就不会将分号解释为命令的结尾。
-
列出规则
列出一个链中的所有规则。
# 列出 inet 筛中 filter 表的 input 链中的所有的规则
$ nft list chain inet filter input
-
编辑链
要编辑一个链,只需按名称调用并重新定义要更改的规则即可。
# 将默认表中的 input 链策略从 accept 更改为 drop
$ nft chain inet mytable input { policy drop ; }
-
清空链中的规则
# 清空指定链中的规则,这里为 input
$ nft flush chain inet mytable input
-
删除链
# 删除指定的链,这里为 input
$ nft delete chain inet mytable input
注意:要删除的链中不能包含任何规则或者跳转目标。
nftables 的规则管理
nftables
规则由语句或表达式构成,包含在链中。以下为创建 nftables
规则的基本命令语法:
nft add rule [<family>] <table> <chain> <matches> <statements>
nft insert rule [<family>] <table> <chain> [position <position>] <matches> <statements>
nft replace rule [<family>] <table> <chain> [handle <handle>] <matches> <statements>
nft delete rule [<family>] <table> <chain> [handle <handle>]
其中 matches
是报文需要满足的条件。matches
的内容非常多,可以识别以下多种类型的报文。
ip : ipv4 协议字段
ip6 : ipv6 协议字段
tcp : tcp 协议字段
udp : udp 协议字段
udplite : udp-lite 协议
sctp : sctp 协议
dccp
ah
esp
comp
icmp
icmpv6
ether : 以太头
dst
frag :
hbh
mh
rt
vlan : vlan
arp : arp协议
ct : 连接状态
meta : 报文的基本信息
对每一种类型的报文,你又可以同时检查多个字段,例如:
ip dscp cs1
ip dscp != cs1
ip dscp 0x38
ip dscp != 0x20
ip dscp {cs0, cs1, cs2, cs3, cs4, cs5, cs6, cs7, af11, af12, af13, af21,
af22, af23, af31, af32, af33, af41, af42, af43, ef}
ip length 232
ip length != 233
ip length 333-435
ip length != 333-453
ip length { 333, 553, 673, 838}
ip6 flowlabel 22
ip6 flowlabel != 233
ip6 flowlabel { 33, 55, 67, 88 }
ip6 flowlabel { 33-55 }
而 statement
是报文匹配规则时触发的操作,大致有以下几种:
Verdict statements : 动作
Log : 记录日志并继续处理请求
Reject : 停止处理并拒绝请求
Counter : 计数
Limit : 如果达到了接收数据包的匹配限制,则根据规则处理数据包
Nat : NAT
Queuea : 停止处理并发送数据包到用户空间程序
其中 Verdict Statements
是一组动作,大致有以下几种:
-
accept:接受数据包并停止剩余规则评估。
-
drop:丢弃数据包并停止剩余规则评估。
-
queue:将数据包排队到用户空间并停止剩余规则评估。
-
continue:使用下一条规则继续进行规则评估。
-
return:从当前链返回并继续执行最后一条链的下一条规则。
-
jump
:跳转到指定的规则链,当执行完成或者返回时,返回到调用的规则链。 -
goto
:类似于跳转,发送到指定规则链但不返回。
下面将以添加一条允许 SSH
登录的规则为例,给大家介绍下如何增加或插入一条新的规则。
-
增加规则
$ nft add rule inet mytable input tcp dport ssh accept
默认情况下,add
表示将规则添加到链的末尾。如果你想从链的开头增加规则,可以使用 insert
来实现。
$ nft insert rule inet mytable input tcp dport http accept
-
列出规则
-
列出目前链中所有的规则
$ nft list ruleset
table inet mytable {
chain input {
type filter hook input priority 0; policy accept;
tcp dport http accept
tcp dport ssh accept
}
}
-
列出某个表中的所有规则
$ nft list table inet mytable
-
列出某条链中的所有规则
$ nft list chain inet mytable input
-
按指定位置增加规则
无论你是使用 add
或者 insert
来增加规则,你都可以通过 index
或者 handle
来指定添加的位置。
-
使用 index 来指定规则的索引
index
类似于 iptables
的 -I
选项, add
表示新规则添加在索引位置的规则后面,inser
表示新规则添加在索引位置的规则前面。
# 在 input 链中已有规则中的第二条规则前插入一条新的规则
$ nft insert rule inet mytable input index 1 tcp dport nfs accept
$ nft list ruleset
table inet my_table {
chain my_filter_chain {
type filter hook input priority 0; policy accept;
tcp dport http accept
tcp dport nfs accept
tcp dport ssh accept
}
}
# 在 input 链中已有规则中的第一条规则后插入一条新的规则
$ nft add rule inet mytable input index 0 tcp dport 1234 accept
$ nft list ruleset
table inet my_table {
chain my_filter_chain {
type filter hook input priority 0; policy accept;
tcp dport http accept
tcp dport 1234 accept
tcp dport nfs accept
tcp dport ssh accept
}
}
注意:
index
的值是从0
开始的,index
必须指向一个已存在的规则的索引。
-
使用 handle 来指定规则的句柄
通过 handle
的值来指定规则添加的位置,必须先知道现有规则的句柄位置。你可以通过参数 --handle
来获取当前规则的句柄位置。
$ nft --handle list ruleset
table inet mytable { # handle 10
chain input { # handle 2
type filter hook input priority 0; policy accept;
tcp dport http accept # handle 4
tcp dport 1234 accept # handle 6
tcp dport nfs accept # handle 5
tcp dport ssh accept # handle 3
}
}
获取到当前规则的句柄位置后,我们就可以在指定句柄位置添加规则。下面我们以在句柄位置 4 后面和句柄位置 5 前面分别增加一条新的规则为例:
$ nft add rule inet mytable input handle 4 tcp dport 2345 accept
$ nft insert rule inet mytable input handle 5 tcp dport 3456 accept
$ nft --handle list ruleset
table inet my_table { # handle 10
chain my_filter_chain { # handle 2
type filter hook input priority 0; policy accept;
tcp dport http accept # handle 4
tcp dport 2345 accept # handle 8
tcp dport 1234 accept # handle 6
tcp dport 3456 accept # handle 9
tcp dport nfs accept # handle 5
tcp dport ssh accept # handle 3
}
}
在 nftables
中,句柄值是固定不变的,除非规则被删除。而 index
的值是可变的,只要有新规则插入,就有可能发生变化。一般建议使用 handle
来插入新规则。
你也可以在创建规则时就获取到规则的句柄值,只需要在创建规则时同时加上参数 --echo
和 --handle
。
$ nft --echo --handle add rule inet mytable input udp dport 3333 accept
add rule inet mytable input udp dport 3333 accept # handle 10
-
删除规则
单个规则只能通过句柄值删除,每个规则的句柄值可通过 nft --handle list ruleset
命令查看。
# 删除指定句柄值对应的规则
$ nft delete rule inet mytable input handle 8
小技巧:你可以使用
iptables-translate
实用程序将iptables
规则转换成nftables
格式
nftables 高级功能进阶
nftables
除了上面的基础功能外,还给我们额外提供了一些非常实用且功能强大的高级功能。
集合
-
匿名集合
匿名集合比较适合用于将来不需要更改的规则。
# 允许来自源 IP 处于 10.10.10.123 ~ 10.10.10.231 这个区间内的主机的流量。
$ nft add rule inet mytable input ip saddr { 10.10.10.123, 10.10.10.231 } accept
# 允许指定协义的流量通过
$ nft add rule inet mytable input tcp dport { http, nfs, ssh } accept
匿名集合的缺点是需要修改集合规则时,就得替换原规则。如果需要频繁修改的集合,推荐使用命名集合。
-
命名集合
nftables
的命名集合是可以修改的。创建命名集合时需要指定其元素的类型,当前支持的数据类型有:
-
-
-
-
inet_proto
: 网络协议 -
inet_service
: 网络服务 -
mark
: 标记类型
这里,我们来看一个实例。首先,创建一个空的命名集合。
# 创建一个空的命名集合
$ nft add set inet mytable myset { type ipv4_addr ; }
$ nft list sets
table inet mytable {
set myset {
type ipv4_addr
}
}
接着,我们向集合中添加一些元素。
$ nft add element inet mytable myset { 10.10.10.22, 10.10.10.33 }
$ nft list set inet mytable myset
table inet mytable {
set myset {
type ipv4_addr
elements = { 10.10.10.22, 10.10.10.33 }
}
}
然后,在添加规则时引用集合,你可以使用 @
符号跟上集合的名字来引用命名集合。
# 将来源为集合 myset 中的 IP 地址的请求阻止掉
$ nft insert rule inet mytable input ip saddr @myset drop
$ nft list chain inet mytable input
table inet mytable {
chain input {
type filter hook input priority 0; policy accept;
ip saddr @my_set drop
tcp dport http accept
tcp dport nfs accept
tcp dport ssh accept
ip saddr { 10.10.10.123, 10.10.10.231 } accept
}
}
-
支持区间
$ nft add element inet mytable myset { 10.10.10.0-10.10.10.255 }
Error: Set member cannot be range, missing interval flag on declaration
add element inet my_table my_set { 10.10.10.0-10.10.10.255 }
^^^^^^^^^^^^^^^^^^^^^^^
如果你想在集合中使用区间,需要加上一个 flag interval
,因为内核必须提前确认该集合存储的数据类型,以便采用适当的数据结构。我们来看一个实例吧:
# 创建一个支持区间的命名集合
$ nft add set inet mytable my_rangeset { type ipv4_addr ; flags interval
$ nft add element inet mytable my_rangeset { 10.10.10.0/24 }
$ nft list set inet mytable my_rangeset
table inet mytable {
set my_rangeset {
type ipv4_addr
flags interval
elements = { 10.10.10.0/24 }
}
}
-
级联不同类型
首先,我们创建一个级联类型的集合。
$ nft add set inet mytable my_concatset { type ipv4_addr . inet_proto . inet_service ; }
# 不同类型的元素可以通过级联操作符 . 来分隔。
$ nft list set inet mytable my_concatset
table inet my_table {
set my_concatset {
type ipv4_addr . inet_proto . inet_service
}
}
接着,向集合中添加元素。
$ nft add element inet mytable my_concatset { 10.30.30.30 . tcp . telnet }
最后,我们在规则中对级联类型的集合进行引用。
# 如果数据包的源 IP、协议类型、目标端口匹配 10.30.30.30、tcp、telnet 时,就会允许该数据包通过
$ nft add rule inet mytable input ip saddr . meta l4proto . tcp dport @my_concatset accept
除了命名集合,匿名集合也是可以使用级联元素,例如:
$ nft add rule inet mytable input ip saddr . meta l4proto . udp dport { 10.30.30.30 . udp . bootps } accept
在规则中引用级联类型的集合和一般类型集合的主要不同之处:主要在于需要标明集合中每个元素对应到规则中的哪个位置,这类似于 ipset 的聚合类型,例如 hash:ip,port
。
字典
字典是 nftables
的又一个高级特性,它同样可以支持在一条规则上面使用不同类型的数据。
首先,我们创建一个命名字典。
$ nft add map inet mytable my_vmap { type inet_proto : verdict ; }
接着,我们向字典中添加一些元素。
$ nft add element inet mytable my_vmap { 192.168.0.10 : drop, 192.168.0.11 : accept }
最后,我们就可以在规则中引用字典中的元素。
$ nft add rule inet my_table input ip saddr vmap @my_vmap
和集合一样,除了命名字典,你也可以创建匿名字典。例如,为了从逻辑上对 TCP
和 UDP
的数据包拆分开来用两条不同链来处理,你就可以通过使用字典来实现。
$ nft add chain inet mytable my_tcpchain
$ nft add chain inet mytable my_udpchain
$ nft add rule inet mytable input meta l4proto vmap { tcp : jump my_tcpchain, udp : jump my_udpchain }
$ nft list chain inet mytable input
table inet mytable {
chain input {
...
meta nfproto ipv4 ip saddr . meta l4proto . udp dport { 10.30.30.30 . udp . bootps } accept
meta l4proto vmap { tcp : jump my_tcpchain, udp : jump my_udpchain }
}
}
表与命名空间
在 nftables
中,每个表都是一个独立的命名空间,这就意味着不同的表中的链、集合、字典等名字可以相同。例如:
$ nft add table inet table_one
$ nft add chain inet table_one mychain
$ nft add table inet table_two
$ nft add chain inet table_two mychain
$ nft list ruleset
...
table inet table_one {
chain mychain {
}
}
table inet table_two {
chain mychain {
}
}
有了这个特性后,不同的应用就可以在相互不影响的情况下管理自己的表中的规则。不过使用这个特性前,你需要注意的一点是:由于 nftables
将每个表都被视为独立的防火墙,一个数据包必须被所有表中的规则放行才能真正通过。如果,出现两条链的优先级相同,就会进入竞争状态。
当然,你可以使用 nftables
优先级特性来解决这个问题。优先级值越高的链优先级越低,所以优先级值低的链会比优先级值高的链先执行。
备份与恢复
默认情况下,通过 nftables
用户态工具 nft
直接在终端中加入的规则都是临时的。如果要想永久生效,我们可以将规则备份后并在开机自动加载时进行恢复。
-
备份规则
$ nft list ruleset > /root/nftables.conf
-
恢复规则
$ nft -f /root/nftables.conf
在 CentOS 8
中,nftables
是以 Systemd
服务形式进行工作的。nftables.service
的规则被存储在 /etc/nftables.conf
中,其中包含了一些其他的示例规则,一般会位于 /etc/sysconfig/nftables.conf
文件中。
如果你想开机自加载 nftables
规则,只需将备份规则放到 /etc/sysconfig/nftables.conf
文件即可。
总结
至此,本文对 nftables
的基本功能和用法就讲解完了,更高级的用法可以在以下文档中做进一步探索。
Archlinux Wiki:https://wiki.archlinux.org/index.php/Nftables
nftables HOWTO:https://farkasity.gitbooks.io/nftables-howto-zh/
nftables Wiki:https://wiki.nftables.org/wiki-nftables/index.php/Main_Page
参考文档
-
https://www.google.com
-
https://url.cn/56tJkD8
-
http://www.freecls.com/a/2712/fc
-
https://blog.51cto.com/babyshen/2065749
-
https://adoyle.me/Today-I-Learned/linux/iptables.html
-
https://blog.csdn.net/dog250/article/details/54170683
-
https://www.yangcs.net/posts/using-nftables/
-
https://wiki.archlinux.org/index.php/Nftables
-
https://farkasity.gitbooks.io/nftables-howto-zh/content/
-
https://blog.omicron3069.com/post/nftablesfornode/
你可能还喜欢
再见 XShell 和 ITerm 2,是时候拥抱全平台高颜值终端工具 Hyper 了!
以上是关于CentOS 8 都上生产了,你还在用 iptables 吗,是时候拥抱下一代防火墙 nftables 了!的主要内容,如果未能解决你的问题,请参考以下文章
你还在用 Java 8?手把手教你从 Java 8 升级到 Java 17 全过程,真香!!