介绍
nftables 是一个netfilter项目,旨在替换现有的{ip,ip6,arp,eb}tables框架,为{ip,ip6}tables提供一个新的包过滤框架、一个新的用户空间实用程序(nft)和一个兼容层。它使用现有的钩子、链接跟踪系统、用户空间排队组件和netfilter日志子系统。
它由三个主要组件组成:内核实现、libnl netlink通信和nftables用户空间前端。 内核提供了一个netlink配置接口以及运行时规则集评估,libnl包含了与内核通信的基本函数,nftables前端是用户通过nft交互。
iptables的不足
iptables框架有一些局限性,无法轻松解决:
-
避免代码重复和不一致:许多iptables扩展都是特定于协议的,因此没有统一的方式来匹配数据包字段,而是为它支持的每种协议都有一个扩展。这使代码库中的代码非常相似,可以执行类似的任务:有效负载匹配。
-
通过增强的通用集和映射基础结构,可以更快地进行数据包分类。
-
通过新的inet系列简化的双堆栈IPv4 / IPv6管理,使您可以注册可查看IPv4和IPv6流量的基链。
-
更好的动态规则集更新支持。
-
就像其他Linux网络和Netfilter子系统一样,为第三方应用程序提供Netlink API。
-
解决语法不一致的问题,并提供更好,更紧凑的语法。
与iptables 不同点
- nftables 在
用户空间
中运行,iptables 中的每个模块都运行在内核(空间)
中
从用户的角度来看,nftable和iptables之间的主要区别是:
-
语法。iptables的命令行工具使用getopt_long() -基于解析器其中密钥总是由双减去,例如之前。–key或一个减号,例如 -p tcp。在这方面,nftables使用了更好的,更直观的和更紧凑的语法,该语法受tcpdump的启发。
-
表和链是完全可配置的。在nftable中,表是没有特定语义的链的容器。请注意,iptables附带了具有预定义数量的基链的表,您可以通过全有或全无的方式获得它们。因此,即使您只需要其中之一,所有链都已注册。过去我们得到的报告是,即使您根本不添加任何规则,未使用的基础链也会损害性能。使用这种新方法,您可以根据设置注册所需的链。此外,您还可以按照需要使用链优先级为管道建模,并为表和链选择任何名称。
-
不再区分匹配和目标。在nftables中,表达式是规则的基本构建块,因此,规则基本上是从左到右线性求值的表达式的组合:如果第一个表达式匹配,则对下一个表达式求值,依此类推,直到达到规则的最后一个表达式。表达式可以匹配某些特定的有效负载字段,数据包/流元数据和任何操作。
-
可以在一个规则中指定多个操作。在iptables中,您只能指定一个目标。这是一个长期存在的局限性,用户可以通过跳到自定义链来解决,但代价是使规则集结构稍微复杂一些。
-
每个链和规则没有内置计数器。在nftable中,这些是可选的,因此您可以按需启用计数器。
-
更好地支持动态规则集更新。在nftables中,如果添加新规则,则剩余的现有规则将保持不变,因为规则集以链表形式表示,这与整体式blob表示相反,后者在执行规则集更新时内部状态信息的维护很复杂。
-
简化的双堆栈IPv4 / IPv6管理,通过新的inet系列,可让您注册同时查看IPv4和IPv6流量的基链。 因此,您不再需要依赖脚本来复制规则集。
-
通用集和映射基础结构。这个新的基础设施紧密地集成到nftables核心中,它允许高级配置,如字典、映射和间隔,以实现面向性能的包分类。最重要的是,您可以使用任何受支持的选择器来对流量进行分类。
-
支持串连。从Linux内核4.1开始,您可以连接几个键并将它们与字典和映射结合起来。其思想是构建一个元组,对其值进行散列处理,以获得将在O(1)附近执行的操作。
-
无需内核升级的新受支持协议。内核升级可能是一项耗时且艰巨的任务。特别是如果您必须在网络中维护多个防火墙。出于稳定性方面的考虑,发行商通常包括一些较旧的Linux内核版本。使用新的nftables虚拟机方法,您很可能不需要进行此类升级即可支持新协议。相对简单的nft用户空间软件更新应足以支持新协议。
安装
# debian
apt-get install nftables
apt-get purge iptables
用法
nftables区分命令行输入的临时规则和从文件加载或保存到文件的永久规则。 默认配置文件是/etc/nftables.conf
,其中已经包含一个名为”inet filter”的简单ipv4/ipv6防火墙列表。
运行服务
systemctl enable nftables
systemctl start nftables
也就是重新加载下/etc/nftables.conf里的规则
检查规则集
# nft list ruleset
nftables相关模块
确保nftables的模块被加载,服务才能正常运行
# lsmod | grep '^nf'
nf_nat_masquerade_ipv4 13412 1 ipt_MASQUERADE
nf_conntrack_netlink 40449 0
nfnetlink 14696 2 nf_conntrack_netlink
nf_conntrack_ipv4 15053 4
nf_defrag_ipv4 12729 1 nf_conntrack_ipv4
nf_nat_ipv4 14115 1 iptable_nat
nf_nat 26787 3 nf_nat_ipv4,xt_nat,nf_nat_masquerade_ipv4
nf_conntrack 133387 6 nf_nat,nf_nat_ipv4,xt_conntrack,nf_nat_masquerade_ipv4,nf_conntrack_netlink,nf_conntrack_ipv4
配置
nftables的用户空间实用程序nft评估大多数规则集并传递到内核。规则存储在链中,链存储在表中。以下部分说明如何创建和修改这些结构。
以下所有更改都是临时的。要永久更改,请见规则集保存到nftables.service
加载的/etc/nftables.conf
中:
# nft list ruleset > /etc/nftables.conf
注意:
nft list
不输出变量的定义,如果在/etc/nftables.conf
中有任何变量定义将会丢失。规则中使用的变量将替换为其变量值。nft list ruleset
在nftables v0.7 (Scrooge McDuck)在同时使用ICMP和ICMPv6时有语法限制。如果导出用作新的规则集将会导致错误。有关信息和解决方案,请参阅 stackexchange post。
要从文件读取输入,请使用-f
参数:
# nft -f filename
注意,任何已加载的规则不会自动清空。
表(Tables)
表包含#链。与iptables中的表不同,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
类型的链。(source)有关地址簇的完整描述,请参见nft(8)中的
ADDRESS FAMILIES
章节。
在以下情况中,family
是可选的,如果未指定则设为ip
。
创建表
创建一个新的表:
# nft add table family table
列出表
列出所有表:
# nft list tables
列出表中的链和规则
列出指定表中的所有链和规则:
# nft list table family table
例如,要列出inet
簇中filter
表中的所有规则:
# nft list table inet filter
删除表
删除一个表:
# nft delete table family table
只能删除不包含链的表。
清空表
要清空一个表中的所有规则:
# nft flush table family table
链(Chains)
链的目的是保存#规则。与iptables中的链不同,nftables没有内置链。这意味着与iptables不同,如果链没有使用netfilter框架中的任何类型或钩子,则流经这些链的数据包不会被nftables触及。
链有两种类型。基本链是来自网络栈的数据包的入口点,其中指定了钩子值。常规链可以作为更好地处理的跳转目标。
在以下情况中,family
是可选的,如果未指定则设为ip
。
创建链
常规链
将名为chain
的常规链添加到名为table
的表中:
# nft add chain family table chain
例如,将名为tcpchain
的常规链添加到inet
簇中名为filter
的表中:
# nft add chain inet filter tcpchain
基本链
添加基本链,需要指定钩子和优先级值:
# nft add chain family table chain { type type hook hook priority priority \; }
type
可以是filter
、route
或者nat
。
IPv4/IPv6/Inet地址簇中,hook
可以是prerouting
、input
、forward
、output
或者postrouting
。其他地址簇中的钩子列表请参见nft(8)。
priority
采用整数值。数字较小的链优先处理,并且可以是负数。[3]
例如,添加筛选输入数据包的基本链:
# nft add chain inet filter input { type filter hook input priority 0\; }
将上面命令中的add
替换为create
则可以添加一个新的链,但如果链已经存在,则返回错误。
列出规则
列出一个链中的所有规则:
# nft list chain family table chain
例如,要列出inet
中filter
表的output
链中的所有规则:
# nft list chain inet filter output
编辑链
要编辑一个链,只需按名称调用并定义要更改的规则。
# nft chain family table chain { [ type type hook hook device device priority priority \; policy <policy> \; ] }
例如,将默认表中的input链策略从accept
更改为drop
:
# nft chain inet filter input { policy drop \; }
删除链
删除一个链:
# nft delete chain family table chain
要删除的链不能包含任何规则或者跳转目标。
清空链中的规则
清空一个链的规则:
# nft flush chain family table chain
规则(Rules)
规则由语句或表达式构成,包含在链中。
添加规则
提示: iptables-translate实用程序何以将iptables规则转换成nftables格式。
将一条规则添加到链中:
# nft add rule family table chain handle handle statement
规则添加到handle
处,这是可选的。如果不指定,则规则添加到链的末尾。
将规则插入到指定位置:
# nft insert rule family table chain handle handle statement
如果未指定handle
,则规则插入到链的开头。
表达式
通常情况下,statement
包含一些要匹配的表达式,然后是判断语句。结论语句包括accept
、drop
、queue
、continue
、return
、jump chain
和goto chain
。也可能是其他陈述。有关信息信息,请参阅nft(8)。
nftables中有多种可用的表达式,并且在大多数情况下,与iptables的对应项一致。最明显的区别是没有一般或隐式匹配。一般匹配是始终可用的匹配,如--protocol
或--source
。隐式匹配是用于特定协议的匹配,如TCP数据包的--sport
。
以下是可用匹配的部分列表:
- meta (元属性,如接口)
- icmp (ICMP协议)
- icmpv6 (ICMPv6协议)
- ip (IP协议)
- ip6 (IPv6协议)
- tcp (TCP协议)
- udp (UDP协议)
- sctp (SCTP协议)
- ct (链接跟踪)
以下是匹配参数的部分列表(完整列表请参见nft(8)):
meta:
oif <output interface INDEX>
iif <input interface INDEX>
oifname <output interface NAME>
iifname <input interface NAME>
(oif 和 iif 接受字符串参数并转换为接口索引)
(oifname 和 iifname 更具动态性,但因字符串匹配速度更慢)
icmp:
type <icmp type>
icmpv6:
type <icmpv6 type>
ip:
protocol <protocol>
daddr <destination address>
saddr <source address>
ip6:
daddr <destination address>
saddr <source address>
tcp:
dport <destination port>
sport <source port>
udp:
dport <destination port>
sport <source port>
sctp:
dport <destination port>
sport <source port>
ct:
state <new | established | related | invalid>
注意: nft不使用
/etc/services
文件匹配端口号和名称,而是使用内置列表。要在命令行显示端口映射,请使用nft describe tcp dport
。
删除
单个规则只能通过其句柄删除。使用nft --handle list
命令确定规则的句柄。请注意,--handle
开关告诉nft
输出句柄列表。
下面命令确定一个规则的句柄,然后删除。--number
参数用于查看数字输出,如未解析的IP地址。
# nft --handle --numeric list chain inet filter input
table ip fltrTable {
chain input {
type filter hook input priority 0;
ip saddr 127.0.0.1 accept # handle 10
}
}
# nft delete rule inet fltrTable input handle 10
可以用nft flush table
命令清空表中的所有的链。可以用nft flush chain
或者nft delete rule
命令清空单个链。
# nft flush table foo
# nft flush chain foo bar
# nft delete rule ip6 foo bar
第一个命令清空foo
表中的所有链。第二个命令清空ip foo
表中的bar
链。第三个命令删除ip6 foo
表bar
两种的所有规则。
自动重载
清空当前规则集:
# echo "flush ruleset" > /tmp/nftables
导出当前规则集:
# nft list ruleset >> /tmp/nftables
可以直接修改/tmp/nftables文件,使更改生效则运行:
# nft -f /tmp/nftables
简单可用的防火墙
详细信息参见 Simple stateful firewall 。
单一计算机
清空当前规则集:
# nft flush ruleset
添加一个表:
# nft add table inet filter
添加input、forward和output三个基本链。input和forward的默认策略是drop。output的默认策略是accept。
# nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
# nft add chain inet filter forward { type filter hook forward priority 0 \; policy drop \; }
# nft add chain inet filter output { type filter hook output priority 0 \; policy accept \; }
添加两个与TCP和UDP关联的常规链:
# nft add chain inet filter TCP
# nft add chain inet filter UDP
related和established的流量会accept:
# nft add rule inet filter input ct state related,established accept
loopback接口的流量会accept:
# nft add rule inet filter input iif lo accept
无效的流量会drop:
# nft add rule inet filter input ct state invalid drop
新的echo请求(ping)会accept:
# nft add rule inet filter input ip protocol icmp icmp type echo-request ct state new accept
新的UDP流量跳转到UDP链:
# nft add rule inet filter input ip protocol udp ct state new jump UDP
新的TCP流量跳转到TCP链:
# nft add rule inet filter input ip protocol tcp tcp flags \& \(fin\|syn\|rst\|ack\) == syn ct state new jump TCP
未由其他规则处理的所有通信会reject:
# nft add rule inet filter input ip protocol udp reject
# nft add rule inet filter input ip protocol tcp reject with tcp reset
# nft add rule inet filter input counter reject with icmp type prot-unreachable
此时,应决定对传入连接打开哪些端口,这些由TCP和UDP链处理。例如,要打开web服务器的连接端口,添加:
# nft add rule inet filter TCP tcp dport 80 accept
要打开web服务器HTTPS连接端口443:
# nft add rule inet filter TCP tcp dport 443 accept
允许SSH连接端口22:
# nft add rule inet filter TCP tcp dport 22 accept
允许传入DNS请求:
# nft add rule inet filter TCP tcp dport 53 accept
# nft add rule inet filter UDP udp dport 53 accept
确保更改是永久的。
~# nft list ruleset > /etc/nftables.conf
检查规则集
~# nft list ruleset
table inet filter {
chain forward {
type filter hook forward priority 0; policy drop;
}
chain TCP {
tcp dport http accept
tcp dport https accept
tcp dport ssh accept
tcp dport domain accept
}
chain UDP {
udp dport domain accept
}
chain output {
type filter hook output priority 0; policy accept;
}
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif "lo" accept
ct state invalid drop
ip protocol icmp icmp type echo-request ct state new accept
ip protocol udp ct state new jump UDP
ip protocol tcp tcp flags & (fin | syn | rst | ack) == syn ct state new jump TCP
ip protocol udp reject
ip protocol tcp reject with tcp reset
meta nfproto ipv4 counter packets 0 bytes 0 reject with icmp type prot-unreachable
}
}
从iptables迁移到nftables
翻译单个规则
~# iptables-translate -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
nft add rule ip filter INPUT tcp dport 22 ct state new counter accept
~# ip6tables-translate -A FORWARD -i eth0 -o eth3 -p udp -m multiport --dports 111,222 -j ACCEPT
nft add rule ip6 filter FORWARD iifname "eth0" oifname "eth3" meta l4proto udp udp dport { 111,222} counter accept
翻译规则集
~# iptables-save > iptables_save.txt
~# cat iptables_save.txt
# Generated by xtables-save v1.8.2 on Wed Feb 5 12:05:26 2020
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
COMMIT
# Completed on Wed Feb 5 12:05:26 2020
~# iptables-restore-translate -f iptables_save.txt
# Translated by iptables-restore-translate v1.8.2 on Wed Feb 5 12:05:38 2020
add table ip filter
add chain ip filter INPUT { type filter hook input priority 0; policy accept; }
add chain ip filter FORWARD { type filter hook forward priority 0; policy accept; }
add chain ip filter OUTPUT { type filter hook output priority 0; policy accept; }
add rule ip filter INPUT tcp dport 22 ct state new counter accept
# Completed on Wed Feb 5 12:05:38 2020
应用规则
~# iptables-restore-translate -f iptables_save.txt > ruleset.nft
~# nft -f ruleset.nft
~# nft list ruleset
table ip filter {
chain INPUT {
type filter hook input priority 0; policy accept;
meta l4proto tcp tcp dport 22 ct state new counter packets 0 bytes 0 accept
tcp dport ssh ct state new counter packets 0 bytes 0 accept
}
chain FORWARD {
type filter hook forward priority 0; policy accept;
}
chain OUTPUT {
type filter hook output priority 0; policy accept;
}
}
使用nf_tables compat后端
~# iptables-nft -A FORWARD -p icmp -j ACCEPT
~# iptables-nft-save
# Generated by xtables-save v1.8.2 on Wed Feb 5 12:12:04 2020
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A FORWARD -p icmp -j ACCEPT
COMMIT
# Completed on Wed Feb 5 12:12:04 2020
~# nft list ruleset
table ip filter {
chain INPUT {
type filter hook input priority 0; policy accept;
}
chain FORWARD {
type filter hook forward priority 0; policy accept;
meta l4proto icmp counter packets 0 bytes 0 accept
}
chain OUTPUT {
type filter hook output priority 0; policy accept;
}
}
使用iptables-nft
加载iptable的规则集
~# iptables-save > iptables.txt
~# iptables-nft-restore < iptables.txt
~# iptables-nft-save
# Generated by xtables-save v1.8.2 on Wed Feb 5 12:14:45 2020
*filter
:INPUT ACCEPT [6:364]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [4:352]
-A FORWARD -p icmp -j ACCEPT
COMMIT
# Completed on Wed Feb 5 12:14:45 2020
~# nft list ruleset
table ip filter {
chain INPUT {
type filter hook input priority 0; policy accept;
}
chain FORWARD {
type filter hook forward priority 0; policy accept;
meta l4proto icmp counter packets 0 bytes 0 accept
}
chain OUTPUT {
type filter hook output priority 0; policy accept;
}
}