跳转至内容

nftables

来自 ArchWiki

nftables 是一个 Netfilter 项目,旨在取代现有的 {ip,ip6,arp,eb}tables 框架。它提供了一个新的包过滤框架、一个新的用户空间工具 (nft) 以及一个针对 {ip,ip6}tables 的兼容层。它利用了 netfilter 现有的钩子 (hooks)、连接追踪系统、用户空间排队组件和日志记录子系统。

它由三个主要部分组成:内核实现、libnl netlink 通信库和 nftables 用户空间前端。内核提供了 netlink 配置接口以及运行时的规则集评估;libnl 包含了与内核通信的底层函数;而 nftables 前端则是用户通过 nft 命令与之交互的界面。

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

安装

安装用户空间工具包 nftables

如果您安装了 iptables-legacy,请安装 iptables,它将自动卸载 iptables-legacy 并防止与 nftables 冲突。

注意 iptables 软件包提供的 iptables 命令实现实际上会创建并作用于 nftables 规则。然而,使用旧版 iptables-legacy 工具创建的规则是独立的对象,如果检测到它们,iptables 会发出警告。

前端

提示 大多数 iptables 前端目前没有直接或间接支持 nftables,但未来可能会引入支持[1]。一个同时支持 nftables 和 iptables 的图形化前端是 firewalld[2]ufw 通过 iptables-nft 兼容层获得支持[3]
  • firewalld (firewall-cmd) — 用于配置网络和防火墙区域以及设置和配置防火墙规则的守护进程和控制台接口。
https://firewalld.org/ || firewalld
  • nft-blackhole — 用于通过国家/地区列表和黑名单在 nftables 中阻止 IP 的脚本/守护进程。
https://github.com/tomasz-c/nft-blackhole || nft-blackholeAUR
  • ufw — Ufw 代表 Uncomplicated Firewall,是一个用于管理 netfilter 防火墙的程序。
https://help.ubuntu.com/community/UFW || ufw
  • reaction — 一个扫描程序输出中的重复模式并采取行动的守护进程。fail2ban的轻量级替代品。
https://framagit.org/ppom/reaction || reactionAUR

用法

提示 如果您已经有 iptables 规则,可以将它们转换为 nftables 规则,请参阅 [4]

nftables 对命令行创建的临时规则和从文件加载或保存到文件的永久规则不作区分

所有规则都必须使用 nft 命令行工具来创建或加载。

有关如何使用,请参阅 #配置 章节。

打印当前规则集:

# nft list ruleset

移除所有规则集,使系统处于无防火墙状态:

# nft flush ruleset

通过重启 nftables.service/etc/nftables.conf 读取规则集。

简单的防火墙

nftables 附带了一个存储在 /etc/nftables.conf 文件中的简单且安全的防火墙配置。

nftables.service启动或启用时会从该文件加载规则。

配置

nftables 用户空间工具 nft 在将规则集交给内核之前,会执行大部分的规则评估。规则存储在链中,而链又存储在表中。以下章节将说明如何创建和修改这些结构。

要从文件读取输入,请使用 -f/--file 选项:

# nft --file filename

请注意,已加载的任何规则不会自动刷新。

有关所有命令的完整列表,请参阅 nft(8)

表格

表 (Tables) 是 #链 (Chains)#集合 (Sets) 等对象的容器;它们仅用于命名空间、作用域划分和组织这些对象。

没有特殊或魔术般的表名。通常,工具会创建带有自身名称的表(例如 Firewalld 只会创建和修改 firewalld 表),因此手动创建一个通用名称(如 firewall)的表通常是个好主意(示例)。

每个表可以包含许多不同类型的规则(例如同时包含 filternat),但仅适用于以下单一协议簇的数据包:

nftables 协议簇 对应的 iptables 工具
ip (如果省略,则默认为此值) iptables(8)
ip6 ip6tables(8)
inet 结合了 iptables(8)ip6tables(8)
arp arptables(8)
bridge ebtables(8)

要创建一条同时适用于 IPv4 和 IPv6 的规则,请使用 inetinet 允许统一 ipip6 协议簇,从而使为两者定义规则变得更容易。

有关地址簇的完整说明,请参阅 nft(8) § ADDRESS FAMILIES

提示iptables 中的表不同,nftables 中没有内置表。

基本命令

列出所有表:
# nft list tables
添加一个新表:
# nft add table family_type table_name
提示 如果希望即使表已存在命令也能成功执行,请使用 create 而不是 add
列出表中的所有链和规则:
# nft list table family_type table_name

例如,列出 inet 协议簇下 my_table 表的所有规则:

# nft list table inet my_table
清空表(删除所有链规则):
# nft flush table family_type table_name
删除一个表:
# nft delete table family_type table_name
提示 如果希望即使表不存在命令也能成功执行,请使用 destroy 而不是 delete

链 (Chains)

链是应用于数据包的 #规则 (Rules) 的集合。数据包在 流量流程图 中从左向右流动;每当数据包到达一个 "钩子"(如 ingress, prerouting, input...)时,所有与该钩子关联的“基础”链都会被处理。(因此,链的处理顺序由钩子类型隐含决定。)

关于链的各种属性、用途以及如何管理它们,Netfilter 的 wiki 上有非常完善的 配置链 (Configuring chains) 文章。

需要注意的是,链的优先级 (priority) 值不仅决定了它相对于其他链的顺序,还决定了它 相对于内核处理过程(特别是连接追踪)的顺序。数据包越早被丢弃,浪费的系统资源越少;但代价是关于该数据包的可获取信息也越少。

规则

关于规则结构和相关命令的基础知识,请参考 Netfilter wiki 上的 简单规则管理 (Simple rule management)

规则由 表达式 (Expressions) 后跟 语句 (Statements) 组成。

最常见的语句是“判定语句”:accept, drop, queue, continue, return, jump chain_namegoto chain_name。除了判定语句外,还有其他类型的语句;详情请参阅 nft(8) § STATEMENTS

表达式

nftables 中提供了多种表达式,它们在很大程度上与 iptables 的对应项一致。最显著的区别在于没有通用或隐含的匹配。在 iptables 中,通用匹配是始终可用的(如 --protocol--source),隐含匹配则是特定于协议的(如确定是 TCP 包后可以使用 --sport)。

另请参阅 Netfilter wiki 上的 通过表达式构建规则 (Building rules through expressions)

以下是可用匹配项的不完整列表:

  • meta (元属性,例如接口)
  • icmp (ICMP 协议)
  • icmpv6 (ICMPv6 协议)
  • ip (IP 协议)
  • ip6 (IPv6 协议)
  • tcp (TCP 协议)
  • udp (UDP 协议)
  • sctp (SCTP 协议)
  • ct (连接追踪)

以下是匹配参数的不完整列表(完整列表请参阅 nft(8)):

meta:
  iif <input interface INDEX>
  oif <output interface INDEX>
  iifname <input interface NAME>
  oifname <output interface NAME>

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>

从某种意义上说,iif/oif 与 iifname/oifname 的区别类似于静态与动态,或者编程概念中定义 (definition) 与声明 (declaration) 的区别,亦或是早期绑定与延迟绑定的区别。请参阅 用例及进一步解释

集合 (Sets)

集合可以是命名的或匿名的,由一个或多个用逗号分隔、并在花括号内的元素组成。匿名集合嵌入在规则中且无法更新,必须删除并重新添加规则才能修改。例如,在下例中不能直接从 dports 集合中移除 "http"。

# nft add rule ip6 filter input tcp dport {telnet, http, https} accept

命名集合可以更新,并可以指定类型和标记。sshguard 使用命名集合来存储被封禁主机的 IP 地址。

table ip sshguard {
       set attackers {
               type ipv4_addr
               flags interval
               elements = { 1.2.3.4 }
       }

要向集合中添加删除元素,请使用:

# nft add element ip sshguard attackers { 5.6.7.8/32 }
# nft delete element ip sshguard attackers { 1.2.3.4/32 }

请注意,ipv4_addr 类型可以包含 CIDR 网络掩码(此处的 /32 不是必需的,但为了完整性而包含)。还要注意,此处由 TABLE ip sshguard { SET attackers } 定义的集合,在引用时应使用 ip sshguard attackers

提示 可以配置 systemd-networkd 的连接,将主机的 IP 地址、网络前缀和接口索引填充到预定义的命名集合中。有关详细信息,请参阅 systemd.network(5) § [ADDRESS] SECTION OPTIONS 中关于 NFTSet= 的描述,以及 #使用 systemd-networkd 配置动态命名集合 的示例。

原子重载

刷新当前规则集:

# echo "flush ruleset" > /tmp/nftables 

导出当前规则集:

# nft -s list ruleset >> /tmp/nftables

现在您可以编辑 /tmp/nftables 并应用更改:

# nft -f /tmp/nftables

示例

工作站

/etc/nftables.conf
flush ruleset

table inet my_table {
	set LANv4 {
		type ipv4_addr
		flags interval

		elements = { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 }
	}
	set LANv6 {
		type ipv6_addr
		flags interval

		elements = { fd00::/8, fe80::/10 }
	}

	chain my_input_lan {
		udp sport 1900 udp dport >= 1024 meta pkttype unicast limit rate 4/second burst 20 packets accept comment "Accept UPnP IGD port mapping reply"

		udp sport netbios-ns udp dport >= 1024 meta pkttype unicast accept comment "Accept Samba Workgroup browsing replies"

	}

	chain my_input {
		type filter hook input priority filter; policy drop;

		iif lo accept comment "Accept any localhost traffic"
		ct state invalid drop comment "Drop invalid connections"
		fib daddr . iif type != { local, broadcast, multicast } drop comment "Drop packets if the destination IP address is not configured on the incoming interface (strong host model)"
		ct state { established, related } accept comment "Accept traffic originated from us"

		meta l4proto { icmp, ipv6-icmp } accept comment "Accept ICMP"
		ip protocol igmp accept comment "Accept IGMP"

		udp dport mdns ip6 daddr ff02::fb accept comment "Accept mDNS"
		udp dport mdns ip daddr 224.0.0.251 accept comment "Accept mDNS"

		ip6 saddr @LANv6 jump my_input_lan comment "Connections from private IP address ranges"
		ip saddr @LANv4 jump my_input_lan comment "Connections from private IP address ranges"

		counter comment "Count any other traffic"
	}

	chain my_forward {
		type filter hook forward priority filter; policy drop;
		# Drop everything forwarded to us. We do not forward. That is routers job.
	}

	chain my_output {
		type filter hook output priority filter; policy accept;
		# Accept every outbound connection
	}

}
提示 使用 systemd-networkd 且连接到本地网络时,可以使用 systemd.network(5)NFTSet= 选项来获取连接的网络前缀,从而避免硬编码网络子网。请参阅 #使用 systemd-networkd 配置动态命名集合

服务器

/etc/nftables.conf
flush ruleset

table inet my_table {
	set LANv4 {
		type ipv4_addr
		flags interval

		elements = { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 }
	}
	set LANv6 {
		type ipv6_addr
		flags interval

		elements = { fd00::/8, fe80::/10 }
	}

	chain my_input_lan {
		meta l4proto { tcp, udp } th dport 2049 accept comment "Accept NFS"

		udp dport netbios-ns accept comment "Accept NetBIOS Name Service (nmbd)"
		udp dport netbios-dgm accept comment "Accept NetBIOS Datagram Service (nmbd)"
		tcp dport netbios-ssn accept comment "Accept NetBIOS Session Service (smbd)"
		tcp dport microsoft-ds accept comment "Accept Microsoft Directory Service (smbd)"

		udp sport { bootpc, 4011 } udp dport { bootps, 4011 } accept comment "Accept PXE"
		udp dport tftp accept comment "Accept TFTP"
	}

	chain my_input {
		type filter hook input priority filter; policy drop;

		iif lo accept comment "Accept any localhost traffic"
		ct state invalid drop comment "Drop invalid connections"
		fib daddr . iif type != { local, broadcast, multicast } drop comment "Drop packets if the destination IP address is not configured on the incoming interface (strong host model)"
		ct state { established, related } accept comment "Accept traffic originated from us"

		meta l4proto { icmp, ipv6-icmp } accept comment "Accept ICMP"
		ip protocol igmp accept comment "Accept IGMP"

		udp dport mdns ip6 daddr ff02::fb accept comment "Accept mDNS"
		udp dport mdns ip daddr 224.0.0.251 accept comment "Accept mDNS"

		ip6 saddr @LANv6 jump my_input_lan comment "Connections from private IP address ranges"
		ip saddr @LANv4 jump my_input_lan comment "Connections from private IP address ranges"

		tcp dport ssh accept comment "Accept SSH on port 22"

		tcp dport ipp accept comment "Accept IPP/IPPS on port 631"

		meta l4proto { tcp, udp } th dport { http, https, 8008, 8080 } accept comment "Accept HTTP (ports 80, 443, 8008, 8080)"

		udp sport bootpc udp dport bootps ip saddr 0.0.0.0 ip daddr 255.255.255.255 accept comment "Accept DHCPDISCOVER (for DHCP-Proxy)"
	}

	chain my_forward {
		type filter hook forward priority filter; policy drop;
		# Drop everything forwarded to us. We do not forward. That is routers job.
	}

	chain my_output {
		type filter hook output priority filter; policy accept;
		# Accept every outbound connection
	}

}

限速

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

		iif lo accept comment "Accept any localhost traffic"
		ct state invalid drop comment "Drop invalid connections"
		fib daddr . iif type != { local, broadcast, multicast } drop comment "Drop packets if the destination IP address is not configured on the incoming interface (strong host model)"

		meta l4proto icmp icmp type echo-request limit rate over 10/second burst 4 packets drop comment "No ping floods"
		meta l4proto ipv6-icmp icmpv6 type echo-request limit rate over 10/second burst 4 packets drop comment "No ping floods"

		ct state { established, related } accept comment "Accept traffic originated from us"

		meta l4proto { icmp, ipv6-icmp } accept comment "Accept ICMP"
		ip protocol igmp accept comment "Accept IGMP"

		tcp dport ssh ct state new limit rate 15/minute accept comment "Avoid brute force on SSH"

	}

}

跳转 (Jump)

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

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

针对不同接口使用不同规则

如果您的机器有多个网络接口,并且您想为不同接口使用不同规则,可以使用一个“分发”过滤链,然后使用特定于接口的过滤链。例如,假设您的机器充当家用路由器,您想运行一个可以通过局域网 (接口 enp3s0) 访问但不能通过公共互联网 (接口 enp2s0) 访问的 Web 服务器,您可以考虑这样的结构:

table inet my_table {
  chain my_input { # this chain serves as a dispatcher
    type filter hook input priority filter; policy drop;

    iif lo accept comment "always accept loopback"
    iifname enp2s0 jump my_input_public
    iifname enp3s0 jump my_input_private
  }
  chain my_input_public { # rules applicable to public interface interface
    ct state {established,related} accept
    ct state invalid drop
    udp dport bootpc accept
    tcp dport bootpc accept
  }
  chain my_input_private {
    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 icmpx port-unreachable comment "all other traffic"
  }
  chain my_output { # we let everything out
    type filter hook output priority filter;
    accept
  }
}

或者,您可以只选择一个 iifname 语句(例如针对单个上行接口),并将所有其他接口的默认规则放在一处,而不是为每个接口都进行分发。

地址伪装 (Masquerading)

nftables 有一个特殊的关键字 masquerade,它“会自动将源地址设置为输出接口的地址” (来源)。这对于接口 IP 地址不可预测或不稳定(例如连接到多个 ISP 的路由器的上行接口)的情况特别有用。没有它,每次接口 IP 地址更改时,网络地址转换规则都必须更新。

要使用它:

  • 确保内核中启用了 masquerading(如果您使用默认内核则是启用的),否则在内核配置中设置 CONFIG_NFT_MASQ=m
  • masquerade 关键字只能在 nat 类型的链中使用。
  • 地址伪装是一种源 NAT,因此它仅在输出路径中有效。

对于有两个接口的机器示例:LAN 连接到 enp3s0,公共互联网连接到 enp2s0

table inet my_nat {
  chain my_masquerade {
    type nat hook postrouting priority srcnat;
    oifname "enp2s0" masquerade
  }
}

由于表类型是 inet,IPv4 和 IPv6 数据包都将被伪装。如果您只想伪装 IPv4 数据包(因为 IPv6 地址空间巨大,无需 NAT),可以在 oifname "enp2s0" 前使用 meta nfproto ipv4 表达式,或者将表类型更改为 ip

带有端口转发的 NAT

此示例将伪装通过 WAN 接口 eth0 退出的流量,并将端口 22 和 80 转发到 10.0.0.2。您需要通过 sysctlnet.ipv4.ip_forward 设置为 1

table nat {
    chain prerouting {
        type nat hook prerouting priority dstnat;
        iif eth0 tcp dport {22, 80} dnat to 10.0.0.2
    }
    chain postrouting {
        type nat hook postrouting priority srcnat;
        oif eth0 masquerade
    }
}

统计每个 IP 的新连接数

使用此代码段统计 HTTPS 连接:

/etc/nftables.conf
table inet filter {
    set https {
        type ipv4_addr;
        flags dynamic;
        size 65536;
        timeout 60m;
    }

    chain input {
        type filter hook input priority filter;
        ct state new meta l4proto { tcp, udp } th dport 443 update @https { ip saddr counter }
    }
}

要打印计数器,运行 nft list set inet filter https

动态黑洞

使用此代码段可丢弃在 1 分钟内连接超过 10/秒限制的源 IP(或 /64 IPv6 范围)的所有 HTTPS 连接。

/etc/nftables.conf
table inet dev {
    set blackhole_ipv4 {
        type ipv4_addr;
        flags dynamic, timeout;
        size 65536;
    }
    set blackhole_ipv6 {
        type ipv6_addr;
        flags dynamic, timeout;
        size 65536;
    }

    chain input {
        type filter hook input priority filter; policy accept;
        ct state new meta l4proto { tcp, udp } th dport 443 \
                meter flood_ipv4 size 128000 { ip saddr timeout 10s limit rate over 10/second } \
                add @blackhole_ipv4 { ip saddr timeout 1m }
        ct state new meta l4proto { tcp, udp } th dport 443 \
                meter flood_ipv6 size 128000 { ip6 saddr and ffff:ffff:ffff:ffff:: timeout 10s limit rate over 10/second } \
                add @blackhole_ipv6 { ip6 saddr and ffff:ffff:ffff:ffff:: timeout 1m }

        ip saddr @blackhole_ipv4 counter drop
        ip6 saddr and ffff:ffff:ffff:ffff:: @blackhole_ipv6 counter drop
    }
}

要打印被列入黑洞的 IP,运行 nft list set inet dev blackhole_ipvX

技巧与提示

保存当前规则集

nft list ruleset 命令的输出本身就是一个有效的输入文件。当前的规则集可以保存到文件并在稍后加载回来。

# nft -s list ruleset | tee filename
注意 nft list 不会输出变量定义,如果您在原始文件中定义了变量,它们将会丢失。规则中使用的任何变量都将被其具体值替换。

简单的有状态防火墙

有关更多信息,请参阅 简单的状态防火墙

单机

刷新当前规则集:

# nft flush ruleset

添加一个表:

# nft add table inet my_table

添加输入、转发和输出基础链。输入和转发的策略设置为 drop。输出的策略设置为 accept。

# nft add chain inet my_table my_input '{ type filter hook input priority 0 ; policy drop ; }'
# nft add chain inet my_table my_forward '{ type filter hook forward priority 0 ; policy drop ; }'
# nft add chain inet my_table my_output '{ type filter hook output priority 0 ; policy accept ; }'

添加两个与 tcp 和 udp 关联的常规链:

# nft add chain inet my_table my_tcp_chain
# nft add chain inet my_table my_udp_chain

接受相关和已建立的流量:

# nft add rule inet my_table my_input ct state '{ related, established }' accept

接受所有回环接口流量:

# nft add rule inet my_table my_input iif lo accept

丢弃任何无效流量:

# nft add rule inet my_table my_input ct state invalid drop

接受 ICMP 和 IGMP:

# nft add rule inet my_table my_input meta l4proto '{ icmp, ipv6-icmp }' accept
# nft add rule inet my_table my_input ip protocol igmp accept

新的 udp 流量将跳转到 UDP 链:

# nft add rule inet my_table my_input meta l4proto udp ct state new jump my_udp_chain

新的 tcp 流量将跳转到 TCP 链:

# nft add rule inet my_table my_input 'meta l4proto tcp tcp flags & (fin|syn|rst|ack) == syn ct state new jump my_tcp_chain'

此时,您应该决定向传入连接开放哪些端口(由 TCP 和 UDP 链处理)。例如,要开放 Web 服务器连接,请添加:

# nft add rule inet my_table my_tcp_chain tcp dport 80 accept

要接受端口 443 上的 Web 服务器 HTTPS 连接:

# nft add rule inet my_table my_tcp_chain tcp dport 443 accept
# nft add rule inet my_table my_udp_chain udp dport 443 accept

要接受端口 22 上的 SSH 流量:

# nft add rule inet my_table my_tcp_chain tcp dport 22 accept

要接受传入的 DNS 请求:

# nft add rule inet my_table my_tcp_chain tcp dport 53 accept
# nft add rule inet my_table my_udp_chain udp dport 53 accept

确保在满意后使更改永久生效。

防范暴力破解攻击

Sshguard 是一个可以检测暴力破解攻击并根据其暂时封禁的 IP 地址修改防火墙的程序。有关如何配置 nftables 以配合其使用,请参阅 Sshguard#nftables

记录流量日志

您可以使用 log 动作记录数据包。记录所有传入流量的最简单规则是:

# nft add rule inet filter input log

有关详细信息,请参阅 nftables wiki

监控

监听所有事件,并以原生 nft 格式报告:

# nft monitor

请参阅 nft(8) § MONITOR

临时规则集调试追踪

meta nftrace set 1 开启/关闭规则集数据包追踪。使用 监控追踪 (monitor trace) 命令查看追踪结果。

在另一个 shell 中,在交互式 shell 内部“包含”该文件:

# nft -i
nft> include "/root/nftables.trace"

示例,根据需要进行调整:

/root/nftables.trace
add table ip temp-trace {comment "Temporary table!!"; flags owner;}
add chain ip temp-trace icmp-prerouting { type filter hook prerouting priority raw - 1 ; }
add rule ip temp-trace icmp-prerouting ip protocol icmp meta nftrace set 1

此文件添加了一个临时表 (flags owner),因此如果调用它的交互式 nft 进程关闭,它会自动被移除。基础链需要根据您的使用场景进行调整。您可以创建多个链和多个规则,“meta nftrace set 1”中“ip protocol icmp”仅作为示例,并非必需。实现类似效果的方法有很多,其优势在于关闭交互式 shell 时,先前的状态会自动恢复,并且如果文件中存在错误,则不会执行任何操作。

请参阅 nftables wiki 和一个 自动化并着色显示结果的 python 工具

使用 iptables-nft

本文或本章节的准确性存在争议。

原因: 如果加载了旧版 iptables 对象,nftables 会发出警告,因此“同时使用旧版 iptables nftables 并非完全可行”。(请在 Talk:Nftables 中讨论)

较旧的 iptables 语言在 Linux 文档中仍然占据主导地位,并且许多事物仍然依赖 iptables 运行(例如 Docker 的网络)。尽管同时使用旧版 iptables nftables 是完全可行的,但优先使用 iptables-nft 的转换功能更好,因为:

  • 它将所有内容放在同一位置,并使用更新、更高效、无锁的框架。
  • 它会检查冲突。

有两种方法可以在 nftables 中使用旧版 iptables 语言:

  • iptables-translateiptables-restore-translate(对于 ip6tables, ebtables 等同理)接受 iptables 语言并输出 nft 语言。它们不会更改当前运行的 nft 设置。请参阅 xtables-translate(8)
对于以后需要维护的配置,最好使用 -translate 工具并将结果代码集成到您现有的规则中。例如,如果您在 简单的状态防火墙 或互联网上找到有用的内容,可以将其转换为您的 nft 配置。
  • iptablesiptables-restore(对于 ip6tables 等同理)使用上述翻译并将其放入当前运行的 nft 设置中。它还像常规 iptables 一样提供统计信息。请参阅 xtables-nft(8)
这些命令在处理现有内容时表现相当不错。对于简单的用法它们应该“直接可用”,但偶尔您可能需要手动介入进行调试。
注意 转换器涵盖了 iptables 语言的大部分,但不是全部。有些 iptables 规则是配合使用的,转换器需要上下文才能正确转换,因此如果某些行被转换为空,请不要惊慌。

使用 systemd-networkd 配置动态命名集合

systemd-networkd 的连接可以使用 NFTSet= 选项将主机的 IP 地址、网络前缀和接口索引填充到预定义的命名集合中。这避免了在 /etc/nftables.conf 中进行硬编码。NFTSet= 选项在 [Address], [DHCPv4], [DHCPv6][IPv6AcceptRA] 节中受支持。请参阅 systemd.network(5) § [ADDRESS] SECTION OPTIONS

例如,要在单独的 my_input_lan 链中处理来自本地网络(IP 地址通过 DHCP 或 SLAAC 分配)的连接:

/etc/nftables.conf
...
table inet my_table {

	set eth_ipv4_prefix {
		type ipv4_addr
		flags interval
		comment "Populated by systemd-networkd"
	}
	set eth_ipv6_prefix {
		type ipv6_addr
		flags interval
		comment "Populated by systemd-networkd"

		elements = { fe80::/10 }
	}
	set eth_ifindex {
		type iface_index
		comment "Populated by systemd-networkd"
	}
...
	chain my_input {
		type filter hook input priority filter; policy drop;

		iif @eth_ifindex ip6 saddr @eth_ipv6_prefix jump my_input_lan comment "Connections from LAN"
		iif @eth_ifindex ip saddr @eth_ipv4_prefix jump my_input_lan comment "Connections from LAN"
	}
...
}
/etc/systemd/network/my-network.network
...

[DHCPv4]
NFTSet=prefix:inet:my_table:eth_ipv4_prefix
NFTSet=ifindex:inet:my_table:eth_ifindex

[DHCPv6]
NFTSet=prefix:inet:my_table:eth_ipv6_prefix
NFTSet=ifindex:inet:my_table:eth_ifindex

[IPv6AcceptRA]
NFTSet=prefix:inet:my_table:eth_ipv6_prefix
NFTSet=ifindex:inet:my_table:eth_ifindex
...

故障排除

与 Docker 协同工作

  • 使用以下设置,即使使用 --net host --privileged,您也无法在容器内使用 AF_BLUETOOTH 等协议。
  • Rootless Docker 容器已经在单独的网络命名空间中运行。您可能不需要执行任何操作。

使用 nftables 可能会干扰 Docker 的网络(以及其他容器运行时)。您可以在互联网上找到各种变通方法,要么涉及修补 iptables 规则并确保定义的服务启动顺序,要么完全禁用 Docker 的 iptables 管理,但这会使 Docker 的使用非常受限(考虑端口转发或 docker-compose)。

一种可靠的方法是让 Docker 在一个单独的网络命名空间中运行,在那里它可以随心所欲地操作。可能最好不要使用 iptables,而是使用 iptables-legacy 来防止 Docker 混合 nftables 和 iptables 规则。

使用以下 Docker 服务 放置文件 (drop-in file)

/etc/systemd/system/docker.service.d/netns.conf
[Service]
PrivateNetwork=yes
PrivateMounts=No

# cleanup
ExecStartPre=-nsenter -t 1 -n -- ip link delete docker0

# add veth
ExecStartPre=nsenter -t 1 -n -- ip link add docker0 type veth peer name docker0_ns
ExecStartPre=sh -c 'nsenter -t 1 -n -- ip link set docker0_ns netns "$$BASHPID" && true'
ExecStartPre=ip link set docker0_ns name eth0

# bring host online
ExecStartPre=nsenter -t 1 -n -- ip addr add 10.0.0.1/24 dev docker0
ExecStartPre=nsenter -t 1 -n -- ip link set docker0 up

# bring ns online
ExecStartPre=ip addr add 10.0.0.100/24 dev eth0
ExecStartPre=ip link set eth0 up
ExecStartPre=ip route add default via 10.0.0.1 dev eth0

如果 10.0.0.* IP 地址不适合您的设置,请进行调整。

使用以下 postrouting 规则启用 IP 转发并为 docker0 设置 NAT:

iifname docker0 oifname eth0 masquerade

然后,确保启用了 内核 IP 转发

现在您可以为 docker0 接口设置防火墙和端口转发,而不会受到 nftables 的任何干扰。

参见

© . This site is unofficial and not affiliated with Arch Linux.

Content is available under GNU Free Documentation License 1.3 or later unless otherwise noted.