我在运行 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(如此不同的接口),这些设备无法相互看到,并且我可以通过 tcpdump 以及通过 nftables 查看数据包,使用日志记录规则或 nftrace。
但是不知何故,如果我现在将两台机器放在同一个界面上,例如 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
警告:这是一个通用的 Linux 答案。这个答案不会涉及与 NixOS 的特定集成及其自己的网络配置方法或如何从其配置中调用任意命令。
推介会
在 OP 的第一种情况(两个不同的接口)中,路由器实际上在两个接口wlp1s0和wlp5s0之间进行路由:在nftables的 family ip, filter forward hook中可以看到转发的 IPv4 流量。
在第二种情况下,流量由路由器的接入点接口wlp1s0桥接:nftables的family ip表看不到桥接流量,只有 IPv4 流量。
此外,这种桥接甚至不会发生在标准的 Linux 桥接级别,而是由接入点 (AP) 的驱动程序(和/或硬件加速)直接完成:两个 Wifi 设备将在它们之间进行通信(仍然通过 AP)他们的帧没有到达实际的网络堆栈。
为了让系统真正过滤此流量,必须完成三件事:
其他未采用的选项:
作为 2+3 的替代方案,并且没有可能的状态防火墙,人们可能会想象使用nftables的netdev系列与入口和可能的出口(需要Linux 内核 >= 5.17用于出口
fwd
)但是会有很多极端情况需要处理:最好不要而不是 3,使用旧的 bridge netfilter 代码,用于iptables(并由 Docker 使用)在桥路径中进行状态防火墙,以将所有规则放在同一个表中
同样受其影响的nftables旨在不依赖于此代码,因此缺乏正确使用的功能(主要是它缺乏相当于iptables的
physdev
模块,用于区分桥接流量和同一规则集中路由流量的一种方法)。这将使事情仍然依赖iptables,因此仍然需要多个表。(Docker 中这种复杂用法的示例:nftables whitelisting docker)。作为警告,如果在路由器上添加 Docker,预计会中断下面显示的设置。
设置
更改hostapd设置
必须在hostapd设置中更改两个相关设置:
告诉帧必须由网络堆栈处理而不是被驱动程序短路
必须更改wlp1s0的hostapd配置。如果以某种方式为两个 Wifi 接口存在一个配置,那么现在很可能应该有两个单独的配置。我不会在此答案中解决此类集成问题,而是将重点放在单一接口wlp1s0上。
必须启用AP 隔离
hostapd.conf
:现在,两个站点客户端 (STA) 之间的帧将到达网络堆栈,而不是直接由 AP 驱动程序处理。
配置hostapd使用网桥,设置无线接口为网桥端口
仅使用先前的设置,网络堆栈的路由堆栈部分将只处理进出路由器的帧。不是发往或来自路由器的帧将被简单地丢弃,如果以太网接口收到不是发往其 MAC 地址的单播帧,就会发生这种情况。这也是设置被命名的原因:默认情况下,STA 之间相互隔离。
ap_isolate
需要一座桥来处理这个问题。告诉hostapd在将其配置为 AP 模式后立即将wlp1s0设置为网桥端口。它将创建一个网桥或(首选)重用具有提供名称的现有网桥,并在运行时将接口设置为网桥端口。我选择了任意名称brwlan1。
在以下位置配置网桥
hostapd.conf
:更改与使用网桥相关的网络设置
配置没有附加端口且没有延迟的网桥
手动那只是:
注意:
hostapd
是将 Wifi 接口附加到网桥的工具,因为它必须先设置为 AP,然后才能设置为网桥端口。将关于wlp1s0 的任何路由(第 3 层)设置移动到brwlan1:
这还包括更改各种应用程序中的任何接口引用,例如在 DHCP 设置中。
...还有nftables,但这将在下一部分处理。
运行hostapd _
验证一旦其wlp1s0实例运行,wlp1s0被设置为brwlan1网桥端口:
人们应该看到类似于以下内容的内容:
然后在单个桥接端口上启用发夹模式
目前,仍然无法进行STA-to-STA通信,只能进行STA-to-AP或AP-to-STA:STA-to-STA需要重发到达单桥端口wlp1s0的帧在同一个桥接端口上。即使现在有一个网桥来转发这些帧,它们也不会:默认情况下,以太网网桥(或交换机)禁止转发回原始端口,因为它在正常的有线设置中没有多大意义。
因此必须在wlp1s0上启用发夹,以便可以在同一端口上重新发送在此端口上接收到的帧。目前只有hostapd的开发版本(分支main)接受新的配置参数来自动执行此操作(版本 2.10 还不够新)。这可以使用命令手动完成(过时的命令不支持此功能):
bridge_hairpin=1
bridge
brctl
这部分需要适当的 OS 集成:它必须仅在hostapd附加wlp1s0作为网桥端口之后完成,因为它只能在网桥端口上使用。我希望在守护进程之前将wlp1s0
hostapd
设置为桥接端口,然后让网络配置工具运行命令。如果不是这种情况并且发生了竞争条件,可以考虑在此命令之前简单地插入一个延迟(例如:),以确保该接口在命令运行时是桥接端口。如果wlp1s0与网桥分离/重新连接(例如:重新启动),则必须再次运行此命令:它应该从网络配置中调用。sleep 3;
hostapd
适应nftables规则集
...使用bridge 系列而不是 ip 系列。这与路由非常相似。在输入挂钩中可以看到用于路由器的帧,在输出挂钩中可以看到来自路由器的帧,在转发挂钩中可以看到 STA 到 STA 的帧。
由于对象命名空间是每个表的,因此两者之间不能重用任何规则,因此需要一些重复。我只是复制并改编了与转发相关的路由规则的相关部分。例如,我启用了 ping 和用于 KDE 连接的端口,以及一些计数器。一些样板文件并不是真正需要的(例如:如果首先有一个通用的 IPv6 丢弃规则,
ether type ip ip protocol tcp tcp dport 1714
就可以替换它。在向内核提供规则时,命令会在内部插入任何需要的样板文件)。tcp dport 1714
nft
如果wlp5s0以后同样配置有自己的独立网桥,那么将需要按网桥端口或网桥进行过滤(例如:
iifname wlp1s0
或ibrname brwlan1
在需要时等)。其他情况仍然由 OP 的初始规则集中的标准路由处理:输入和输出过滤器挂钩未配置,因此将接受流量,无论是进出路由器,还是路由到/来自其他接口。
OP 的路由nftables也必须进行调整。
table ip filter
出现这个词的地方wlp1s0
,必须换成brwlan1
which is now the interface participating routing。