nftables (简体中文)

From ArchWiki
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
翻译状态:本文是 Nftables翻译。上次翻译日期:2019-04-17。如果英文版本有所更改,则您可以帮助同步翻译。

Tango-preferences-desktop-locale-modified.png这篇文章或章节的翻译不反映原文。Tango-preferences-desktop-locale-modified.png

原因:Last updated in 2019 (571555), out of sync with English page(在 Talk:Nftables (简体中文)# 中讨论)

nftables 是一个netfilter项目,旨在替换现有的{ip,ip6,arp,eb}tables框架,为{ip,ip6}tables提供一个新的包过滤框架、一个新的用户空间实用程序(nft)和一个兼容层。它使用现有的钩子、链接跟踪系统、用户空间排队组件和netfilter日志子系统。

它由三个主要组件组成:内核实现、libnl netlink通信和nftables用户空间前端。 内核提供了一个netlink配置接口以及运行时规则集评估,libnl包含了与内核通信的基本函数,nftables前端是用户通过nft交互。

您还可以访问nftables官方wiki页面获取更多信息。

安装

安装用户空间实用程序包nftables或者git版本nftables-gitAUR

提示: 大部分iptables前端没有直接或间接支持nftables,但可以导入到其中。[1] firewalld是一个同时支持nftables和iptables的图形前端。[2]

用法

nftables区分命令行输入的临时规则和从文件加载或保存到文件的永久规则。 默认配置文件是/etc/nftables.conf,其中已经包含一个名为"inet filter"的简单ipv4/ipv6防火墙列表。

start/enable nftables.service

检查规则集:

# nft list ruleset
注意: 要使systemd服务正确运行,可能需要创建包含nftables所有相关模块的/etc/modules-load.d/nftables.conf文件。可以用以下命令获取模块列表:
$ lsmod | grep '^nf'
否则,可能会出现错误: Error: Could not process rule: No such file or directory

配置

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

注意,任何已加载的规则不会自动清空。

有关命令的完整列表,参见nft(8)

表(Tables)

表包含#链[损坏的链接:无效的章节]。与iptables中的表不同,nftables中没有内置表。表的数量和名称由用户决定。但是,每个表只有一个地址簇,并且只适用于该簇的数据包。表可以指定五个簇中的一个:

nftables簇 iptables实用程序
ip iptables
ip6 ip6tables
inet iptables和ip6tables
arp arptables
bridge ebtables

ip(即IPv4)是默认簇,如果未指定簇,则使用该簇。

要创建同时适用于IPv4和IPv6的规则,请使用inetinet允许统一ipip6簇,以便更容易地定义规则。

注意: 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可以是filterroute或者nat

IPv4/IPv6/Inet地址簇中,hook可以是preroutinginputforwardoutput或者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

例如,要列出inetfilter表的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包含一些要匹配的表达式,然后是判断语句。结论语句包括acceptdropqueuecontinuereturnjump chaingoto 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 foobar两种的所有规则。

自动重载

清空当前规则集:

# echo "flush ruleset" > /tmp/nftables 

导出当前规则集:

# nft list ruleset >> /tmp/nftables

可以直接修改/tmp/nftables文件,使更改生效则运行:

# nft -f /tmp/nftables

配置示例

工作站

/etc/nftables.conf
flush ruleset

table inet filter {
        chain input {
                type filter hook input priority 0;

                # accept any localhost traffic
                iif lo accept

                # accept traffic originated from us
                ct state established,related accept

		# accept ICMP & IGMP
		ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept
		ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept
		ip protocol igmp accept

                # activate the following line to accept common local services
                #tcp dport { 22, 80, 443 } ct state new accept

                # count and drop any other traffic
                counter drop
        }
}

简单的IPv4/IPv6防火墙

firewall.rules
# A simple firewall

flush ruleset

table inet filter {
	chain input {
		type filter hook input priority 0; policy drop;

		# established/related connections
		ct state established,related accept

		# invalid connections
		ct state invalid drop
		
		# loopback interface
		iif lo accept

		# ICMP & IGMP
		ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept
		ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept
		ip protocol igmp accept

		# SSH (port 22)
		tcp dport ssh accept

		# HTTP (ports 80 & 443)
		tcp dport { http, https } accept
	}

	chain forward {
		type filter hook forward priority 0; policy drop;
	}

	chain output {
		type filter hook output priority 0; policy accept;
	}

}

IPv4/IPv6防火墙限流

firewall.2.rules
table inet filter {
	chain input {
		type filter hook input priority 0; policy drop;

		ct state invalid drop

		iif lo accept

		# no ping floods:
		ip protocol icmp icmp type echo-request limit rate over 10/second burst 4 packets  drop
		ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate over 10/second burst 4 packets drop

		ct state established,related accept

		# ICMP & IGMP
		ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept
		ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept
		ip protocol igmp accept

		# avoid brute force on ssh:
		tcp dport ssh ct state new limit rate 15/minute accept

	}

	chain forward {
		type filter hook forward priority 0; policy drop;
	}

	chain output {
		type filter hook output priority 0; policy accept;
	}

}

跳转

在配置文件中使用跳转时,必须确保已经定义目标链。否则会引起错误Error: Could not process rule: No such file or directory

jump.rules
table inet filter {
    chain web {
        tcp dport http accept
        tcp dport 8080 accept
    }
    chain input {
        type filter hook input priority 0;
        ip saddr 10.0.2.0/24 jump web
        drop
    }
}

不同接口采用不同规则

如果设备有多个网络接口,并且需要对不同接口采用不同的规则,则可能需要使用“调度”筛选链,然后使用特定接口筛选链。例如,假设设备用作家庭路由器,你需要在LAN(接口enp3s0)运行一个web服务器,但不用于internet(接口enp2s0),可以考虑以下的结构:

table inet filter {
  chain input { # this chain serves as a dispatcher
    type filter hook input priority 0;

    iif lo accept # always accept loopback
    iifname enp2s0 jump input_enp2s0
    iifname enp3s0 jump input_enp3s0

    reject with icmp type port-unreachable # refuse traffic from all other interfaces
  }
  chain input_enp2s0 { # rules applicable to public interface interface
    ct state {established,related} accept
    ct state invalid drop
    udp dport bootpc accept
    tcp dport bootpc accept
    reject with icmp type port-unreachable # all other traffic
  }
  chain input_enp3s0 {
    ct state {established,related} accept
    ct state invalid drop
    udp dport bootpc accept
    tcp dport bootpc accept
    tcp port http accept
    tcp port https accept
    reject with icmp type port-unreachable # all other traffic
  }
  chain ouput { # we let everything out
    type filter hook output priority 0;
    accept
  }
 }

或者,可以只选择一个iifname语句,如单个上游接口,并将其他接口的默认规则放在一起,而不是为每个接口进行调度。

Masquerading

nftables有一个特殊的关键字masquerade "where the source address is automagically set to the address of the output interface(源地址自动设置为出口地址)" ([http://wiki.nftables.org/wiki-ip地址改变时更新规则。[失效链接 2020-08-04 ⓘ]

使用方法:

  • 确保在内核中启用Masquerading(默认内核已启用),否则在内核配置过程中设置
CONFIG_NFT_MASQ=m
  • masquerade关键字只能用于nat类型的链,而nat链又不能在inet簇的表中。要用ip和/或ip6簇的表。
  • masquerading是一中源NAT,只能工作于输出路径。

具有两个接口的计算机的示例:LAN连接到nsp3s0,internet连接到enp2s0

table ip nat {
  chain prerouting {
    type nat hook prerouting priority 0;
  }
  chain postrouting {
    type nat hook postrouting priority 100;
    oifname "enp0s2" masquerade
  }
}

提示和技巧

简单可用的防火墙

详细信息参见 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

确保更改是永久的。

防止暴力攻击

Sshguard是可以检测暴力攻击的程序,并根据临时黑名单IP地址修改防火墙。有关如何与nftables一起使用的问题,请参阅Sshguard#nftables

参考