Nftables是个嘛

简单来说,他是一个数据包分类框架,nftables的推出,直接能够取代 {ip,ip6,arp,eb}_tables四种框架,也就是nftables直接实现了四合一的功能

当然,他也对上述四个框架做了一定程度上的兼容,能让你在nftables内核上运行iptables(也就是nft 可以利用内核提供的表达式去模拟旧的 iptables 命令)

nftables的特点

与iptables之间的差异:

  • iptables 规则的布局是基于连续的大块内存的,即数组式布局;而 nftables 的规则采用链式布局。其实就是数组和链表的区别
  • iptables 大部分工作在内核态完成,如果要添加新功能,只能重新编译内核;而 nftables 的大部分工作是在用户态完成的,添加新功能很 easy,不需要改内核。
  • iptables 有内置的链,即使你只需要一条链,其他的链也会跟着注册;而 nftables 不存在内置的链,你可以按需注册。由于 iptables 内置了一个数据包计数器,所以即使这些内置的链是空的,也会带来性能损耗。
  • 简化了 IPv4/IPv6 双栈管理
  • 原生支持集合、字典和映射

优势功能:

  1. 改进性能

  2. 支持查询表

    1. nftables 拥有一些高级的类似编程语言的能力,例如:定义变量和包含外部文件,即拥有使用额外脚本的能力。nftables 也可以用于多种地址簇的过滤和处理
    2. 不同于 iptables, nftables 并不包含任何的内置表,需要哪些表并在这些表中添加什么处理规则一切由管理员决定
    3. 表包含规则链,规则链包含规则
  3. 事务型规则更新

  4. 所有规则自动应用

组件组成:

  1. 内核的实现 提供了 netlink 接口并 运行时规则集评估

  2. libnl netlink通信 与内核通信的基本函数

  3. nftables 用户空间 新引入的命令行工具 nft 和用户进行交互

优点:

  1. 更新速度快:

    nftables 使用原子的快速操作来更新规则集合

  2. 内核更新少:

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

nftables 使用

基础操作

首先,我们需要明确一点:nftables实际上也是由tables,chain和rule组成,tables包含chain,chain包含rule,其中rule是真正的动作。

因此,开始使用时我们未尝不可先从添加一个表开始:

表:

介绍

  1. 首先,nftables并没有内置表,因此我们可以自由决定表的数量和名称

  2. 其次,我们可以自由决定表的地址簇,当然,每个表只有一个地址簇,其中ip(也就是ipv4)是默认簇image-20230923022312098

    其中我们知道 inet不能用于nat类型,只能用于filter的链

Nftables 表 是 Nftables 的一种容器,存放 chain 链
Nftables 表 无法修改名称,仅存在 flag 可能会有更新。

Nftables 表 需要定义 数据包 的 family 地址簇 类型,用以限定引用范围
Nftables 表 对 规则顺序 没有影响,仅限定了 规则 的使用区域与地址簇类别
Nftables 表 可以使用 标志位 flag 暂时禁用所有规则,且仅有 dormant 唯一标志 {flags dormant;}

Nftables 表 所使用的 地址簇****family 一共有六大类
​ 主四类为 ip / ip6 / bridge / arp,分别对应 IPV4 / IPV6 / L2 / ARP 专项
​ 第五类为 ip&ip6 双栈使用的 inet , 不能搭配 nat 类型链,只能搭配 filter 类型链,且网络层流量会自动匹配对应协议栈规则
​ 第六类为 netdev ,优先级顺序最高,通常用于代码开发和排障测试使用。

​ Nftables 表的配置需要参考如下
可选项 [] 的默认值是 ip,在不填写的时候会自动填入
可选项 [] 的默认值是 无,在不填写的时候为激活,填写 {flags dormant;} 的时候暂时禁用

具体操作:

  1. 创建表

    1
    nft add|create  table [<family>] <name> [{ flags flags ; }]
  2. 列出表:

    1
    2
    3
    4
    5
    6
    7
    8
    # 列出所有表
    $ nft list tables [<family>]

    # 列出指定族的所有表
    $ nft list tables [<family>] <name> [-n] [-a]

    # 列出表中的所有规则:
    $ nft list table [<family>] <name>

    如:列出 inet 簇中 mytable 表中的所有规则
    nft list table inet mytable

  3. 删除一个表:

    1
    nft delete  table [<family>] <name> 

    只能删除不包含链的表

  4. 清空表中的链与规则:

    1
    nft flush table [<family>] <name>

介绍:

链是用来保存规则的,由于nftables不配置内置链,因此我们需要显式创建链

链内部规则按顺序从上到下执行

  1. 链分为两种类型:

    • 常规链:主要用来做跳转, 不需要指定钩子的类型和优先级,支持所有簇,主要以调度其他链和跳转为主
    • 基本链:来自网络栈数据包的入口点,需要指定钩子类型和优先级,支持 ipip6 簇,管理流经网络协议栈(TCP/IP)的数据包
  2. 基本链的类型:

    filter:过滤数据包
    route:对数据包进行重路由
    nat:对 数据流 进行 网络地址转换(数据流仅第一个包会因为钩子进入本链,之后即使有钩子也会绕过)

  3. 配置特点:

    Nftables 链 需要指定 类型 type 配置 流量应当以何种方式被控制(基础链强制)
    Nftables 链 需要调用 钩子 hook 抓取网络数据包 才会对数据进行处理(基础链强制)
    Nftables 链 需要调用 优先级 priority 管理链的优先级顺序(基础链强制)

  4. nftables依然使用 netfiler 的五个钩子

    img

    ​ 然而nftables在钩子上相较于iptables 的不同在于 他新增了ingress钩子

    img

  5. 各个钩子的作用:

    • prerouting:刚到达并未被 nftables 的其他部分所路由或处理的数据包(在这条钩子捕获 刚刚进入内核网络接口的数据包。)。
    • input:已经被接收并且已经经过 prerouting 钩子的传入数据包(捕获 打算进入本机的本地进程之前的数据包)。
    • forward:如果数据报将被发送到另一个设备,它将会通过 forward 钩子(捕获 那些通过并不进入主机的所有数据包)。
    • output:从本地传出的数据包(捕获 打算离开本机的本地进程产出的数据包)。
    • postrouting:仅仅在离开系统之前,可以对数据包进行进一步处理(捕获 刚刚进入内核网络接口的数据包)
    • ingress:在prerouting之前处理(捕获 网卡驱动 向 内核网络接口 传递的数据包。)
  6. 不同簇之间对于钩子的适用范围不同

    • ipip6inet 簇支持的钩子有: preroutinginputforwardoutputpostrouting
    • arp 簇支持的钩子有: inputoutput
    • netdev 地址簇表 可以使用 INGRESS 钩子类型
  7. nftables链的优先级:

    Nftables 基础链 优先级 priority 通过 顺序排列 进而 按顺序执行不同 钩子 hook,优先级小数值优先

    优先级采用整数值表现,数字越小处理优先级越高:

    见:

    image-20230929122518800

    • 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)

    特殊 1:当存在两个拥有 相同钩子 但 钩子优先级不同 的 A/B 基础链 (A<B),会先执行 A 链 然后 执行 B 链
    特殊 2:但当上方情况为 A 链 执行 Drop 时,此时数据包 Drop 行为会立即生效,并终止继续执行

  8. 链有几个常见策略:

    • accept(常用)
    • drop (常用)
    • queue
    • continue
    • return

基本语法

  1. 创建常规链:

    1
    2
    3
    4
    nft create chain [<family>] <table> <name>
    # 如:

    nft add chain inet mytable tcpchain
  2. 创建一个基本链,必须指定钩子和优先级,基本链的类型可以是filterroute 或者 nat

    1
    2
    3
    4
    5
    nft (add | create) chain [<family>] <table> <name> [ { type <type> hook <hook> [device <device>] priority <priority> \; [policy <policy> \;] } ]

    # 例:添加一个筛选输入数据包的基本链

    nft add chain inet mytable input { type filter hook input priority 0\; }

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

  3. 列出链的所有规则:

    1
    nft list chain [<family>] <table> <name>
  4. 编辑链

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

1
2
# 将input链的策略更改成drop
nft chain inet mytable input { policy drop \; }
  1. 清空/删除链:

    1
    nft (delete | flush) chain [<family>] <table> <name>

    要删除的链中不能包含任何规则或者跳转目标。

规则

基本操作

​ nftables 的规则包含在链中,创建nftables规则的基本语法有:

  1. 将规则添加到链尾:

    1
    nft add rule [<family>] <table> <chain> <matches> <statements>
  2. 将规则添加到链头:

    1
    nft insert rule [<family>] <table> <chain> [position <position>] <matches> <statements>
  3. 指定位置增加规则

    1
    2
    3
    4
    5
    6
    # 可以通过index/handle来指定增加规则的位置
    # 例子:
    # 在 input 链中已有规则中的第二条规则前插入一条新的规则
    nft insert rule inet mytable input index 1 tcp dport nfs accept
    # 在 input 链中已有规则中的第一条规则后插入一条新的规则
    nft add rule inet mytable input index 0 tcp dport 1234 accept

    注意:index 的值是从 0 开始的,index *必须指向一个已存在的规则的索引。

​ 也可以使用handle来指定规则的句柄

  1. 通过handle来指定规则添加的位置

    通过 handle 的值来指定规则添加的位置,必须先知道现有规则的句柄位置。你可以通过参数 --handle 来获取当前规则的句柄位置。

    1
    2
    nft --handle list ruleset
    # 获取到当前规则的句柄位置后,我们就可以在指定句柄位置添加规则

​ 在 nftables 中,句柄值是固定不变的,除非规则被删除。而 index 的值是可变的,只要有新规则插入,就 有可能发生变化。一般建议使用 handle 来插入新规则.

​ 你也可以在创建规则时就获取到规则的句柄值,只需要在创建规则时同时加上参数 --echo--handle

  1. 更改规则;

    1
    nft replace rule [<family>] <table> <chain> [handle <handle>] <matches> <statements>
  2. 删除规则:

    1
    nft delete rule [<family>] <table> <chain> [handle <handle>]

    单个规则只能通过句柄值进行删除

matches 匹配

​ 其中,matches为报文需要满足的条件:

  1. 目前有以下几种报文可以被识别:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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 : 报文的基本信息
  2. 每种类型的报文可以检查多个字段,比如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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 }

具体有哪些match可以见:

https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes#Rules

https://www.mankier.com/8/nft#Expressions到https://www.mankier.com/8/nft#Payload_Expressions

statments 触发

statments作为保温匹配规则时触发的操作,有以下几种:

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

而Verdict Statements 是一系列报文匹配match时触发的操作:

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

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

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

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

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

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

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

rule中具体规则详见:https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes#Rules

statment详解见:https://www.mankier.com/8/nft#Statements

进阶

集合

nftables 的语法原生支持集合,可以用来匹配多个 IP 地址、端口号、网卡或其他任何条件。

  1. 匿名集合:

    匿名集合适用于将来不需要更改的规则,但如果要修改集合规则,就得将原规则进行替换:

    如:

    1
    2
    3
    4
    5
    # 允许来自源 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
  2. 命名集合:

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

    • ipv4_addr : IPv4 地址
    • ipv6_addr : IPv6 地址
    • ether_addr : 以太网(Ethernet)地址
    • inet_proto : 网络协议
    • inet_service : 网络服务
    • mark : 标记类型

    例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    # 创建一个空的命名集合
    $ 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
    }
    }
  3. 支持区间

    如果想要在上述的集合钟使用区间,可以加一个flag interval

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 创建一个支持区间的命名集合
    $ nft add set inet mytable my_rangeset { type ipv4_addr \; flags interval
    $ nft add element inet mytable my_rangeset { 10.10.10.0/24 }# 使用了子网掩码来表示 IP 地址段,它会被隐式转换为 IP 地址的区间
    $ nft list set inet mytable my_rangeset
    table inet mytable {
    set my_rangeset {
    type ipv4_addr
    flags interval
    elements = { 10.10.10.0/24 }
    }
    }
  4. 级联不同类型

    集合支持不同类型的元素进行级联

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    $ nft add set inet mytable my_concatset  { type ipv4_addr . inet_proto . inet_service \; }

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

字典

字典支持在一条规则上面使用不同类型的数据

当然字典同集合一样,也分为命名字典和匿名字典

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 创建命名字典
$ 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 mytable input ip saddr vmap @my_vmap

# 匿名字典
$ 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 中,每个表都是一个独立的命名空间,这就意味着不同的表中的链、集合、字典等名字可以相同。

如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ 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

  2. 恢复规则:

    $ nft -f /root/nftables.conf

后记

大概的介绍了一下nftables的作用,感觉没啥要补充的了,接下来就要到实战了😋

后续:懒得整了,实战连接:

https://icloudnative.io/posts/use-nftables-as-firewall/

https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/8/html/securing_networks/using-verdict-maps-in-nftables-commands_getting-started-with-nftables

然后这byd写了我小一周,主要全在了解网络协议去了。唉,网络

参考链接: