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

检查规则集:

# 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)

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

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)

链的目的是保存#规则[broken link: 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可以是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地址改变时更新规则。

使用方法:

  • 确保在内核中启用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

参考

Last updated