我看到输出链中的内核丢弃了 wireguard 数据包,但没有 UID 字段。如何仅启用内核在端口 51820 上发送 udp 数据包。(这是 Linux 5.8.3)。我有一个捕获所有日志规则,输出日志中没有 UID。
如果我不启用 UID 检查,那么任何用户都可以通过此端口发送 udp 数据包。
答案1
非由用户进程本地发出的数据包没有 UID,例如:
- 传入流量
- 路由流量
- 内核生成的流量(例如:ICMP 错误、TCP RST)
- 用于隧道传输 WireGuard 的 UDP 数据包(由核心的 WireGuard 驱动程序),即使这里只涉及额外的协议数据包而不是全部(见下文)。
- 可能缺少一些例子
以下是所有者匹配模块:
转发的数据包没有与之关联的套接字。来自内核线程的数据包确实有套接字,但通常没有所有者。
[...]
[!] --uid-owner userid[-userid]
如果数据包套接字的文件结构(如果有)归给定用户所有,则匹配。您还可以指定数字 UID 或 UID 范围。
当数据包没有关联的 UID(关联的套接字)时,保留的无效的 uid -1,又名 2^32-1=4294967295(这不是作为参数给出的有效值)返回给所有者match 模块而不是 UID。这只是为了解释这些值的含义。要找到与任何 UID 都不关联的数据包,必须执行匹配所有可能 UID 的反向查询: ! --uid-owner 0-4294967294
。
现在至少对于 WireGuard 的具体情况来说,WireGuard 接口中数据包的所有者元属性(例如WG0)传播到实际接口上的封装数据包(例如eth0)。 所以最多封装的 UDP 流量将与生成该流量的相关所有者一起出现,与内部流量的所有者相匹配WG0接口。这不应该是理所当然的(我实际上很惊讶这是事实,这里使用的是内核 5.7.x),其他类型的隧道可能在这里表现不同(它们在接口内部有所有者,但在封装数据包外部没有所有者)。我预计像 OpenVPN 这样的用户空间隧道会像这样表现。
那么还是一开始描述的那些没有owner的所有情况,其中包括:
- WireGuard 的协议流量会定期重新验证通信。通常在没有通信的情况下,在某些数据有效负载之前或之后发送。在以下情况下也会发送持久保活已启用。
- 隧道内核流量无所有者(例如 TCP RST),因此封装的 UDP 数据包也没有所有者。
这些需要如上所述的反向匹配。
我不知道这个问题背后的实际目的,但假设它只向某些 UID 授予访问权限。对于 UID 0 和 1000,这将变成(可以进一步分解):
iptables -I OUTPUT 1 -p udp --dport 51820 -m owner ! --uid-owner 0-4294967294 -j ACCEPT
iptables -I OUTPUT 2 -p udp --dport 51820 -m owner 0 -j ACCEPT
iptables -I OUTPUT 3 -p udp --dport 51820 -m owner 1000 -j ACCEPT
iptables -I OUTPUT 4 -p udp --dport 51820 -j DROP
可以使用两个规则来验证日志中的用户/内核属性,如下所示(这肯定需要一些额外的限制匹配):
iptables -I OUTPUT 1 -p udp --dport 51820 -m owner ! --uid-owner 0-4294967294 -j LOG --log-prefix ' kernel originated packet: '
iptables -I OUTPUT 2 -p udp --dport 51820 -m owner --uid-owner 0-4294967294 -j LOG --log-prefix 'userland originated packet: ' --log-uid
答案2
添加一条规则,阻止所有用户的所有流量,即 UID 不为 0 的用户(如果您比较谨慎,也可以为 root 添加一个)。然后添加一条规则,允许刚刚阻止但没有 UID 值的流量。