zeek/zeek-packet-source-udp

GitHub: zeek/zeek-packet-source-udp

一个Zeek插件,用于从GENEVE或VXLAN等UDP隧道中摄取云镜像流量,解决了容器环境中Zeek无特权部署的难题。

Stars: 0 | Forks: 0

# Zeek UDP 数据包源 这是一个 [Zeek](https://github.com/zeek/zeek) 数据包源插件,可直接从 GENEVE 或 VXLAN 等 UDP 流量镜像隧道中消费数据包输入。 ## 概念 其核心概念是使 Zeek 成为一个高性能的 UDP 数据包接收器,用于摄取镜像流量。换言之,Zeek 通过监听适当的端口自行终止 UDP 隧道,而不是从网络接口读取原始数据包。 这避免了在容器环境中访问主机接口所带来的麻烦和复杂性。实际上,此数据包源允许 Zeek 以完全无特权的方式运行。它仅从非特权 UDP 端口接收数据包。换言之,这绕过了在云和容器环境中可能成为负担的任何特权要求、CAP_NET_RAW 能力或复杂的接口配置。 目前,VXLAN 和 GENEVE 封装层已被剥离,仅将包含的数据包数据转发给 Zeek。对于监控单个 VPC,这应该足够了。如果未剥离这些层,Zeek 还将跟踪和记录镜像隧道连接,而这通常没有太大用处。 ## 构建与安装 此插件不包含配置脚本,也没有额外的 Zeek 脚本,因此您可以直接使用 ``cmake`` 进行安装: ``` $ mkdir build && cd build && cmake ../ && make && make install ``` 或者,使用 ``zkg``: ``` $ zkg install zeek-packet-source-udp ``` 如果您想测试 io_uring 实现,请确保安装了较新版本的 liburing。 ## 性能与扩展 此数据包源为 UDP 接收器提供了两种实现。一种基于 ``recvmmsg()`` 系统调用,可在 Linux 上运行,可能也适用于 FreeBSD;另一种实现基于 [liburing](https://github.com/axboe/liburing),需要较新的 Linux 内核(6.1+ 应可运行,已在 Ubuntu 24.04 的 6.4.0 上测试)。 可通过以下方式选择要使用的实现: ``` redef PacketSource::UDP::implementation = PacketSource::UDP = RECVMMSG; # default redef PacketSource::UDP::implementation = PacketSource::UDP = IO_URING; ``` 使用套接字选项 ``SO_TIMESTAMP`` 请求每个数据包的时间戳,并作为 [cmsg](https://man7.org/linux/man-pages/man3/cmsg.3.html) 辅助数据接收。 为了在多个进程间进行负载均衡,使用了 Linux 内核的 [SO_REUSEPORT](https://lwn.net/Articles/542629/) 功能。FreeBSD 也具备此功能,但其名为 SO_REUSEPORT_LB,且仅经过轻度测试。也就是说,多个 Zeek 工作进程将监听同一个 UDP 端口,Linux 内核将根据最外层 IP/UDP 头部在所有监听 UDP 套接字上进行流平衡。只要镜像流量使用内部流的(双向/对称)哈希值作为外部 UDP 源端口(如 [RFC 8926, 第 3.3 节 (GENEVE)](https://datatracker.ietf.org/doc/html/rfc8926#name-udp-header) 或 [RFC 7348, 第 5 节 (VXLAN)](https://datatracker.ietf.org/doc/html/rfc7348#section-5) 中建议),这应在 Zeek 的工作进程之间实现良好且可靠的流平衡。 ## ConnKey 集成 此插件附带了三个 ConnKey 实现,类似于 [Zeek 插件文档](https://docs.zeek.org/en/master/devel/plugins/connkey-plugin.html)中概述的实现。 但是,默认情况下这些都不会加载。这意味着来自不同监控会话的重叠 IP 范围可能会发生冲突。即,一个镜像会话中的 192.168.0.1 与另一个镜像会话中的 192.168.0.1 无法区分。要包含 GENVE VNI 或 VXLAN VNI,或包含来自两层的 VNIs(对于 ``geneve+vxlan`` 封装),请加载此插件中附带的相应脚本: ``` @load policy/frameworks/conn_key/packet_source/udp/vxlan_vni @load policy/frameworks/conn_key/packet_source/udp/geneve_vni @load policy/frameworks/conn_key/packet_source/udp/geneve_vxlan_vni ``` 加载这些脚本时,``conn_id_ctx`` 记录类型将扩展一个 ``gevene_vni`` 字段、一个 ``vxlan_vni`` 字段,或两者都包含。 此插件中没有自动检测或自动加载适用的 ConnKey 实现。技术上我们可以查看接口路径,但此处倾向于使用显式的 ``@load`` 来启用它们。这也便于很好地测试 ConnKey 插件的效果。此插件旨在封装一致且静态的环境中使用。如果这不适用于您的环境,请 fork 仓库并根据需要调整代码。 使用 geneve_vxlan_vni 脚本时,包含两个 VNIs 的 conn.log 记录如下所示: ``` { "ts": 1770052116.800996, "uid": "C15cDG2P4Znvk4uO9g", "id.orig_h": "172.20.10.3", "id.orig_p": 59588, "id.resp_h": "199.60.103.106", "id.resp_p": 80, "id.ctx.geneve_vni": 4711, "id.ctx.vxlan_vni": 4242, "proto": "tcp", "service": "http", ... ``` 请注意,此插件提供的 ConnKey 实现仅适用于使用此插件中的数据包源的实时流量。 ## 用法 ### ZeekControl 用法 在 ``node.cfg`` 中将 ``interface`` 键设置为 ``udp::0.0.0.0:4789:vxlan``,然后运行典型的 ``zeekctl deploy`` 步骤。使用 ``zeectl diag`` 检查错误。 ### zeek-systemd-generator 用法 如果单个主机部署足以满足您的目的,并且您拥有 Zeek 8.1,您可以查看 [Zeek 的 systemd-generator](https://github.com/zeek/zeek/tree/master/tools/systemd-generator) 来设置集群。将以下行放入 ``/etc/zeek/zeek.conf`` 以配置一个 Zeek 集群,其中四个工作进程在端口 4789 上监听 VXLAN 封装的流量。 ``` interface = udp::0.0.0.0:4789:vxlan workers = 4 ``` 运行以下两个命令启动集群: ``` systemctl daemon-reload && systemctl restart zeek.target ``` 日志应出现在 ``/spool/zeek/logger-1/`` 中,并轮转到 ``/var/zeek/logs/``。使用 ``systemctl`` 和 ``journalctl`` 检查各个 Zeek 进程。 ## 支持的封装选项 ``encap`` 选项可设置为以下之一: * vxlan * geneve * geneve+vxlan * raw * skip= 对于 VXLAN 或 GENEVE,它们的头部被剥离,内部数据包及其时间戳通过 ``zeek::Packet`` 数据结构传递给 Zeek。对于 ``geneve+vxlan``,首先剥离 GENEVE 头部,随后是预期的 IP/UDP 头部。这些也被剥离,以及其后的 8 字节 VXLAN 头部。``geneve+vxlan`` 主要用于 AWS GWLB 环境。 ``raw`` 选项导致 UDP 有效负载作为数据包有效负载传递,而不剥离任何头部。 ``skip=`` 选项允许跳过 UDP 有效负载中的固定字节数。这对于具有固定头部大小的不支持的封装可能有用。 例如,``vxlan`` 和 ``skip=8`` 行为相同,只是 ``vxlan`` 将来可能会提取 VNI。 ## 支持的链路类型 链路类型默认为 ``DLT_EN10MB``,但如果没有 L2 头部,且 IPv4 或 IPv6 数据包紧随其后,可以设置为 ``DLT_RAW``。也支持 ``DLT_PP``。如果您需要更多,欢迎提供补丁。 在选项路径中使用以下内容: ``` dlt=[en10mb|raw|ppp] ``` 例如,如果流量作为 GENEVE 封装的 IP 数据包到达,没有以太网头部,则使用以下调用: ``` zeek -i udp::[::1]:6081:geneve:dlt=raw ``` ## 调优与监控 在默认设置下,很容易观察到丢弃的 UDP 数据包。该插件默认为套接字配置了 16MB 的接收缓冲区,这应该是一个不错的起始值。如果增加此大小,请确保 ``net.core.rmem_max`` 值足够大。 在内核中,UDP 数据包可能在多个位置被丢弃。您通常会在增加的捕获丢失或 ``gG`` 或 Zeek 的 [连接历史](https://docs.zeek.org/en/master/scripts/base/protocols/conn/main.zeek.html#field-Conn::Info$history) 中看到这一点。 一些针对 Linux 系统的指引: 检查 UDP 接收错误: ``` $ netstat -suna | grep errors ``` 检查软中断网络错误: ``` $ cat /proc/net/softnet_stat ``` 检查并增加接收缓冲区(32MB 可能是一个好的起点): ``` $ sysctl -a -r '^net.core.[rw]mem' net.core.rmem_default = 33554432 net.core.rmem_max = 33554432 net.core.wmem_default = 33554432 net.core.wmem_max = 33554432 ``` 如需要,请检查并增加 ``netdev_max_backlog``: ``` $ sysctl -a -r net.core.netdev_max_backlog net.core.netdev_max_backlog = 1000 $ sysctl -w net.core.netdev_max_backlog=10000 ``` ## FreeBSD 在 FreeBSD 上,默认的 16M ``udp_recv_buffer_size`` 对于内核默认值来说太大。以 root 身份运行以下命令,和/或将其添加到 ``/etc/sysctl.conf`` 以使更改持久化,将其提升到 64M。 ``` $ sysctl -w kern.ipc.maxsockbuf=67108864 ``` ## 对数据包镜像基础设施的要求 如上所述,此数据包源插件对用于最外层 UDP 源端口的值很敏感。所选端口值应代表镜像流的粘性和对称流哈希(双向数据包具有相同的流哈希)。例如,AWS 的 GWLB [就是这样工作的](https://aws.amazon.com/blogs/networking-and-content-delivery/integrate-your-custom-logic-or-appliance-with-aws-gateway-load-balancer/)。无论流哈希如何,所有镜像数据包的目标端口值必须相同 — Zeek 正在监听该端口。 上游(在到达运行 Zeek 的系统之前)或在操作系统内核内可能发生数据包重排序。使用原始接口进行嗅探无法缓解上游生产者导致的数据包重排序。 如果您观察到 Linux 网络栈中发生数据包重排序,您需要进行研究、调优您的系统,并可能调试内核。这里的关键字是 RSS(接收端缩放)和 RPS(接收包转向)。
标签:Bash脚本, GENEVE隧道, liburing, Linux内核, UDP协议, VXLAN隧道, Web截图, Zeek插件, 容器安全, 数据包源, 无特权运行, 流量监控, 流量镜像, 系统分析, 网络分析, 网络安全, 进程监控, 隐私保护, 高性能数据包捕获