• nftables(8)MAPS、VMAPS


    MAPS

    MAPS简介

    上篇文章我们介绍了SETS集合相关的内容,本篇文章主要介绍map,在nftables中,Map(映射)用于存储键值对,类似于许多编程语言中的关联数组/字典/哈希表。在nftables规则中,可以指定一个数据包字段(例如:tcp目的端口),然后引用一个映射来搜索具有与数据包字段值匹配的键的映射元素,并返回该映射元素的值(或者如果映射中没有匹配元素则返回失败)。

    可以把map想象成一个集合,但它不仅返回“在集合中/不在集合中”的结果,而且返回一个具体的值。在内部实现上,集合和map都使用了相同的通用集合基础设施,因此它们共享许多相同的选项和语义。

    maps和前面所介绍的集合非常相似也分为匿名映射(Anonymous Maps)和命名映射(Named Maps),匿名映射和命名映射的区别如下:

    区别

    匿名映射命名映射
    定义方式直接在规则中定义事先声明,独立于规则存在
    重用性通常只在该规则中使用,不具有重用性可以在多个规则中引用,具有高度的可重用性
    可维护性由于直接在规则中定义,可能较难维护由于具有名称和独立的声明,更容易维护和修改
    动态性不支持动态更新(因为直接在规则中定义)支持动态更新,可以添加、删除或修改映射元素
    适用场景适用于简单的映射需求,快速定义和使用适用于复杂的网络策略,需要高度灵活性和可维护性的场景

    匿名映射

    虽然这么区分,但是在nftables的上下文中,实际上并没有直接称为“匿名映射”的明确概念,因为nftables中的映射(sets)通常是通过名称来引用的,以便在多个规则中重复使用。然而,可以理解为在某些场景下,nftables规则中直接使用了类似映射的功能,但这些功能并没有事先声明为独立的命名映射,而是直接在规则表达式中定义,这种情况可以视为一种“匿名”的使用方式。

    不过,由于nftables的规则和表达式设计并不直接支持传统意义上的“匿名映射”,我将通过下面的示例来说明如何在nftables规则中模拟类似映射的功能,尽管这些并不是严格意义上的匿名映射。

    如上图所示,这是前面我们在做测试的时候我们配置的内容,我们在规则中直接使用了IP地址列表,这可以视为一种“匿名”的映射方式。

    命名映射

    命名映射的语法如下:

    1. add map [family] table map { type type | typeof expression [flags flags ;] [elements = { element[, ...] } ;] [size size ;] [comment comment ;] [policy 'policy ;] }
    2. {delete | destroy | list | flush | reset } map [family] table map
    3. list maps [family]
    命令/属性描述
    add map在指定的表中添加一个新的Map。
    delete map删除指定的Map。
    destroy map删除指定的Map,如果不存在则不会失败。
    list map显示指定Map中的元素。
    flush map从指定的Map中移除所有元素。
    reset map重置Map中所有元素的状态,例如计数器和配额的值。
    [family]Map所属的地址族,如ipip6等。
    [table]Map所属的表。
    [map name]用户定义的Map名称。
    typeMap元素的数据类型,如ipv4_addripv6_addrether_addrinet_protoinet_servicemarkcounterquota(注意:counterquota不能用作键)。
    typeof通过表达式推导出的Map元素数据类型。
    flagsMap标志,与Set标志相同。
    elementsMap包含的元素,具体数据类型取决于typetypeof
    sizeMap中元素的最大数量,无符号64位整数。
    policyMap策略,如performance(默认)或memory

    Set 和 Map 标志

    标志描述
    constantSet/Map的内容在创建后永远不会改变。
    dynamicSet/Map必须支持从数据包路径使用addupdatedelete关键字进行更新。
    intervalSet/Map必须能够存储间隔(范围)。
    timeoutSet/Map必须支持元素超时(元素过期后自动删除)。

    以上的这些参数是不是和我们前面所配置的set很相似,因为它们使用了相同的通用集合基础设施。

    添加元素语法如下

    1. {add | create | delete | destroy | get | reset } element [family] table set { ELEMENT[, ...] }
    2. ELEMENT := key_expression OPTIONS [: value_expression]
    3. OPTIONS := [timeout TIMESPEC] [expires TIMESPEC] [comment string]
    4. TIMESPEC := [numd][numh][numm][num[s]]
    命令描述
    add向集合或映射中添加一个或多个元素。如果元素已存在,则可能失败或更新现有元素(取决于上下文)。
    create类似于add,但要求添加的元素在集合或映射中必须不存在。如果已存在,则操作失败。
    delete从集合或映射中删除一个或多个元素。在映射中,如果指定了值表达式,则必须完全匹配键和值才能删除;如果只指定了键表达式,则只根据键来删除。
    destroy删除整个集合或映射,包括其所有元素。这是一个更彻底的操作,用于移除不再需要的集合或映射。
    get检查集合或映射中是否包含特定的元素。对于大型或区间集合,如果元素存在,则可能返回包含该元素的区间而非元素本身。
    reset重置与给定元素相关联的状态,如计数器或配额语句的值。这通常用于清除元素的状态信息。

    元素选项

    选项描述
    timeout为具有超时标志的集合或映射中的元素设置超时值。当元素达到指定的超时时间后,它可能会被自动删除或标记为过期。
    expires设置给定元素的过期时间。这个选项主要用于规则集复制的场景,以指示元素何时应被视为过期。
    comment为元素添加注释字段。这有助于在查看或调试规则集时提供有关元素的额外信息。

    配置命名映射

    创建map

    如上图所示,创建了一个nat表,和postrouting链,然后创建了一个map,端口和IP地址的映射关系

    添加元素

    添加两个元素,80对应192.168.143.1和8080对应192.168.143.3

    引用map

    配置一个snat关联到这个snat,意思是到达目的端口为80的,将源地址修改为143.1,到达目的端口为8080的将源地址修改为143.3。有关NAT的相关内容大家可以参考iptables/firewalld相关部分的内容,nftables nat的相关内容也会在后续文章中进行介绍。

    vmaps

    Verdict Maps (vmaps) 在 nftables 防火墙规则集中是一种特殊类型的映射(map),它们允许将元素直接映射到裁决(verdict)语句上。裁决语句决定了当匹配到特定规则时应该采取的动作,比如接受(accept)、拒绝(reject)或丢弃(drop)数据包。

    使用 vmap 语句创建的 Verdict Maps 内部基于通用的集合(set)基础设施,因此它们共享一些语义和选项。某些文档中可能将 vmaps 称为字典(dictionaries),在 nftables 上下文中具有特定的用途和行为。

    1. expression vmap { VMAP_ELEMENTS }
    2. VMAP_ELEMENTS := VMAP_ELEMENT [, VMAP_ELEMENTS]
    3. VMAP_ELEMENT := key : verdict

    裁决语句(verdict statement)

    裁决语句描述
    accept终止规则集评估并接受数据包。数据包仍可能在后续钩子中被丢弃,例如在forward钩子中接受的数据包可能在postrouting钩子或之后评估的具有更高优先级编号的forward基础链中被丢弃。
    drop终止规则集评估并丢弃数据包。丢弃操作立即发生,不再评估其他链或钩子。一旦数据包被丢弃,后续链中不会再评估该数据包。
    queue终止规则集评估并将数据包排队到用户空间。用户空间必须提供丢弃或接受裁决。如果接受,处理将在下一个基础链钩子中恢复,而不是在queue裁决之后的规则。
    continue继续评估规则集中的下一条规则。这是在没有裁决的情况下规则的默认行为。
    return从当前链返回并继续在上一个链中的下一条规则进行评估。如果在基础链中发出,则等同于基础链的策略。
    jump chain在链中的第一条规则处继续评估。当前规则集位置被推送到调用堆栈,并在新链完全评估或发出return裁决后,评估将在那里继续。如果链中的规则发出绝对裁决,则立即终止规则集评估并执行特定操作。
    goto chain类似于jump,但当前位置不会被推送到调用堆栈,这意味着在新链评估完成后,将在上一个链(而不是包含goto语句的链)中继续评估。

    Anonymous vmaps(匿名映射)

    nftables本身不直接支持“匿名映射”的概念,但我们可以将这个概念理解为那些没有显式命名的映射。然而,在nftables的实际使用中,几乎所有的映射都是命名的,因为你需要一个引用来在规则中引用它们。

    例如可以从逻辑上将对 TCP 和 UDP 数据包的处理规则拆分开来,那么vmaps是如何做的呢?

    我们先清空现有的nftables表,然后进行配置:

    如上图所示,先通过nft flush ruleset清空nftables表,然后添加表filter-vmap,添加链inpu-vmap,hook到input链上。再添加我们需要分流tcp和udp的链tcp-vmap ,udp-vmap。然后添加规则通过jump跳转到对应的链上。
    1. root@debian:~# nft flush ruleset
    2. root@debian:~# nft list ruleset
    3. root@debian:~# nft add table filter-vmap
    4. root@debian:~# nft add chain filter-vmap input-vmap { type filter hook input priority -200 \; }
    5. root@debian:~# nft list table filter-vmap
    6. table ip filter-vmap {
    7. chain input-vmap {
    8. type filter hook input priority -200; policy accept;
    9. }
    10. }
    11. root@debian:~# nft add chain filter-vmap tcp-vmap
    12. root@debian:~# nft add chain filter-vmap udp-vmap
    13. root@debian:~# nft list table filter-vmap
    14. table ip filter-vmap {
    15. chain input-vmap {
    16. type filter hook input priority -200; policy accept;
    17. }
    18. chain tcp-vmap {
    19. }
    20. chain udp-vmap {
    21. }
    22. }
    23. root@debian:~# nft add rule filter-vmap input-vmap meta l4proto vmap { tcp : jump tcp-vmap , udp : jump udp-vmap }
    24. root@debian:~# nft list table filter-vmap
    25. table ip filter-vmap {
    26. chain input-vmap {
    27. type filter hook input priority -200; policy accept;
    28. meta l4proto vmap { tcp : jump tcp-vmap, udp : jump udp-vmap }
    29. }
    30. chain tcp-vmap {
    31. }
    32. chain udp-vmap {
    33. }
    34. }

    测试

    现在已经分流了,我们可以对效果进行测试。如果我们需要匹配放行从192.168.140.248访问本机的80端口的tcp流量应该怎么做呢?

    注意,我们在添加rule的时候,没有指定input,因为该条规则是跳转过来的,跳转规则本来就在input,所以必须要再次指定input参数,当我们140.248通过http访问过来的时候就会匹配上该条规则。

    Name vmaps(命名映射)

    在上部分匿名映射中,我们可以可以从逻辑上将对 TCP 和 UDP 数据包的处理规则拆分开来,那么命名映射该如何做的呢?

    我们先清空现有的nftables表,然后进行配置:

    创建表和分流链

    清空防火墙,创建表filter-name-vmap和两个链tcp和udp

    创建默认链

    创建一个链,类型为filter,hook为input,将会通过这个链把tcp和udp流量分流到tcp和udp链上

    创建vmap

    创建一个vmap,名称为input-name-vmap,注意这个名称虽然和上面的链的名称是一样的,但是不是一个东西,名称根据自己的需要配置即可。注意上面的映射类型为inet_proto:verdict,即协议类型:verdict。因为我们需要的是匹配tcp/udp流量,所以这里的类型为inet_proto,如果是其他的如ip地址那么要变为ipv4_addr : verdict

    流量分流策略

    在vmap中配置将tcp跳转到input-name-tcp链udp跳转到对应的udp链上

    引用vmap分流策略

    在input-name-vmap链下通过@参数引用该vmap。此时就已经实现了tcp、udp流量分流。
    1. root@debian:~# nft flush ruleset
    2. root@debian:~# nft list ruleset
    3. root@debian:~# nft add table filter-name-vmap
    4. root@debian:~# nft add chain filter-name-vmap input-name-tcp
    5. root@debian:~# nft add chain filter-name-vmap input-name-udp
    6. root@debian:~# nft list table filter-name-vmap
    7. table ip filter-name-vmap {
    8. chain input-name-tcp {
    9. }
    10. chain input-name-udp {
    11. }
    12. }
    13. root@debian:~# nft add chain filter-name-vmap input-name-vmap { type filter hook input priority -200 \; }
    14. root@debian:~# nft list table filter-name-vmap
    15. table ip filter-name-vmap {
    16. chain input-name-tcp {
    17. }
    18. chain input-name-udp {
    19. }
    20. chain input-name-vmap {
    21. type filter hook input priority -200; policy accept;
    22. }
    23. }
    24. root@debian:~# nft add map filter-name-vmap input-name-vmap { type inet_proto : verdict \; }
    25. root@debian:~# nft list table filter-name-vmap
    26. table ip filter-name-vmap {
    27. map input-name-vmap {
    28. type inet_proto : verdict
    29. }
    30. chain input-name-tcp {
    31. }
    32. chain input-name-udp {
    33. }
    34. chain input-name-vmap {
    35. type filter hook input priority -200; policy accept;
    36. }
    37. }
    38. root@debian:~# nft add element filter-name-vmap input-name-vmap { tcp : jump input-name-tcp , udp : jump input-name-udp }
    39. root@debian:~# nft list table filter-name-vmap
    40. table ip filter-name-vmap {
    41. map input-name-vmap {
    42. type inet_proto : verdict
    43. elements = { tcp : jump input-name-tcp, udp : jump input-name-udp }
    44. }
    45. chain input-name-tcp {
    46. }
    47. chain input-name-udp {
    48. }
    49. chain input-name-vmap {
    50. type filter hook input priority -200; policy accept;
    51. }
    52. }
    53. root@debian:~# nft add rule filter-name-vmap input-name-vmap meta l4proto vmap @input-name-vmap
    54. root@debian:~# nft list table filter-name-vmap
    55. table ip filter-name-vmap {
    56. map input-name-vmap {
    57. type inet_proto : verdict
    58. elements = { tcp : jump input-name-tcp, udp : jump input-name-udp }
    59. }
    60. chain input-name-tcp {
    61. }
    62. chain input-name-udp {
    63. }
    64. chain input-name-vmap {
    65. type filter hook input priority -200; policy accept;
    66. meta l4proto vmap @input-name-vmap
    67. }
    68. }

    测试

    如果我们需要匹配阻止从192.168.140.248访问本机的80端口的tcp流量应该怎么做呢?我们在匿名映射那里配置的是放心,这里我们配置阻止。

    此时就已经阻止了140.248访问本机的80端口了

    总结

    通过我们前篇文章介绍的sets集合,和本篇文章介绍的maps和vmaps,可以极大的提升防火墙策略的灵活程度,我们所介绍的示例只是基础的规则应用和原理,更多的功能搭配和更复杂的事项方式需要自己根据需求自行进行探索。

  • 相关阅读:
    Code Snippet的使用
    计算机图形学入门11:图形管线与着色器
    ssm+vue+elementUI 基于微信小程序的游戏美术外包管理信息系统-计算机毕业设计
    Threejs着色器(GPU)编程——感温管网
    [论文笔记] MapReduce: Simplified Data Processing on Large Clusters
    高速公路车路协同网络需求研究
    Linux引导故障排除:从问题到解决方案的详细指南
    [附源码]SSM计算机毕业设计大学生心理咨询网站JAVA
    基于thinkphp5的物业管理系统
    UE5.1编辑器拓展【二、脚本化资产行为,快速更改资产名字,1.直接添加前缀或后缀2.通过资产类判断添加修改前缀】
  • 原文地址:https://blog.csdn.net/weixin_42064802/article/details/140457075