nftables 简体中文

博客作者:联系请点击,搬运不易,希望请作者喝咖啡,可以点击联系博客作者

翻译状态: 本文是英文页面 Nftables翻译,最后翻译时间:2019-04-17,点击这里可以查看翻译后英文页面的改动。
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。

用法

nftables区分命令行输入的临时规则和从文件加载或保存到文件的永久规则。 默认配置文件是/etc/nftables.conf,其中已经包含一个名为"inet filter"的简单ipv4/ipv6防火墙列表。
start/enable nftables.service
检查规则集:
1
# nft list ruleset
Copied!
注意: 要使systemd服务正确运行,可能需要创建包含nftables所有相关模块的/etc/modules-load.d/nftables.conf文件。可以用以下命令获取模块列表:
1
$ lsmod | grep '^nf'
Copied!
否则,可能会出现错误: Error: Could not process rule: No such file or directory

配置

nftables的用户空间实用程序nft评估大多数规则集并传递到内核。规则存储在链中,链存储在表中。以下部分说明如何创建和修改这些结构。
以下所有更改都是临时的。要永久更改,请见规则集保存到nftables.service加载的/etc/nftables.conf中:
1
# nft list ruleset > /etc/nftables.conf
Copied!
注意:
  • nft list不输出变量的定义,如果在/etc/nftables.conf中有任何变量定义将会丢失。规则中使用的变量将替换为其变量值。
  • nft list ruleset在nftables v0.7 (Scrooge McDuck)在同时使用ICMP和ICMPv6时有语法限制。如果导出用作新的规则集将会导致错误。有关信息和解决方案,请参阅 stackexchange post
要从文件读取输入,请使用-f参数:
1
# nft -f filename
Copied!
注意,任何已加载的规则不会自动清空。
有关命令的完整列表,参见nft(8)

表(Tables)

表包含#链[broken link: invalid section]。与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

创建表

创建一个新的表:
1
# nft add table family table
Copied!

列出表

列出所有表:
1
# nft list tables
Copied!

列出表中的链和规则

列出指定表中的所有链和规则:
1
# nft list table family table
Copied!
例如,要列出inet簇中filter表中的所有规则:
1
# nft list table inet filter
Copied!

删除表

删除一个表:
1
# nft delete table family table
Copied!
只能删除不包含链的表。

清空表

要清空一个表中的所有规则:
1
# nft flush table family table
Copied!

链(Chains)

链的目的是保存#规则[broken link: invalid section]。与iptables中的链不同,nftables没有内置链。这意味着与iptables不同,如果链没有使用netfilter框架中的任何类型或钩子,则流经这些链的数据包不会被nftables触及。
链有两种类型。基本链是来自网络栈的数据包的入口点,其中指定了钩子值。常规链可以作为更好地处理的跳转目标。
在以下情况中,family是可选的,如果未指定则设为ip

创建链

常规链
将名为chain的常规链添加到名为table的表中:
1
# nft add chain family table chain
Copied!
例如,将名为tcpchain的常规链添加到inet簇中名为filter的表中:
1
# nft add chain inet filter tcpchain
Copied!
基本链
添加基本链,需要指定钩子和优先级值:
1
# nft add chain family table chain { type type hook hook priority priority \; }
Copied!
type可以是filterroute或者nat
IPv4/IPv6/Inet地址簇中,hook可以是preroutinginputforwardoutput或者postrouting。其他地址簇中的钩子列表请参见nft(8)
priority采用整数值。数字较小的链优先处理,并且可以是负数。[3]
例如,添加筛选输入数据包的基本链:
1
# nft add chain inet filter input { type filter hook input priority 0\; }
Copied!
将上面命令中的add替换为create则可以添加一个新的链,但如果链已经存在,则返回错误。

列出规则

列出一个链中的所有规则:
1
# nft list chain family table chain
Copied!
例如,要列出inetfilter表的output链中的所有规则:
1
# nft list chain inet filter output
Copied!

编辑链

要编辑一个链,只需按名称调用并定义要更改的规则。
1
# nft chain family table chain { [ type type hook hook device device priority priority \; policy <policy> \; ] }
Copied!
例如,将默认表中的input链策略从accept更改为drop
1
# nft chain inet filter input { policy drop \; }
Copied!

删除链

删除一个链:
1
# nft delete chain family table chain
Copied!
要删除的链不能包含任何规则或者跳转目标。

清空链中的规则

清空一个链的规则:
1
# nft flush chain family table chain
Copied!

规则(Rules)

规则由语句或表达式构成,包含在链中。

添加规则

提示: iptables-translate实用程序何以将iptables规则转换成nftables格式。
将一条规则添加到链中:
1
# nft add rule family table chain handle handle statement
Copied!
规则添加到handle处,这是可选的。如果不指定,则规则添加到链的末尾。
将规则插入到指定位置:
1
# nft insert rule family table chain handle handle statement
Copied!
如果未指定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)):
1
meta:
2
oif <output interface INDEX>
3
iif <input interface INDEX>
4
oifname <output interface NAME>
5
iifname <input interface NAME>
6
7
(oif 和 iif 接受字符串参数并转换为接口索引)
8
(oifname 和 iifname 更具动态性,但因字符串匹配速度更慢)
9
10
icmp:
11
type <icmp type>
12
13
icmpv6:
14
type <icmpv6 type>
15
16
ip:
17
protocol <protocol>
18
daddr <destination address>
19
saddr <source address>
20
21
ip6:
22
daddr <destination address>
23
saddr <source address>
24
25
tcp:
26
dport <destination port>
27
sport <source port>
28
29
udp:
30
dport <destination port>
31
sport <source port>
32
33
sctp:
34
dport <destination port>
35
sport <source port>
36
37
ct:
38
state <new | established | related | invalid>
Copied!
注意: nft不使用/etc/services文件匹配端口号和名称,而是使用内置列表。要在命令行显示端口映射,请使用 nft describe tcp dport

删除

单个规则只能通过其句柄删除。使用nft --handle list命令确定规则的句柄。请注意,--handle开关告诉nft输出句柄列表。
下面命令确定一个规则的句柄,然后删除。--number参数用于查看数字输出,如未解析的IP地址。
1
# nft --handle --numeric list chain inet filter input
Copied!
1
table ip fltrTable {
2
chain input {
3
type filter hook input priority 0;
4
ip saddr 127.0.0.1 accept # handle 10
5
}
6
}
Copied!
1
# nft delete rule inet fltrTable input handle 10
Copied!
可以用nft flush table命令清空表中的所有的链。可以用nft flush chain或者nft delete rule命令清空单个链。
1
# nft flush table foo
2
# nft flush chain foo bar
3
# nft delete rule ip6 foo bar
Copied!
第一个命令清空foo表中的所有链。第二个命令清空ip foo表中的bar链。第三个命令删除ip6 foobar两种的所有规则。

自动重载

清空当前规则集:
1
# echo "flush ruleset" > /tmp/nftables
Copied!
导出当前规则集:
1
# nft list ruleset >> /tmp/nftables
Copied!
可以直接修改/tmp/nftables文件,使更改生效则运行:
1
# nft -f /tmp/nftables
Copied!

配置示例

工作站

1
/etc/nftables.conf
Copied!
1
flush ruleset
2
3
table inet filter {
4
chain input {
5
type filter hook input priority 0;
6
7
# accept any localhost traffic
8
iif lo accept
9
10
# accept traffic originated from us
11
ct state established,related accept
12
13
# accept ICMP & IGMP
14
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
15
ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept
16
ip protocol igmp accept
17
18
# activate the following line to accept common local services
19
#tcp dport { 22, 80, 443 } ct state new accept
20
21
# count and drop any other traffic
22
counter drop
23
}
24
}
Copied!

简单的IPv4/IPv6防火墙

1
firewall.rules
Copied!
1
# A simple firewall
2
3
flush ruleset
4
5
table inet filter {
6
chain input {
7
type filter hook input priority 0; policy drop;
8
9
# established/related connections
10
ct state established,related accept
11
12
# invalid connections
13
ct state invalid drop
14
15
# loopback interface
16
iif lo accept
17
18
# ICMP & IGMP
19
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
20
ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept
21
ip protocol igmp accept
22
23
# SSH (port 22)
24
tcp dport ssh accept
25
26
# HTTP (ports 80 & 443)
27
tcp dport { http, https } accept
28
}
29
30
chain forward {
31
type filter hook forward priority 0; policy drop;
32
}
33
34
chain output {
35
type filter hook output priority 0; policy accept;
36
}
37
38
}
Copied!

IPv4/IPv6防火墙限流

1
firewall.2.rules
Copied!
1
table inet filter {
2
chain input {
3
type filter hook input priority 0; policy drop;
4
5
ct state invalid drop
6
7
iif lo accept
8
9
# no ping floods:
10
ip protocol icmp icmp type echo-request limit rate over 10/second burst 4 packets drop
11
ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate over 10/second burst 4 packets drop
12
13
ct state established,related accept
14
15
# ICMP & IGMP
16
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
17
ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept
18
ip protocol igmp accept
19
20
# avoid brute force on ssh:
21
tcp dport ssh ct state new limit rate 15/minute accept
22
23
}
24
25
chain forward {
26
type filter hook forward priority 0; policy drop;
27
}
28
29
chain output {
30
type filter hook output priority 0; policy accept;
31
}
32
33
}
Copied!

跳转

在配置文件中使用跳转时,必须确保已经定义目标链。否则会引起错误Error: Could not process rule: No such file or directory
1
jump.rules
Copied!
1
table inet filter {
2
chain web {
3
tcp dport http accept
4
tcp dport 8080 accept
5
}
6
chain input {
7
type filter hook input priority 0;
8
ip saddr 10.0.2.0/24 jump web
9
drop
10
}
11
}
Copied!

不同接口采用不同规则

如果设备有多个网络接口,并且需要对不同接口采用不同的规则,则可能需要使用“调度”筛选链,然后使用特定接口筛选链。例如,假设设备用作家庭路由器,你需要在LAN(接口enp3s0)运行一个web服务器,但不用于internet(接口enp2s0),可以考虑以下的结构:
1
table inet filter {
2
chain input { # this chain serves as a dispatcher
3
type filter hook input priority 0;
4
5
iif lo accept # always accept loopback
6
iifname enp2s0 jump input_enp2s0
7
iifname enp3s0 jump input_enp3s0
8
9
reject with icmp type port-unreachable # refuse traffic from all other interfaces
10
}
11
chain input_enp2s0 { # rules applicable to public interface interface
12
ct state {established,related} accept
13
ct state invalid drop
14
udp dport bootpc accept
15
tcp dport bootpc accept
16
reject with icmp type port-unreachable # all other traffic
17
}
18
chain input_enp3s0 {
19
ct state {established,related} accept
20
ct state invalid drop
21
udp dport bootpc accept
22
tcp dport bootpc accept
23
tcp port http accept
24
tcp port https accept
25
reject with icmp type port-unreachable # all other traffic
26
}
27
chain ouput { # we let everything out
28
type filter hook output priority 0;
29
accept
30
}
31
}
Copied!
或者,可以只选择一个iifname语句,如单个上游接口,并将其他接口的默认规则放在一起,而不是为每个接口进行调度。

Masquerading

nftables有一个特殊的关键字masquerade "where the source address is automagically set to the address of the output interface(源地址自动设置为出口地址)" ([http://wiki.nftables.org/wiki-ip地址改变时更新规则。
使用方法:
  • 确保在内核中启用Masquerading(默认内核已启用),否则在内核配置过程中设置
1
CONFIG_NFT_MASQ=m
Copied!
  • masquerade关键字只能用于nat类型的链,而nat链又不能在inet簇的表中。要用ip和/或ip6簇的表。
  • masquerading是一中源NAT,只能工作于输出路径。
具有两个接口的计算机的示例:LAN连接到nsp3s0,internet连接到enp2s0
1
table ip nat {
2
chain prerouting {
3
type nat hook prerouting priority 0;
4
}
5
chain postrouting {
6
type nat hook postrouting priority 100;
7
oifname "enp0s2" masquerade
8
}
9
}
Copied!

提示和技巧

简单可用的防火墙

详细信息参见 Simple stateful firewall

单一计算机

清空当前规则集:
1
# nft flush ruleset
Copied!
添加一个表:
1
# nft add table inet filter
Copied!
添加input、forward和output三个基本链。input和forward的默认策略是drop。output的默认策略是accept。
1
# nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
2
# nft add chain inet filter forward { type filter hook forward priority 0 \; policy drop \; }
3
# nft add chain inet filter output { type filter hook output priority 0 \; policy accept \; }
Copied!
添加两个与TCP和UDP关联的常规链:
1
# nft add chain inet filter TCP
2
# nft add chain inet filter UDP
Copied!
related和established的流量会accept:
1
# nft add rule inet filter input ct state related,established accept
Copied!
loopback接口的流量会accept:
1
# nft add rule inet filter input iif lo accept
Copied!
无效的流量会drop:
1
# nft add rule inet filter input ct state invalid drop
Copied!
新的echo请求(ping)会accept:
1
# nft add rule inet filter input ip protocol icmp icmp type echo-request ct state new accept
Copied!
新的UDP流量跳转到UDP链:
1
# nft add rule inet filter input ip protocol udp ct state new jump UDP
Copied!
新的TCP流量跳转到TCP链:
1
# nft add rule inet filter input ip protocol tcp tcp flags \& \(fin\|syn\|rst\|ack\) == syn ct state new jump TCP
Copied!
未由其他规则处理的所有通信会reject:
1
# nft add rule inet filter input ip protocol udp reject
2
# nft add rule inet filter input ip protocol tcp reject with tcp reset
3
# nft add rule inet filter input counter reject with icmp type prot-unreachable
Copied!
此时,应决定对传入连接打开哪些端口,这些由TCP和UDP链处理。例如,要打开web服务器的连接端口,添加:
1
# nft add rule inet filter TCP tcp dport 80 accept
Copied!
要打开web服务器HTTPS连接端口443:
1
# nft add rule inet filter TCP tcp dport 443 accept
Copied!
允许SSH连接端口22:
1
# nft add rule inet filter TCP tcp dport 22 accept
Copied!
允许传入DNS请求:
1
# nft add rule inet filter TCP tcp dport 53 accept
2
# nft add rule inet filter UDP udp dport 53 accept
Copied!
确保更改是永久的。

防止暴力攻击

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

参考

Last modified 2yr ago