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 工具存在的诸多限制。相对于旧的 iptablesnftables 最引人注目的功能包括:改进性能、支持查询表、事务型规则更新、所有规则自动应用等等。

nftables 主要由三个组件组成:内核实现、libnl netlink 通信和 nftables 用户空间。其中内核提供了一个 netlink 配置接口以及运行时规则集评估,libnl 包含了与内核通信的基本函数,用户空间可以通过新引入的命令行工具 nft 和用户进行交互。

nft 可以通过在寄存器中储存和加载来交换数据。也就是说,它的语法与 iptables 不同。但 nft 可以利用内核提供的表达式去模拟旧的 iptables 命令,维持兼容性的同时获得更大的灵活性。简单来说,nftiptables 及其衍生指令(ip6tablesarptables )的超集。

nftables 的特点

  1. 不同于 iptables, nftables 并不包含任何的内置表,需要哪些表并在这些表中添加什么处理规则一切由管理员决定。

  2. 表包含规则链,规则链包含规则。

nftables 相较于 iptables 的优点

  1. 更新速度更快

iptables 中添加一条规则,会随着规则数量增多而变得非常慢。这种状况对 nftables 而言就不存在了,因为 nftables 使用原子的快速操作来更新规则集合。

  1. 内核更新更少。

使用 iptables 时,每一个匹配或投递都需要内核模块的支持。因此,如果你忘记一些东西或者要添加新的功能时都需要重新编译内核。而在 nftables 中就不存在这种情况了, 因为在 nftables 中,大部分工作是在用户态完成的,内核只知道一些基本指令(过滤是用伪状态机实现的)。例如,icmpv6 支持是通过 nft 工具的一个简单的补丁实现的,而在 iptables 中这种类型的更改需要内核和 iptables 都升级才可以。

nftables 基础操作

nftablesiptables 一样,由表(table)、链(chain)和规则(rule)组成,其中表包含链,链包含规则,规则是真正的动作。

nftables 中,表是链的容器。所以开始使用 nftables 时你首先需要做的是添加至少一个表。然后,你可以向你的表里添加链,然后往链里添加规则。

nftables 的表管理

nftables 簇 对应 iptables 的命令行工具
ip iptables
ip6 ip6tables
inet iptables 和 ip6tables
arp arptables
bridge ebtables

ip(即 IPv4)是默认簇,如果未指定簇,则使用该簇。如果要创建同时适用于 IPv4IPv6 的规则,请使用 inet 簇 。inet 允许统一 ipip6 簇,以便更容易地定义规则。

注意: inet 不能用于 nat 类型的链,只能用于 filter 类型的链。

下面我们来看看 nftables 是如何进行表管理操作的,以下为 nftables 创建表的基本命令语法。

nft list tables [<family>]nft list table [<family>] <name> [-n] [-a]nft (add | delete | flush) table [<family>] <name>

这里我们以创建一个 inet 簇的表为例,演示如何创建和管理一个新的表。

  1. 创建表
# 创建一个新的表$ nft add table inet mytable
  1. 列出表
# 列出所有表$ nft list tables
# 列出指定族的所有表$ nft list tables inet
# 列出 inet 簇中 mytable 表中的所有规则$ nft list table inet mytable
  1. 删除表
# 删除一个表$ nft delete table inet mytable

注意:只能删除不包含链的表。

  1. 清空表
# 清空一个表中的所有规则$ nft flush table inet mytable

nftables 的链管理

链是用来保存规则的,与 iptables 中的链不同,nftables 没有内置链。这意味着和表一样,链也需要被显示创建。链有以下两种类型:

  • 常规链 : 主要用来做跳转,不需要指定钩子类型和优先级。从逻辑上对规则进行分类,支持所有的 nftables 簇。

  • 基本链 : 来自网络栈数据包的入口点,需要指定钩子类型和优先级,支持 ipip6 簇。

nftables 链支持钩子的类型

nftablesiptables 类似,依然使用 netfiler 中的 5 个 钩子。

CentOS 8 都上生产了,你还在用 iptables 吗,是时候拥抱下一代防火墙 nftables 了!

不同的是 nftablesLinux Kernel 4.2 中新增了 ingress 钩子。

CentOS 8 都上生产了,你还在用 iptables 吗,是时候拥抱下一代防火墙 nftables 了!

nftables 链支持钩子的作用

  • prerouting:刚到达并未被 nftables 的其他部分所路由或处理的数据包。

  • input:已经被接收并且已经经过 prerouting 钩子的传入数据包。

  • forward:如果数据报将被发送到另一个设备,它将会通过 forward 钩子。

  • output:从本地传出的数据包。

  • postrouting:仅仅在离开系统之前,可以对数据包进行进一步处理。

nftables 链支持钩子的适用范围

  • ipip6inet 簇支持的钩子有:preroutinginputforwardoutputpostrouting

  • arp 簇支持的钩子有:inputoutput

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 创建链的基本操作

  1. 创建链
  • 创建一个常规链
# 将名为 tcpchain 的常规链添加到 inet 簇中名为 mytable 的表中$ nft add chain inet mytable tcpchain
  • 创建一个基本链

添加一个基本链,你必需指定钩子和优先级。基本链的类型可以是 filterroute 或者 nat

# 添加一个筛选输入数据包的基本链$ nft add chain inet mytable input { type filter hook input priority 0; }

注意:命令中的反斜线 () 用来转义,这样 Shell 就不会将分号解释为命令的结尾。

  1. 列出规则

列出一个链中的所有规则。

# 列出 inet 筛中 filter 表的 input 链中的所有的规则$ nft list chain inet filter input
  1. 编辑链

要编辑一个链,只需按名称调用并重新定义要更改的规则即可。

# 将默认表中的 input 链策略从 accept 更改为 drop$ nft chain inet mytable input { policy drop ; }
  1. 清空链中的规则
# 清空指定链中的规则,这里为 input$ nft flush chain inet mytable input
  1. 删除链
# 删除指定的链,这里为 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 协议 dccpahespcompicmpicmpv6ether : 以太头dstfrag :hbhmhrt vlan : vlanarp : arp协议ct : 连接状态meta : 报文的基本信息

对每一种类型的报文,你又可以同时检查多个字段,例如:

ip dscp cs1ip dscp != cs1ip dscp 0x38ip dscp != 0x20ip dscp {cs0, cs1, cs2, cs3, cs4, cs5, cs6, cs7, af11, af12, af13, af21, af22, af23, af31, af32, af33, af41, af42, af43, ef}
ip length 232ip length != 233ip length 333-435ip length != 333-453ip length { 333, 553, 673, 838}
ip6 flowlabel 22ip6 flowlabel != 233ip6 flowlabel { 33, 55, 67, 88 }ip6 flowlabel { 33-55 }

statement 是报文匹配规则时触发的操作,大致有以下几种:

Verdict statements : 动作Log : 记录日志并继续处理请求Reject : 停止处理并拒绝请求Counter : 计数Limit : 如果达到了接收数据包的匹配限制,则根据规则处理数据包Nat : NATQueuea : 停止处理并发送数据包到用户空间程序

其中 Verdict Statements 是一组动作,大致有以下几种:

  • accept:接受数据包并停止剩余规则评估。

  • drop:丢弃数据包并停止剩余规则评估。

  • queue:将数据包排队到用户空间并停止剩余规则评估。

  • continue:使用下一条规则继续进行规则评估。

  • return:从当前链返回并继续执行最后一条链的下一条规则。

  • jump :跳转到指定的规则链,当执行完成或者返回时,返回到调用的规则链。

  • goto :类似于跳转,发送到指定规则链但不返回。

下面将以添加一条允许 SSH 登录的规则为例,给大家介绍下如何增加或插入一条新的规则。

  1. 增加规则
$ nft add rule inet mytable input tcp dport ssh accept

默认情况下,add 表示将规则添加到链的末尾。如果你想从链的开头增加规则,可以使用 insert 来实现。

$ nft insert rule inet mytable input tcp dport http accept
  1. 列出规则
  • 列出目前链中所有的规则
$ nft list rulesettable 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
  1. 按指定位置增加规则

无论你是使用 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 rulesettable 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 rulesettable 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 rulesettable 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 rulesettable 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 acceptadd rule inet mytable input udp dport 3333 accept # handle 10
  1. 删除规则

单个规则只能通过句柄值删除,每个规则的句柄值可通过 nft --handle list ruleset 命令查看。

# 删除指定句柄值对应的规则$ nft delete rule inet mytable input handle 8

小技巧:你可以使用 iptables-translate 实用程序将 iptables 规则转换成 nftables 格式

nftables 高级功能进阶

nftables 除了上面的基础功能外,还给我们额外提供了一些非常实用且功能强大的高级功能。

集合

  1. 匿名集合

匿名集合比较适合用于将来不需要更改的规则。

# 允许来自源 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

匿名集合的缺点是需要修改集合规则时,就得替换原规则。如果需要频繁修改的集合,推荐使用命名集合。

  1. 命名集合

nftables 的命名集合是可以修改的。创建命名集合时需要指定其元素的类型,当前支持的数据类型有:

  • inet_proto : 网络协议

  • inet_service : 网络服务

  • mark : 标记类型

这里,我们来看一个实例。首先,创建一个空的命名集合。

# 创建一个空的命名集合$ nft add set inet mytable myset { type ipv4_addr ; }$ nft list setstable 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 mysettable 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 inputtable 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 }}
  1. 支持区间
$ nft add element inet mytable myset { 10.10.10.0-10.10.10.255 }
Error: Set member cannot be range, missing interval flag on declarationadd 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_rangesettable inet mytable { set my_rangeset { type ipv4_addr flags interval elements = { 10.10.10.0/24 } }}
  1. 级联不同类型

首先,我们创建一个级联类型的集合。

$ nft add set inet mytable my_concatset { type ipv4_addr . inet_proto . inet_service ; }
# 不同类型的元素可以通过级联操作符 . 来分隔。$ nft list set inet mytable my_concatsettable 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

和集合一样,除了命名字典,你也可以创建匿名字典。例如,为了从逻辑上对 TCPUDP 的数据包拆分开来用两条不同链来处理,你就可以通过使用字典来实现。

$ 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 inputtable 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 直接在终端中加入的规则都是临时的。如果要想永久生效,我们可以将规则备份后并在开机自动加载时进行恢复。

  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 的基本功能和用法就讲解完了,更高级的用法可以在以下文档中做进一步探索。

  1. Archlinux Wiki:https://wiki.archlinux.org/index.php/Nftables

  2. nftables HOWTO:https://farkasity.gitbooks.io/nftables-howto-zh/

  3. nftables Wiki:https://wiki.nftables.org/wiki-nftables/index.php/Main_Page

参考文档

  1. https://www.google.com

  2. https://url.cn/56tJkD8

  3. http://www.freecls.com/a/2712/fc

  4. https://blog.51cto.com/babyshen/2065749

  5. https://adoyle.me/Today-I-Learned/linux/iptables.html

  6. https://blog.csdn.net/dog250/article/details/54170683

  7. https://www.yangcs.net/posts/using-nftables/

  8. https://wiki.archlinux.org/index.php/Nftables

  9. https://farkasity.gitbooks.io/nftables-howto-zh/content/

  10. https://blog.omicron3069.com/post/nftablesfornode/

CentOS 8 都上生产了,你还在用 iptables 吗,是时候拥抱下一代防火墙 nftables 了!


你可能还喜欢

再见 XShell 和 ITerm 2,是时候拥抱全平台高颜值终端工具 Hyper 了!


以上是关于CentOS 8 都上生产了,你还在用 iptables 吗,是时候拥抱下一代防火墙 nftables 了!的主要内容,如果未能解决你的问题,请参考以下文章

Java 12 来了, 你还在用 Java 8 吗?

你还在用 Java 8?手把手教你从 Java 8 升级到 Java 17 全过程,真香!!

Jenkins 也宣布弃用 JDK 8,你还在用JDK8吗

Jenkins 也宣布弃用 JDK 8,你还在用JDK8吗

都 2021 年了,你还在用 Jenkins ?赶快看看这些替代方案吧!

Windows XP时代终结:假设你还在用它怎么办