# nftables 简体中文

#### 博客作者：联系请[点击](https://k8sadmin.info/lian-xi-zuo-zhe)，搬运不易，希望请作者喝咖啡，可以点击[联系博客作者](https://k8sadmin.info/lian-xi-zuo-zhe)

**翻译状态：** 本文是英文页面 [Nftables](https://wiki.archlinux.org/index.php/Nftables) 的[翻译](https://wiki.archlinux.org/index.php/ArchWiki_Translation_Team_\(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87\))，最后翻译时间：2019-04-17，点击[这里](https://wiki.archlinux.org/index.php?title=Nftables\&diff=0\&oldid=571364)可以查看翻译后英文页面的改动。

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

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

您还可以访问[nftables官方wiki页面](https://wiki.nftables.org/wiki-nftables/index.php/Main_Page)获取更多信息。

## 安装

[安装](https://wiki.archlinux.org/index.php/%E5%AE%89%E8%A3%85)用户空间实用程序包[nftables](https://www.archlinux.org/packages/?name=nftables)或者git版本[nftables-git](https://aur.archlinux.org/packages/nftables-git/)AUR。

## 用法

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

[start/enable](https://wiki.archlinux.org/index.php/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](https://unix.stackexchange.com/questions/408497/nftables-configuration-error-conflicting-protocols-specified-inet-service-v-i?rq=1)。

要从文件读取输入，请使用`-f`参数：

```
# nft -f filename
```

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

有关命令的完整列表，参见[nft(8)](https://jlk.fjfi.cvut.cz/arch/manpages/man/nft.8)。

### 表（Tables）

表包含[#链](https://wiki.archlinux.org/index.php/Nftables_\(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87\)#%E9%93%BE)\[[broken link](https://wiki.archlinux.org/index.php/ArchWiki:Requests#Broken_section_links): invalid section]。与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](http://www.spinics.net/lists/netfilter/msg56411.html)）

有关地址簇的完整描述，请参见[nft(8)](https://jlk.fjfi.cvut.cz/arch/manpages/man/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）

链的目的是保存[#规则](https://wiki.archlinux.org/index.php/Nftables_\(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87\)#%E8%A7%84%E5%88%99)\[[broken link](https://wiki.archlinux.org/index.php/ArchWiki:Requests#Broken_section_links): invalid section]。与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)](https://jlk.fjfi.cvut.cz/arch/manpages/man/nft.8)。

`priority`采用整数值。数字较小的链优先处理，并且可以是负数。[\[3\]](https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_types)

例如，添加筛选输入数据包的基本链：

```
# 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](https://wiki.archlinux.org/index.php/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)](https://jlk.fjfi.cvut.cz/arch/manpages/man/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)](https://jlk.fjfi.cvut.cz/arch/manpages/man/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`文件匹配端口号和名称，而是使用[内置列表](https://git.netfilter.org/nftables/plain/src/services.c)。要在命令行显示端口映射，请使用 `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
```

## 配置示例

### 工作站

```
/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地址改变时更新规则。](http://wiki.nftables.org/wiki-ip%E5%9C%B0%E5%9D%80%E6%94%B9%E5%8F%98%E6%97%B6%E6%9B%B4%E6%96%B0%E8%A7%84%E5%88%99%E3%80%82)

使用方法：

* 确保在内核中启用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](https://wiki.archlinux.org/index.php/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](https://wiki.archlinux.org/index.php/Sshguard)是可以检测暴力攻击的程序，并根据临时黑名单IP地址修改防火墙。有关如何与nftables一起使用的问题，请参阅[Sshguard#nftables](https://wiki.archlinux.org/index.php/Sshguard#nftables)。

## 参考

* [netfilter nftables wiki](https://wiki.nftables.org/)
* [debian:nftables](https://wiki.debian.org/nftables)
* [gentoo:nftables](https://wiki.gentoo.org/wiki/nftables)
* [First release of nftables](https://lwn.net/Articles/324251/)
* [nftables quick howto](https://home.regit.org/netfilter-en/nftables-quick-howto/)
* [The return of nftables](https://lwn.net/Articles/564095/)
* [What comes after ‘iptables’? Its successor, of course: \`nftables\`](http://developers.redhat.com/blog/2016/10/28/what-comes-after-iptables-its-successor-of-course-nftables/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hezhiqiang8909.gitbook.io/nftables/master.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
