什么是 nftables?
nftables
是一个新式的数据包过滤框架,旨在替代现用的 iptables
、ip6tables
、arptables
和 ebtables
的新的包过滤框架。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 的特点
-
nftables
拥有一些高级的类似编程语言的能力,例如:定义变量和包含外部文件,即拥有使用额外脚本的能力。nftables
也可以用于多种地址簇的过滤和处理。 -
不同于
iptables
,nftables
并不包含任何的内置表,需要哪些表并在这些表中添加什么处理规则一切由管理员决定。 -
表包含规则链,规则链包含规则。
nftables 相较于 iptables 的优点
- 更新速度更快
在 iptables
中添加一条规则,会随着规则数量增多而变得非常慢。这种状况对 nftables
而言就不存在了,因为 nftables
使用原子的快速操作来更新规则集合。
- 内核更新更少。
使用 iptables
时,每一个匹配或投递都需要内核模块的支持。因此,如果你忘记一些东西或者要添加新的功能时都需要重新编译内核。而在 nftables
中就不存在这种情况了, 因为在 nftables
中,大部分工作是在用户态完成的,内核只知道一些基本指令(过滤是用伪状态机实现的)。例如,icmpv6
支持是通过 nft
工具的一个简单的补丁实现的,而在 iptables
中这种类型的更改需要内核和 iptables
都升级才可以。
nftables 基础操作
nftables
和 iptables
一样,由表(table)、链(chain)和规则(rule)组成,其中表包含链,链包含规则,规则是真正的动作。
在 nftables
中,表是链的容器。所以开始使用 nftables
时你首先需要做的是添加至少一个表。然后,你可以向你的表里添加链,然后往链里添加规则。
nftables 的表管理
与 iptables
中的表不同,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
创建表的基本命令语法。
1 | nft list tables [<family>] |
这里我们以创建一个 inet
簇的表为例,演示如何创建和管理一个新的表。
- 创建表
1 | # 创建一个新的表 |
- 列出表
1 | # 列出所有表 |
- 删除表
1 | # 删除一个表 |
注意:只能删除不包含链的表。
- 清空表
1 | # 清空一个表中的所有规则 |
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 创建链的基本命令语法
1 | nft (add | create) chain [<family>] <table> <name> [ { type <type> hook <hook> [device <device>] priority <priority> \; [policy <policy> \;] } ] |
nftables 创建链的基本操作
- 创建链
- 创建一个常规链
1 | # 将名为 tcpchain 的常规链添加到 inet 簇中名为 mytable 的表中 |
- 创建一个基本链
添加一个基本链,你必需指定钩子和优先级。基本链的类型可以是 filter
、route
或者 nat
。
1 | # 添加一个筛选输入数据包的基本链 |
注意:命令中的反斜线
(\)
用来转义,这样Shell
就不会将分号解释为命令的结尾。
- 列出规则
列出一个链中的所有规则。
1 | # 列出 inet 筛中 filter 表的 input 链中的所有的规则 |
- 编辑链
要编辑一个链,只需按名称调用并重新定义要更改的规则即可。
1 | # 将默认表中的 input 链策略从 accept 更改为 drop |
- 清空链中的规则
1 | # 清空指定链中的规则,这里为 input |
- 删除链
1 | # 删除指定的链,这里为 input |
注意:要删除的链中不能包含任何规则或者跳转目标。
nftables 的规则管理
nftables
规则由语句或表达式构成,包含在链中。以下为创建 nftables
规则的基本命令语法:
1 | nft add rule [<family>] <table> <chain> <matches> <statements> |
其中 matches
是报文需要满足的条件。matches
的内容非常多,可以识别以下多种类型的报文。
1 | ip : ipv4 协议字段 |
对每一种类型的报文,你又可以同时检查多个字段,例如:
1 | ip dscp cs1 |
而 statement
是报文匹配规则时触发的操作,大致有以下几种:
1 | Verdict statements : 动作 |
其中 Verdict Statements
是一组动作,大致有以下几种:
-
accept:接受数据包并停止剩余规则评估。
-
drop:丢弃数据包并停止剩余规则评估。
-
queue:将数据包排队到用户空间并停止剩余规则评估。
-
continue:使用下一条规则继续进行规则评估。
-
return:从当前链返回并继续执行最后一条链的下一条规则。
-
jump
:跳转到指定的规则链,当执行完成或者返回时,返回到调用的规则链。 -
goto
:类似于跳转,发送到指定规则链但不返回。
下面将以添加一条允许 SSH
登录的规则为例,给大家介绍下如何增加或插入一条新的规则。
- 增加规则
1 | $ nft add rule inet mytable input tcp dport ssh accept |
默认情况下,add
表示将规则添加到链的末尾。如果你想从链的开头增加规则,可以使用 insert
来实现。
1 | $ nft insert rule inet mytable input tcp dport http accept |
- 列出规则
- 列出目前链中所有的规则
1 | $ nft list ruleset |
- 列出某个表中的所有规则
1 | $ nft list table inet mytable |
- 列出某条链中的所有规则
1 | $ nft list chain inet mytable input |
- 按指定位置增加规则
无论你是使用 add
或者 insert
来增加规则,你都可以通过 index
或者 handle
来指定添加的位置。
- 使用 index 来指定规则的索引
index
类似于 iptables
的 -I
选项, add
表示新规则添加在索引位置的规则后面,inser
表示新规则添加在索引位置的规则前面。
1 | # 在 input 链中已有规则中的第二条规则前插入一条新的规则 |
注意:
index
的值是从0
开始的,index
必须指向一个已存在的规则的索引。
- 使用 handle 来指定规则的句柄
通过 handle
的值来指定规则添加的位置,必须先知道现有规则的句柄位置。你可以通过参数 --handle
来获取当前规则的句柄位置。
1 | $ nft --handle list ruleset |
获取到当前规则的句柄位置后,我们就可以在指定句柄位置添加规则。下面我们以在句柄位置 4 后面和句柄位置 5 前面分别增加一条新的规则为例:
1 | $ nft add rule inet mytable input handle 4 tcp dport 2345 accept |
在 nftables
中,句柄值是固定不变的,除非规则被删除。而 index
的值是可变的,只要有新规则插入,就有可能发生变化。一般建议使用 handle
来插入新规则。
你也可以在创建规则时就获取到规则的句柄值,只需要在创建规则时同时加上参数 --echo
和 --handle
。
1 | $ nft --echo --handle add rule inet mytable input udp dport 3333 accept |
- 删除规则
单个规则只能通过句柄值删除,每个规则的句柄值可通过 nft --handle list ruleset
命令查看。
1 | # 删除指定句柄值对应的规则 |
小技巧:你可以使用
iptables-translate
实用程序将iptables
规则转换成nftables
格式
nftables 高级功能进阶
nftables
除了上面的基础功能外,还给我们额外提供了一些非常实用且功能强大的高级功能。
集合
nftables
的语法原生支持集合,可以用来匹配多个 IP
地址、端口号、网卡或其他任何条件。nftables
的集合可分为匿名集合与命名集合,相对 iptables
来说,nftables
是原生支持集合,并不需要借助 ipset
来实现。
- 匿名集合
匿名集合比较适合用于将来不需要更改的规则。
1 | # 允许来自源 IP 处于 10.10.10.123 ~ 10.10.10.231 这个区间内的主机的流量。 |
匿名集合的缺点是需要修改集合规则时,就得替换原规则。如果需要频繁修改的集合,推荐使用命名集合。
- 命名集合
nftables
的命名集合是可以修改的。创建命名集合时需要指定其元素的类型,当前支持的数据类型有:
-
ipv4_addr
:IPv4
地址 -
ipv6_addr
:IPv6
地址 -
ether_addr
: 以太网(Ethernet)地址 -
inet_proto
: 网络协议 -
inet_service
: 网络服务 -
mark
: 标记类型
这里,我们来看一个实例。首先,创建一个空的命名集合。
1 | # 创建一个空的命名集合 |
接着,我们向集合中添加一些元素。
1 | $ nft add element inet mytable myset { 10.10.10.22, 10.10.10.33 } |
然后,在添加规则时引用集合,你可以使用 @
符号跟上集合的名字来引用命名集合。
1 | # 将来源为集合 myset 中的 IP 地址的请求阻止掉 |
- 支持区间
从上面的例子中,我们可以看到填加元素时是使用的两个独立的 IP
地址,并没有直接使用 10.10.10.0-10.10.10.255
这样的区间段来表示。这是因为直接使用区间段会报以下类似错误:
1 | $ nft add element inet mytable myset { 10.10.10.0-10.10.10.255 } |
如果你想在集合中使用区间,需要加上一个 flag interval
,因为内核必须提前确认该集合存储的数据类型,以便采用适当的数据结构。我们来看一个实例吧:
1 | # 创建一个支持区间的命名集合 |
上面的例子中直接使用了子网掩码来表示 IP
地址段,它会被隐式转换为 IP
地址的区间,你也可以直接使用区间 10.10.10.0-10.10.10.255
来获得相同的效果。
- 级联不同类型
命名集合不仅支持同一类型元素,也可以支持对不同类型的元素进行级联。例如,下面的规则可以一次性匹配 IP
地址、协议和端口号。
首先,我们创建一个级联类型的集合。
1 | $ nft add set inet mytable my_concatset { type ipv4_addr . inet_proto . inet_service \; } |
接着,向集合中添加元素。
1 | $ nft add element inet mytable my_concatset { 10.30.30.30 . tcp . telnet } |
最后,我们在规则中对级联类型的集合进行引用。
1 | # 如果数据包的源 IP、协议类型、目标端口匹配 10.30.30.30、tcp、telnet 时,就会允许该数据包通过 |
除了命名集合,匿名集合也是可以使用级联元素,例如:
1 | $ nft add rule inet mytable input ip saddr . meta l4proto . udp dport { 10.30.30.30 . udp . bootps } accept |
在规则中引用级联类型的集合和一般类型集合的主要不同之处:主要在于需要标明集合中每个元素对应到规则中的哪个位置,这类似于 ipset 的聚合类型,例如 hash:ip,port
。
字典
字典是 nftables
的又一个高级特性,它同样可以支持在一条规则上面使用不同类型的数据。
首先,我们创建一个命名字典。
1 | $ nft add map inet mytable my_vmap { type inet_proto : verdict \; } |
接着,我们向字典中添加一些元素。
1 | $ nft add element inet mytable my_vmap { 192.168.0.10 : drop, 192.168.0.11 : accept } |
最后,我们就可以在规则中引用字典中的元素。
1 | $ nft add rule inet mytable input ip saddr vmap @my_vmap |
和集合一样,除了命名字典,你也可以创建匿名字典。例如,为了从逻辑上对 TCP
和 UDP
的数据包拆分开来用两条不同链来处理,你就可以通过使用字典来实现。
1 | $ nft add chain inet mytable my_tcpchain |
表与命名空间
在 nftables
中,每个表都是一个独立的命名空间,这就意味着不同的表中的链、集合、字典等名字可以相同。例如:
1 | $ nft add table inet table_one |
有了这个特性后,不同的应用就可以在相互不影响的情况下管理自己的表中的规则。不过使用这个特性前,你需要注意的一点是:由于 nftables
将每个表都被视为独立的防火墙,一个数据包必须被所有表中的规则放行才能真正通过。如果,出现两条链的优先级相同,就会进入竞争状态。
当然,你可以使用 nftables
优先级特性来解决这个问题。优先级值越高的链优先级越低,所以优先级值低的链会比优先级值高的链先执行。
备份与恢复
默认情况下,通过 nftables
用户态工具 nft
直接在终端中加入的规则都是临时的。如果要想永久生效,我们可以将规则备份后并在开机自动加载时进行恢复。
- 备份规则
1 | $ nft list ruleset > /root/nftables.conf |
- 恢复规则
1 | $ 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