用户名  找回密码
 立即注册

QQ登录

只需一步,快速开始

帖子
热搜: 活动 交友 discuz
查看: 54|回复: 0

[一] 从0开始的 nftables 教程 [入门]

[复制链接]

2

主题

0

回帖

6

积分

新手上路

积分
6
发表于 2025-4-9 19:18:50 | 显示全部楼层 |阅读模式
前言
突发奇想开了个新系列,主要内容就是关于 Linux 网络栈的工具,例如 策略路由 ,nftables 等。nftables比较简单且强大,就先开nftables的教程
为什么写这种文章
  • 大伙平常很多时候都是和网络打交道,而 nftables 作为现在大部分系统自带的网路管理工具,我觉得非常有必要了解。
  • 通过 nftables 可以慢慢了解到Linux的网络构成,对于运维和解决疑难杂症是必须要了解的。
  • 现在大部分应用已经开始从 iptables 过渡到 nftables。
  • 国内,包括论坛内关于 nftables 的文章不多。

介绍
在上一篇文章 ,已经介绍了相关的关于nftables的内容,这里再详细介绍下。
这里就抄 Wiki 的内容了。
nftables is the modern Linux kernel packet classification framework. New code should use it instead of the legacy {ip,ip6,arp,eb}_tables (xtables) infrastructure.nftables是现代Linux内核的数据包处理框架。在之后新写的代码应该适配nftables而不是传统的 {ip,ip6,arp,eb}_tables (xtables) 等。
人话:未来使用应该使用 nftables了,因为nftables已经完全包含了上面的 {ip,ip6,arp,eb}_tables (xtables) 的功能,而且更加现代,以往的 设备,例如iptables,应该慢慢抛弃。新写的代码也应该适配nftables了。
支持状态
现在已经有不少的项目支持了 nftables ,据我日常使用的东西。
Wg-quick , Podman , Libvirtd, firewalld 等已经支持了
但是有部分项目,例如 UFW , Docker 等 还不支持,他们都是通过 iptables-nft 翻译为 nft 来支持的。
如果你要使用现成的 防火墙软件,同时你的环境是nftables, 推荐使用 firewalld ,而不是 UFW。
在 Openwrt 22.xx 过后的版本,防火墙从 fw3 升级为了 fw4 ,带来了原生的 nftables 支持。
正文
本文的示例没有经过稳定的测试,请勿在生产环境中使用。
本文的示例没有经过稳定的测试,请勿在生产环境中使用。
本文的示例没有经过稳定的测试,请勿在生产环境中使用。
环境
如果你是debian系统,在 debian10 开始就是默认的管理框架了。来源
nftables is the default framework in use in Debian (since Debian 10 Buster)
如果你没有 nftables作为默认(有些云厂商打包的镜像没有,例如 Claw)
可以使用下面的命令来安装。
sudo apt update && sudo apt remove iptables && apt install nftables
这里我就用 debian12 来演示
和 iptables 的不同(小白可以直接跳过) 复制自 https://www.cnblogs.com/charlieroro/p/17982707
nftables使用了新的语法:nftables使用了类似tcpdump的紧凑语法
可以完全配置tables和chains:iptables中有一些预定义的tables和chains(即使不需要),会对性能造成一定的影响。而nftables则没有预定义的tables和chains,因此需要明确定义各个table,以及其包含的对象(chains、sets、maps、flowtables和state object)。你可以定义table和chain的名称以及netfilter hook优先级。
单个nftables可以执行多个动作:iptables中通过匹配只能执行单个动作,但在nftables 规则中可以包含0或多个expressions(用于匹配报文),以及1或多个statements,每个expression会测试一个报文是否匹配特定的payload字段或报文/流的元数据。多个expressions会从左到右作线性评估,如果第一个expression匹配成功,则继续评估下一个expression。如果匹配了所有的expressions,则会执行statement。
每个statement会执行一个动作,如设置netfilter mark、计算报文数、记录报文日志或做出诸如接收或丢弃报文或跳到另一个chain的决定。多个statements也是从左到右线性执行的,这样一条规则可以通过多个statements执行多个动作(需要注意的是verdict statement 会终结规则)。
chain和rule中没有内置的计数器:nftables中的计数器是可选的,可以在需要时启用
可以更好地支持动态规则更新:iptables的规则是一体式的,一条规则的变更会影响到整体规则。而nftables的ruleset在内部以链表表示。现在,添加或删除一条规则不会影响道其他规则,从而简化了内部状态信息的维护。
简化了IPv4/IPv6双栈管理: nftables的inet family 可以同时支持IPv4和IPv6的chain。不再需要脚本来复制规则
新的通用型基础结构:该基础结构与nftables的核心紧密集成,并支持高级配置,如maps, verdict maps 和 intervals ,以实现面向性能的报文分类。
支持串联(Concatenations):从Linux kernel 4.1开始,可以串联多个keys,并将它们与maps 和 verdict maps相结合。其思想是构建一个元组,并该元组的值进行哈希以获得接近O(1)的查找效率。
无需升级内核即可支持新协议:内核升级一项耗时且艰巨的任务,尤其是当你必须在网络中维护多个防火墙时。分发的内核版本通常滞后于最新版本。当虚拟机使用nftables时,支持新协议通常不需要更新内核,只需要更新用户空间的nft即可。
nftables的基础使用
nftables提供了几个基础选项用来管理表和链,可以使用 nft --help 来查看。
这里讲解几个文章会用到的。
选项:
-f, --file <filename>           Read input from <filename>---------------------------------------- 这个选项后面跟一个文件,用于加载 nftables 文件,类似于 iptables-persistent 但是这个更加的好用和方便。 注意的是 nft 加载文件没有原子性,推荐在加载前先检查一遍。至于什么是原子性,自行查询。---------------------------------------- -c, --check                     Check commands validity without actually applying the changes.----------------------------------------这个选项可以检测写的 nftables 文件是否符合标准(补充,通常只需要看前面几个报错就知道什么问题,因为后面的报错通常是前面的报错引起的...)## nft -c -f /etc/nftables.conf 检查 /etc/nftables.conf 这个文件中的所有规则(不加载)。## nft -f abc.nft 加载 abc.nft 这个文件的所有内容。
子命令:
list ----------------------------------------list 子命令可以列出相关的存在于nftables的内容。## nft list ruleset 可以列出所有的规则。## nft list table <family> <table_name> 可以列出一个表的全部规则。## nft list chain <famyil> <table_name> <chain_name> 可以列出一个表中的一条链(chain)的全部规则。## nft list set <famyil> <table_name> <set_name> 可以列出一个集合(set)。----------------------------------------monitor # monitor 子命令可以监视进入 nftables 流量的匹配流程。----------------------------------------不过多介绍,见 https://wiki.nftables.org/wiki-n ... --------------flush ----------------------------------------flush 子命令可以清空 chain table set 等等 都可以## nft flush ruleset 清空全部规则。## nft flush table <family> <table_name> 情况一个表的全部内容。## ... 后面和上面的 list 都差不多的。----------------------------------------delete ----------------------------------------和 flush 差不多,但是 这个会删除(不见) 上面那个只是清空了里面内容。----------------------------------------
因为本文大部分都是以文件来教材的,所以其他的例如 add 子命令自行可以学习。
文件规范
不同于 iptables 依赖 iptables-persistent 才可以实现持久化可复现的配置,nftables 自带可复现配置,而且平常使用和运维大部分都是靠这个来处理的。
有几个文件和文件夹需要关注,同时可以帮助我们学习nft的文件(详细见注释)。
  • /etc/nftables.conf
#!/usr/sbin/nft -f----------------------------------------文件开头的 #!/usr/sbin/nft -f 是一个特殊的指令,称为'Shebang'。当你直接执行这个脚本文件时(比如通过 /etc/nftables.conf 命令),操作系统会查看这一行,并明白应该使用 /usr/sbin/nft -f 解释器来运行这个脚本。换句话说,这行代码告诉系统:'嘿,请用nft解释器来执行这个文件里的命令'。如果没有这一行,系统可能会使用默认的shell,而不一定是nft。----------------------------------------flush ruleset----------------------------------------该命令清空当前系统中的所有nftables规则。确保配置从干净的状态开始,避免旧规则与新规则冲突。当然你也可以不用这个规则(其实本人并不推荐使用 flush ruleset 因为也会清空其他程序的规则)可以用下面的规则来单独删除一个表。# table inet filter# delete table inet filter或者 (linux>=6.3)# destroy table inet filter 其实你看上面的命令不难发现flush ruleset 就是 nft flush ruleset 少了一个 nft 命令前缀。是的没错,nft 就是通过不断执行这个文件里面的指令来管理的。----------------------------------------table inet filter {----------------------------------------table 字段指明了这是一个表,通常作为最顶级的部分来处理的。其余部分 例如 set vmap quota chain 都是属于 table的子部分。inet 字段代表了这个 table 支持的 协议族,表示该表同时处理IPv4和IPv6流量其他类似的还有ip  支持 ipv4ip6 单独支持 ipv6arp netdev bridge 可以自行查阅filter 代表的是表名,可以随意取,但必须是英文和部分不作为关键字的字符。----------------------------------------        chain input {        ----------------------------------------        chain 代表了定义一个链,在链里面,可以在内核的 netfilter 挂载对应的挂载点。        同时 从上到下 从左到右 执行链里面的规则。        input 只是名字,可以自己取名的 包括后面的 chain output 也是名字而已,没有很多实际意义。        ----------------------------------------                type filter hook input priority filter;                ----------------------------------------                这一行定义了一个这个链的挂载点,同时指定了这个链条的类型。                type filter 表面了这个链条可能的作用就是 filter(过滤器)                hook input 表明了挂载了 filter 的 input(输入) 点                所有需要进入网卡的流量都需要经过这个 chain(除非在前面部分drop掉了)                priority filter 表面了这个挂载点的优先级,其实 filter 是一个内部定义的数字。                你可以用加减法对其数学计算 例如                 filter + 5 (注意每个字段要用空格分隔)                 filter + 100                也可以直接写数字 例如                 -150                 100                只不过使用 filter 字段更加的清晰。                ----------------------------------------                        }        chain forward {                type filter hook forward priority filter;                ----------------------------------------                类型还是 filter 但是这个挂载了 fowrard 点                所有需要转发的流量都会经过这个点(除非在前面部分drop掉了)                ----------------------------------------        }        chain output {                type filter hook output priority filter;                ----------------------------------------                挂载了 output 点,这个点是本机流量发出走到的第二个点。                第一个点是 type route hook output                这个可以对 本机发出的流量/转发出去的流量 做处理。                ----------------------------------------        }}
  • /etc/nftables.d/
这个文件夹下面存放的都是关于 nftables 的文件,命名方式通常为 数字-作用.nft
例如
00-allow-service.nft10-allow-forward.nft
然后只需要在 /etc/nftables.conf 中添加一行
include "/etc/nftables.d/*.nft"
就可以实现吧多个nft规则分为多个文件了。
问: 为什么需要使用 数字 开头呢?答: 其实是为了控制加载表的顺序。问: 为什么需要控制加载表的顺序?答: 还记得上面文件说明的挂载点吗,如果有多个链条,挂载了同一个点且优先级相同那么nftables底层就会以表的顺序来执行这个链条的内容。例如 00-zero.nft 和 01-first.nft 的文件都定义了一个表,内部都有一个链条定义为 type filter hook output priority filter;那么执行顺序就会变为 00-zero.nft 文件内的链条先执行,01-first.nft 文件内的链条后执行。附: 其实你可以查看 /etc/sysctl.d/ 这个文件夹下面的文件,都是 数字-作用 这样组合的,也是为了控制加载顺序。问: 为什么需要分为多个文件答: 易于维护,如果全部规则写一个文件里面,难以维护。实战
在开始前: 我其实前面没有介绍到 rule 的规范,但是其实写 rule 是非常容易理解的。
这里引用下 上文 关于 nftables 的匹配顺序的介绍。
nftables 规则中可以包含0或多个expressions(用于匹配报文),以及1或多个statements,每个expression会测试一个报文是否匹配特定的payload字段或报文/流的元数据。多个expressions会从左到右作线性评估,如果第一个expression匹配成功,则继续评估下一个expression。如果匹配了所有的expressions,则会执行statement。每个statement会执行一个动作,如设置netfilter ,mark、计算报文数、记录报文日志或做出诸如接收或丢弃报文或跳到另一个chain的决定。多个statements也是从左到右线性执行的,这样一条规则可以通过多个statements执行多个动作(需要注意的是verdict statement 会终结规则)。
这里我先根据上文的表写一个例子先。
关于 nft 表中 rule 的匹配器 可以在这个页面 找到。
#!/usr/sbin/nft -fflush rulesettable inet filter {        # 三个常见的操作        # accept        # 接受这个流量,不再匹配后面的链条和后面的规则        # 直接在这里终止,会跳到下一个挂载点。                # drop         # 丢弃这个流量,同样,后面的链条和规则也不会匹配了        # 下一个挂载点也不会,直接这个包就忽略了。                # return         # 从当前 rule 返回,不匹配这个链条里面后面的规则了,直接进入下一个条 链/挂载点 。        chain input {                type filter hook input priority filter; policy drop;                # 注意这里多了一个 policy drop 字段                # 代表了如果经过这个链没有accept(接受) 的流量都会drop(丢弃) 掉                                # 接受 lo(本地 loopback 网卡) 的流量。                iifname lo accept                # 如果一个连接已经建立了,就接受这个连接的流量。                ct state established,related accept                # 允许 icmp 和 igmp(icmpv6)                # 注意的是 ip 在 nftables 中通常表示 ipv4 并不代表 ipv6                # 参考 表的 family , inet, ip , ip6 等。                ip protocol icmp accept                # 这里用 nexthdr 而不用 protocol 是因为ipv6 的命名更加规范                # 而nexthdr 代表的是 网络层 ipv6 数据包的一个字段。                ip6 nexthdr icmpv6 accept                                # 允许SSH连接                tcp dport 22 accept        }        chain forward {                type filter hook forward priority filter; policy accept;                # 这里多了一个 policy accetp 字段,代表如果没有匹配这个链                                # 如果外部需要转发到 udp://1.1.1.1:53 就返回                ip daddr 1.1.1.1 udp dport 53 return                # 如果来源ip为 1.2.3.4 ,转发协议为tcp, 目标端口是 443 的就丢弃                ip saddr 1.2.3.4 tcp dport 443 drop        }        chain output {                type filter hook output priority filter;                # 如果没有 policy 默认是 accept        }}
可以看到 nft 的rule写起来是十分方便的,语法十分的清晰,大体结构就是从左到右边匹配,如果 数据包/帧 满足当前规则就继续匹配,直到遇到终结规则,例如 accept, drop , return 等。
示例
  • /etc/nftables.d/00-allow-common-traffic.nft
#!/usr/sbin/nft -ftable inet allow-common-trafficdelete table inet allow-common-traffictable inet allow-common-traffic {    chain input {        # 这里优先度 filter - 10 确保所有的流量在匹配其他链之前都匹配下这个链条。        type filter hook input priority filter - 10;policy drop;        # 允许已建立的连接        ct state established,related accept                # 允许本地回环接口        iif lo accept                # 允许ICMP IGMP        ip protocol icmp accept        ip6 nexthdr icmpv6 accept                # 允许DNS查询        udp dport 53 accept                # 允许HTTP和HTTPS        tcp dport { 80, 443 } accept                # 允许SSH        tcp dport 22 accept    }}
  • /etc/nftables.d/10-only-allow-cloudflare.nft
#!/usr/sbin/nft -ftable ip only-allow-cloudflaredelete table ip only-allow-cloudflare# 这里我只对 cloudflare v4 回源做处理# 如果你需要 v6 回源可以添加一个 set 处理。table ip only-allow-cloudflare {    set cloudflare_v4 {        type ipv4_addr        flags constant,interval        auto-merge        elements = {             103.21.244.0/22,             103.22.200.0/22,             103.31.4.0/22,             104.16.0.0/13,             104.24.0.0/14,             108.162.192.0/18,             131.0.72.0/22,             141.101.64.0/18,             162.158.0.0/15,             172.64.0.0/13,             173.245.48.0/20,             188.114.96.0/20,             190.93.240.0/20,             197.234.240.0/22,             198.41.128.0/17         }    }    chain forward {        type filter hook input priority filter + 5;policy accept;        # 只允许 Cloudflare 回源访问 443和80        ip saddr != @cloudflare_v4 tcp dport { 80,443 } drop    }}
  • /etc/nftables.d/55-nat-port-forward.nft
#!/usr/sbin/nft -ftable inet port-forward delete table inet port-forward table inet port-forward {    chain dnat-to{        # 注意这里 policy 要为 accept, 因为如果不是 accept 可能其他不需要 转发的流量会丢弃掉。        type nat hook prerouting priority dstnat;policy accept;        ip protocol udp th dport 53 dnat to 1.1.1.1         ip protocol tcp th dport 8080 dnat to 1.2.3.4:8090         # 这里用 th 来匹配目标端口是为了简明的语法        # 关于 th 关键字         # https://unix.stackexchange.com/q ... -line-with-nftables            }        chain snat-to {        type nat hook postrouting priority srcnat;policy accept;        ip daddr 1.1.1.1 masquerade        ip daddr 1.2.3.4 masquerade         # 这里记得伪装下(snat) 保证本机转发的流量发出到达1.1.1.1 回来的流量还会回到本机。    }}
  • /etc/nftables.d/01-deny-ipv6-output.nft
#!/usr/sbin/nft -ftable ip6 deny-ipv6-outputdelete table ip6 deny-ipv6-outputtable ip6 deny-ipv6-output {    chain output {        type route hook output priority mangle;policy drop;        # 允许IPv6回环通信        ip6 daddr ::1 accept                # 允许本地链路IPv6通信        ip6 daddr fe80::/10 accept                drop    }}
  • /etc/nftables.d/20-fail-2-ban.nft
#!/usr/sbin/nft -ftable inet fail2bandelete table inet fail2bantable inet fail2ban {    # 创建SSH防暴力攻击集合    set blocklist_v4 {        type ipv4_addr;        flags dynamic, timeout;        size 65536;        timeout 1h;    }    set blocklist_v6 {        type ipv6_addr;        flags dynamic, timeout;        size 65536;        timeout 1h;    }        # 创建SSH连接计数集合    set conntrack_v4 {        type ipv4_addr;        flags dynamic, timeout;        size 65536;        timeout 60s;     }    set conntrack_v6 {        type ipv6_addr;        flags dynamic, timeout;        size 65536;        timeout 60s;    }        chain input {        type filter hook input priority filter + 5; policy accept;        meta nfproto ipv4 tcp dport 22 ct state new \        jump check_ipv4_brute_force        meta nfproto ipv6 tcp dport 22 ct state new \        jump check_ipv6_brute_force    }    chain check_ipv6_brute_force {        add @conntrack_v6 { ip6 saddr }        ip6 saddr @conntrack_v6 limit rate over 5/minute burst 5 packets \        log prefix "[SSH-BLOCK-IPv6] " flags all \        add @blocklist_v6 { ip6 saddr } drop    }    chain check_ipv4_brute_force {        add @conntrack_v4 { ip saddr }        ip saddr @conntrack_v4 limit rate over 5/minute burst 5 packets \        log prefix "[SSH-BLOCK-IPv4] " flags all \        add @blocklist_v4 { ip saddr } drop    }}
可以看到使用 nftables 可以实现很多你们平常使用的 fail2ban ,只允许 cloudflare 回源,屏蔽ipv6 流量发出 等操作,包括常用的 统计,限制 上传下载流量也都可以使用nftables来实现。
最近的 YXVM 双IP问题,需要原生网卡发出流量的问题,也可以用 nftables 来解决。
使用
nftablse 在服务器重启后会丢失,如果要实现重启后加载nftables的内容,可以使用下面的命令。
systemctl enable nftables.service
这个命令会在开机的 multi-user.target 加载完成加载 /etc/nftables.conf 这个文件。
如果你需要加载单个文件,例如上面的示例,可以直接使用下面命令加载。
nft -f <文件名.nft>
如果你需要检查文件是否正确,可以使用下面的命令。
nft -c -f <文件名.nft>
这个会检查但是不加载文件,推荐在加载(应用)前都先检查一遍。
查看
加载完后如果想要查看加载的规则。
nft list ruleset
如果要查看对于的表,集合,链之类的,可以按照上面的教程命令来实现查看。
小结
nftables 能实现的事情可不只这一点,具体的可以去查看相关的官方WIKI 来具体查看相关示例和用法。
这一次只介绍了 nftables 的基础用法,下一期会介绍在 Openwrt环境下的 fw4 相关用法和更多的语法糖和使用例子,例如 Tproxy。
欢迎大家提出想看的内容,或者改进建议,每个评论我都会认真看。
希望大家多给点鸡腿让我升级。写这么长一篇文章真的不容易。

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|一起港湾 ( 青ICP备2025004122号-1 )

GMT+8, 2025-4-25 05:55 , Processed in 0.102049 second(s), 21 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表