我在运行 NixOS 22.11 的路由器上使用 nftables(启用了最新的 XanMod 内核补丁和 acpid 以及 irqbalance)。该机器有 3 个接口:连接到互联网的 enp4s0 和两个服务于不同 IP LAN 的本地 WiFi 接入点 wlp1s0 和 wlp5s0。
我的 nftables 配置如下:我只允许本地网络上的入站 DNS、DHCP 和 SSH 流量,并允许出站和转发流量以及 SNAT 到互联网。
table ip filter {
chain conntrack {
ct state vmap { invalid : drop, established : accept, related : accept }
}
chain dhcp {
udp sport 68 udp dport 67 accept comment "dhcp"
}
chain dns {
ip protocol { tcp, udp } th sport 53 th sport 53 accept comment "dns"
}
chain ssh {
ip protocol tcp tcp dport 22 accept comment "ssh"
}
chain in_wan {
jump dns
jump dhcp
jump ssh
}
chain in_iot {
jump dns
jump dhcp
}
chain inbound {
type filter hook input priority filter; policy drop;
icmp type echo-request limit rate 5/second accept
jump conntrack
iifname vmap { "lo" : accept, "wlp1s0" : goto in_wan, "enp4s0" : drop, "wlp5s0" : goto in_iot }
}
chain forward {
type filter hook forward priority filter; policy drop;
jump conntrack
oifname "enp4s0" accept
}
}
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname "enp4s0" snat to 192.168.1.2
}
}
table ip6 global6 {
chain input {
type filter hook input priority filter; policy drop;
}
chain forward {
type filter hook forward priority filter; policy drop;
}
}
通过这个简单的配置,我预计 KDE Connect 无法工作因为它需要打开端口 1714-1764。事实上,如果我将计算机连接到 wlp1s0,将手机连接到 wlp5s0(接口不同),设备将无法看到彼此,并且我可以使用日志记录规则或 nftrace 通过 tcpdump 以及 nftables 查看数据包。
但不知怎的,如果我现在将两台机器放在同一个接口上,例如 wlp1s0,KDE Connect 就可以正常工作,并且设备可以看到对方。我最好的猜测是,这是由于连接跟踪而发生的,但即使我添加
chain trace_wan {
type filter hook prerouting priority filter - 1; policy accept;
iifname "wlp1s0" oifname "wlp1s0" meta nftrace set 1
}
到filter
表中,运行时我看不到任何数据包nft monitor trace
。同样,当在链中索引 0 处插入日志记录规则时,我在系统日志中看不到任何数据包forward
。然而,在运行时,tcpdump -i wlp1s0 port 1716
我可以看到我希望 nftables 也能看到的数据包:
14:33:59.943462 IP 192.168.2.11.55670 > 192.168.2.42.xmsg: Flags [.], ack 20422, win 501, options [nop,nop,TS val 3319725685 ecr 2864656484], length 0
14:33:59.957101 IP 192.168.2.42.xmsg > 192.168.2.11.55670: Flags [P.], seq 20422:20533, ack 1, win 285, options [nop,nop,TS val 2864656500 ecr 3319725685], length 111
当两个设备连接在同一接口上时,为什么 nftables 看不到这些数据包?我怎样才能让 nftables 默认丢弃所有这些转发的数据包?
评论中要求的其他信息:
❯ ip -br link
lo UNKNOWN <LOOPBACK,UP,LOWER_UP>
enp2s0 DOWN <BROADCAST,MULTICAST>
enp3s0 DOWN <BROADCAST,MULTICAST>
enp4s0 UP <BROADCAST,MULTICAST,UP,LOWER_UP>
wlp5s0 UP <BROADCAST,MULTICAST,UP,LOWER_UP>
wlp1s0 UP <BROADCAST,MULTICAST,UP,LOWER_UP>
❯ ip -4 -br address
lo UNKNOWN 127.0.0.1/8
enp4s0 UP 192.168.1.2/24
wlp5s0 UP 192.168.3.1/24
wlp1s0 UP 192.168.2.1/24
❯ bridge link
❯ ip route
default via 192.168.1.1 dev enp4s0 proto static
192.168.1.0/24 dev enp4s0 proto kernel scope link src 192.168.1.2
192.168.1.1 dev enp4s0 proto static scope link
192.168.2.0/24 dev wlp1s0 proto kernel scope link src 192.168.2.1
192.168.3.0/24 dev wlp5s0 proto kernel scope link src 192.168.3.1
❯ sysctl net.bridge.bridge-nf-call-iptables
sysctl: error: 'net.bridge/bridge-nf-call-iptables' is an unknown key
答案1
警告:这是一个通用的 Linux 答案。本答案不会涵盖与 NixOS 的特定集成及其自己的配置网络的方法或如何从其配置中调用任意命令。
推介会
在OP的第一种情况(两个不同的接口)中,路由器实际上是两个接口之间的路由wlp1s0和wlp5s0:转发的 IPv4 流量可见于nftables'家庭ip,过滤转发钩子。
第二种情况下,流量为桥接的通过路由器的接入点接口wlp1s0:nftables'家庭IP表看不到桥接流量,仅看到 IPv4 流量。
此外,这种桥接甚至不会发生在标准 Linux 桥接级别,而是由接入点 (AP) 的驱动程序(和/或硬件加速)直接完成:两个 Wifi 设备将在它们之间进行通信(仍然通过 AP)它们的帧没有到达实际的网络堆栈。
为了使系统真正过滤此流量,必须完成三件事:
- 更改 AP 的设置,以便流量通过网络堆栈
- 有一个与 AP 关联的 Linux 桥,这样网络堆栈就不会丢弃帧,这样nftables可以在桥层面看到它们
- 有足够的nftables桥牌家族的规则。为了网桥系列中的 IP 状态防火墙,这也需要 Linux 内核>= 5.3(NixOS 22.11 就足够了)。
未采用的其他选项:
作为 2+3 的替代方案,并且在没有可能的状态防火墙的情况下,人们可能会想象使用nftables'网络开发者有入口和可能出口的家庭(需要Linux 内核 >= 5.17对于出口
fwd
),但会有很多极端情况需要处理:最好不要而不是 3,使用旧桥网络过滤器代码用于桥接路径中的状态防火墙iptables(并由 Docker 使用)将所有规则放在同一个表中
nftables,它也受它的影响,旨在不依赖于此代码,因此缺乏正确使用的功能(大多数情况下它缺乏相当于iptables'
physdev
模块的一种方式区分同一规则集中的桥接流量和路由流量)。这会让事情仍然依赖iptables因此仍然需要多个表。 (Docker 中此类复杂使用的示例:nftables 白名单 docker)。作为警告,如果在路由器上添加 Docker,预计会中断下面介绍的设置。
设置
改变主机设置
必须更改两个相关设置主机设置:
告诉帧必须由网络堆栈处理,而不是由驱动程序短路
配置如下主机为了wlp1s0必须改变。如果两个 Wifi 接口以某种方式存在单一配置,那么现在很可能应该有两个单独的配置。我不会在这个答案中讨论这种集成,而是集中讨论单一界面wlp1s0。
AP隔离必须在以下位置启用
hostapd.conf
:ap_isolate=1
现在,两个站客户端 (STA) 之间的帧将到达网络堆栈,而不是由 AP 驱动程序直接处理。
配置主机使用网桥并将无线接口设置为桥接端口
仅使用先前的设置,只有发往或来自路由器的帧才会由路由器处理路由堆栈的一部分网络堆。不属于或来自路由器的帧将被简单地丢弃,如果以太网接口收到不属于其 MAC 地址的单播帧,就会发生这种情况。这也是该设置被命名的原因
ap_isolate
:默认情况下,STA 之间相互隔离。需要一座桥来处理这个问题。告诉主机设置wlp1s0一旦将其配置为 AP 模式,即可作为桥接端口。它将创建一个网桥或(首选)使用提供的名称重用现有网桥,并在运行时将接口设置为网桥端口。我选择了任意名称brwlan1。
配置网桥在
hostapd.conf
:bridge=brwlan1
更改与使用网桥相关的网络设置
配置没有连接端口且无延迟的桥接器
手动的话就是:
ip link add name brwlan1 up type bridge forward_delay 0
注意:
hostapd
是将 Wifi 接口连接到桥接器的工具,因为必须先将其设置为 AP,然后才允许将其设置为桥接端口。移动任何路由(第3层)设置关于wlp1s0到brwlan1:
ip addr delete 192.168.2.1/24 dev wlp1s0 ip addr add 192.168.2.1/24 dev brwlan1
这还包括更改各种应用程序中的任何接口引用,例如在 DHCP 设置中。
...并且nftables但这将在下一部分中讨论。
有主机跑步
验证一旦它wlp1s0实例正在运行,wlp1s0被设置为brwlan1桥接端口:
人们应该看到类似以下内容的内容:
# bridge link show dev wlp1s0 6: wlp1s0 <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master brwlan1 state forwarding priority 32 cost 100
然后在单个桥接端口上启用发夹模式
目前,仍然无法进行 STA 到 STA 的通信,但只能进行 STA 到 AP 或 AP 到 STA:STA 到 STA 需要帧到达单个桥接端口wlp1s0在同一网桥端口上重新发送。即使现在有一个网桥来转发此类帧,但它们还不会:默认情况下,以太网网桥(或交换机)会禁用转发回原始端口,因为它在正常的有线设置中没有多大意义。
所以簪必须启用于wlp1s0因此在此端口上接收到的帧可以在同一端口上重新发出。目前只有开发版本(分支主要的) 的主机接受新的配置参数
bridge_hairpin=1
自动执行此操作(2.10 版本还不够新)。这可以使用手动完成bridge
命令(已过时的brctl
命令不支持此功能):bridge link set dev wlp1s0 hairpin on
这部分需要正确的操作系统集成:必须仅在主机随附的wlp1s0作为桥接端口,因为它只能在桥接端口上使用。我希望
hostapd
设置wlp1s0在守护进程之前作为桥接端口,然后让网络配置工具运行命令。如果情况并非如此并且发生竞争条件,则可以考虑简单地插入延迟(例如:sleep 3;
在此命令之前简单地插入一个延迟(例如: ),以确保命令运行时该接口是桥接端口。应该wlp1s0与网桥分离/重新连接(例如:重新启动hostapd
),必须再次运行此命令:应从网络配置中调用它。
适应nftables规则集
... 使用桥梁家族而不是 ip 系列。这与路由非常相似。用于路由器的帧在输入挂钩中可见,来自路由器的帧在输出挂钩中可见,STA 到 STA 的帧在前向挂钩中可见。
由于对象命名空间是每个表的,因此两者之间不能重用任何规则,因此需要一些重复。我只是复制并改编了与以下内容相关的路由规则的相关部分转发。作为示例,我启用了 ping 和 KDE 连接端口,并带有一些计数器。有些样板文件并不是真正需要的(例如:如果首先有 IPv6 的通用丢弃规则,则
ether type ip ip protocol tcp tcp dport 1714
可以用 just 替换。在向内核提供规则时,该命令会在内部插入任何所需的样板文件)。tcp dport 1714
nft
table bridge filter # for idempotence delete table bridge filter # for idempotence table bridge filter { chain conntrack { ct state vmap { invalid : drop, established : accept, related : accept } } chain kdeconnect { udp dport 1714-1764 counter accept tcp dport 1714-1764 counter accept } chain forward { type filter hook forward priority filter; policy drop; jump conntrack ether type ip6 drop # just like OP did: drop any IPv6 icmp type echo-request counter accept jump kdeconnect ether type arp accept # mandatory for IPv4 connectivity counter } }
应该wlp5s0稍后同样使用其自己的单独网桥进行配置,然后将需要每个网桥端口或每个网桥进行过滤(例如:
iifname wlp1s0
或ibrname brwlan1
等需要时)。其他情况仍由 OP 初始规则集中的标准路由处理:输入和输出过滤器挂钩未配置,因此将接受往返于路由器的流量,或路由至/来自其他接口的流量。
OP的nftables路由也必须进行调整。无论
table ip filter
该词出现在何处wlp1s0
,都必须替换为brwlan1
现在参与路由的接口。