简介

nftables开发的目的是为了替换iptables,实现的功能与iptables基本一致。像firewalld这种工具有点类似于图形界面软件的功能,而实际调用的是iptables或nftables(今后估计底层会使用nftables,而不再使用iptables)。更加具体的简介可以看nftables简介

nftables链流向

用户端比较重要的几点如下:

  • firewalld这种工具增加了系统的消耗,而且更加精细的控制是难以实现的,简单功能的服务器或个人PC可以使用firewalld
  • iptables有默认的表、链、规则,而nftables都没有(但nftables同样采用表、链、规则的分级方式)
  • nftables.service只是为了重载/etc/nftables.conf配置而已,并不是一个一直在运行的进程
  • firewalld、nftables、iptables这三个同时只要去使用一个,三个的任意两个同时使用都可能造成规则的混乱

安装(以Debian为例)

# 安装nftables
apt install nftables

# 删除iptables
apt purge iptables

# 开机启动nftables重载/etc/nftables.conf中的规则
systemctl enable nftables

# 重载/etc/nftables.conf中的规则
# 启动nftables服务后会有一个默认的"inet filter"防火墙列表
systemctl start nftables

配置

基本命令

# 查看规则集
nft list ruleset

# 清除规则集(类似iptables -F)
nft flush ruleset

# 从文件中读取规则(可以看下nftables.service执行的就是读取文件规则而已)
# 注意:任何已经加载的规则不会自动清空
nft -f filename

# 永久保存规则(就是将当前的临时规则写入到/etc/nftables.conf文件中)
nft list ruleset > /etc/nftables.conf

# 重载/etc/nftables.conf中的规则
systemctl restart nftables

表(Tables)

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

nftables簇 iptables实用程序
ip iptables
ip6 ip6tables
inet iptables和ip6tables
arp arptables
bridge ebtables
  • ip(即IPv4)是默认簇,如果未指定簇,则使用该簇
  • 要创建同时适用于IPv4和IPv6的规则:
    • 请使用inet
    • inet簇不能用于nat类型的链,只能用于filter类型的链
# 创建一个名为my_table的表(指定采用inet簇)
nft add table inet my_table

# 查看所有的表
nft list tables

# 查看inet簇中my_table表的所有规则
nft list table inet my_table

# 查看ip簇中my_table表的所有规则(同样默认是ip簇)
nft list table my_table

# 删除ip簇的名为my_table的表
nft delete table my_table

# 删除inet簇的名为my_table的表
nft delete table inet my_table

# 清空inet簇my_table中的所有规则
nft flush table inet my_table

链(Chains)

链的目的是保存规则。与iptables中的链不同,nftables没有内置链。这意味着与iptables不同,如果链没有使用netfilter框架中的任何类型或钩子,则流经这些链的数据包不会被nftables触及。

  • 链有两种类型:
    • 常规链:不需要指定钩子类型和优先级,可以用来做跳转,从逻辑上对规则进行分类
    • 基本链:数据包的入口点,需要指定钩子类型和优先级(基本链是重点
# 在inet簇的my_table表中添加my_chain的常规链
nft add chain inet my_table my_chain

# 在inet簇的my_table表中添加my_filter_chain的基本链
# - 反斜线(\)用来转义,这样shell就不会将分号解释为命令的结尾
# - type可以是filter、route或者nat
# - IPv4/IPv6/Inet地址簇中,hook可以是prerouting、input、forward、output或者postrouting
# - priority 采用整数值,可以是负数,值较小的链优先处理
# - 默认的policy为accept
nft add chain inet my_table my_filter_chain { type filter hook input priority 0 \; }

# 列出"inet簇my_table表" -> "my_filter_chain链"中所有的规则
nft list chain inet my_table my_filter_chain

# 修改链:将my_filter_chain的策略从accept改为drop
# 注意:这里改为drop会导致ssh都断掉(所以不要瞎试验)
nft chain inet my_table my_filter_chain { policy drop \;}

# 删除inet簇的my_table表中的my_chain链
# 要删除的链不能包含任何规则或者跳转目标
nft delete chain inet my_table my_chain

# 清空链中的规则
nft flush chain inet my_table my_filter_chain

规则(Rules)

有了表和链之后,就可以创建规则了,规则由语句或表达式构成,包含在链中。

  • nftables添加规则的命令为nft add rule family_name table_name chain_name handle handle_index statement

    • add:表示添加规则到链的最后,可以使用insert添加到链的开头

    • family_name:簇的名称(如ip、inet等,详见

    • table_name:表的名称

    • chain_name:链的名称

    • handle:链中的每条规则都有固定的句柄(handle值)(可以用nft --handle list ruleset查看),指定handle_index就会将规则加入到handle_index之后(add)或之前(insert);也可以用index指令指定规则插入的位置,index就是简单的按规则列出的顺序排列(index值从0开始)。一般建议采用handle形式。

    • statement:通常情况下,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 (链接跟踪)

      • 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>
        
# 添加ssh端口访问
nft add rule inet my_table my_filter_chain tcp dport ssh accept

# 添加http端口访问
nft add rule inet my_table my_filter_chain tcp dport http accept

# 添加8080端口访问到链的最前
nft insert rule inet my_table my_filter_chain tcp dport 8080 accept

# 添加9090端口访问到index 1之前
nft insert rule inet my_table my_filter_chain index 1 tcp dport 9090 accept

# 接受related和established的流量(比如已经建立的连接)
nft add rule inet filter input ct state related,established accept

# 删除9090端口访问规则
# - 删除单条规则只能通过句柄(handle值)
# - 删除整条链的规则参见清空链
# - 查看某条链的handle值可以使用"nft --handle list chain family_name table_name chain_name"
nft delete rule inet my_table my_filter_chain handle 7

集合、字典、命名空间

nftable原生支持集合、字典,可以比较方便的制定规则,具体详见标题连接。

实战-简单防火墙

防火墙测试前

开始设置防火墙之前最好在cron中添加如下项(避免防火墙设置错误导致自己被关在门外,防火墙设置成功后可关闭):

# 每5分钟清空一次防火墙规则(注意不同发行版nft所在位置不一样)
*/5 * * * * /usr/sbin/nft flush ruleset

防火墙设置

如下是简单防火墙的设置脚本:

# 清空当前规则集
nft flush ruleset

# 添加一个表FILTER
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

# 添加服务器服务需要打开的端口:http、https、ssh、DNS等
nft add rule inet FILTER TCP tcp dport 80 accept
nft add rule inet FILTER TCP tcp dport 443 accept
nft add rule inet FILTER TCP tcp dport 22 accept
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

开机启动

建议开机启动采用systemd的nftables.service,但是配置文件/etc/nftables.conf可以按如下编辑:

#!/usr/sbin/nft -f

flush ruleset

include "/etc/nftables.d/*.nft"

然后每一类用脚本(如/etc/nftables.d/basefirewall.sh):

# 清空当前规则集
nft flush ruleset

# ...................
# 定义防火墙的规则......
# ...................

# 保存规则
nft list ruleset > /etc/nftables.d/basefirewall.nft

修改任一类规则后重启防火墙即可systemctl restart nftables

其他

内网转发

# 打开内核ipv4和ipv6网络转发
echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf && sysctl -p

# 新建一个NAT表
nft add table NAT

# 在NAT表中新建一个POSTROUTING链
# 这个链就算没有任何规则也必须建立
nft -- add chain NAT PREROUTING { type nat hook prerouting priority -100 \; }

# 在NAT表中新建一个POSTROUTING链
nft add chain NAT POSTROUTING { type nat hook postrouting priority 100 \; }

# 添加转发的路由规则
nft add rule NAT POSTROUTING masquerade
  • 转发路由规则还可以做如下限制:

    • 指定网卡:nft add rule NAT POSTROUTING oifname "ens160" masquerade
    • 指定来源IP:nft add rule NAT POSTROUTING ip saddr 10.0.0.0/8 masquerade
  • 注意:使用masquerade需要将forward链设置为accept,如果forward链一定要设置为drop则需要添加如下规则():

    # 用于masquerade的FORWARD规则
    # 此处假设FORWARD链在FILTER表中
    # 最后一行允许的IP范围按自身实际
    nft add rule inet FILTER FORWARD ct state related,established accept
    nft add rule inet FILTER FORWARD iif lo accept
    nft add rule inet FILTER FORWARD ct state invalid drop
    nft add rule inet FILTER FORWARD ip daddr 192.168.50.0/24 accept
    

从iptables迁移到nftables

iptables1.8.x以后的版本中(apt install iptables)有个工具可以翻译iptables的规则为nftables规则:

  • 翻译单条规则
# 如下命令会输出:
iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
# nft add rule ip filter INPUT tcp dport 22 counter accept
  • 翻译规则集
# 导出iptables规则
iptables-save > iptables_save.txt

# 翻译规则集并导出到ruleset.nft文件
iptables-restore-translate -f iptables_save.txt > ruleset.nft

# 应用规则
nft -f ruleset.nft
  • iptables-nft加载iptables的规则集
# 导出iptables规则
iptables-save > iptables_save.txt

# 将iptables的规则集导入到nftables中
iptables-nft-restore < iptables.txt

# 应用规则集
iptables-nft-save

参考