Zeek Broker 集群后端解析:固定对等连接策略、节点发现与集群布局配置
网络安全网络IDS【免费下载链接】zeekZeek is a powerful network analysis framework that is much different from the typical IDS you may know.项目地址https://gitcode.com/gh_mirrors/ze/zeek点击查看免费下载Broker 集群后端是 Zeek 集群框架中历史最悠久、使用最广泛的点对点通信后端它自 Bro 2.6 起投入使用并作为默认后端一直持续到 Zeek 8.1。本文以 Broker 集群后端官方文档 为骨架结合仓库中的 main.zeek 实现 与 Cluster 框架核心系统讲解其固定对等连接策略、基于 cluster-layout.zeek 的集群布局定义、节点启动时的监听/订阅/连接流程以及基于hello/node_up/node_down事件的节点生命周期管理。读完本文你将能够理解并独立配置一套基于 Broker 的 Zeek 集群。Broker 后端的定位从默认走向可选Broker 集群后端policy/frameworks/cluster/backend/broker是 Zeek 集群框架base/frameworks/cluster的一个可选后端实现。它的定位在文档中表述得非常明确它是一种点对点peer-to-peer后端集群中每个节点按照固定的连接策略主动与特定类型的其他节点建立 Broker 对等连接它自Bro 2.6起就在 Zeek 中使用并一直是默认后端直到Zeek 8.1才让位于新的后端实现backend 目录 中同时存在broker与zeromq两个后端集群节点的对等关系信息存储在Cluster::nodes表中该表由cluster-layout.zeek文件填充或在启用 Supervisor 时由 Supervisor 内部填充。从源码上看选择后端的机制非常简单仓库中的 main.zeek 在加载时直接对Cluster::backend进行重定义module Cluster; redef Cluster::backend Cluster::CLUSTER_BACKEND_BROKER;而 init-bare.zeek 中定义了后端的默认值为Cluster::CLUSTER_BACKEND_NONE。也就是说默认情况下集群后端是未启用的只有通过load frameworks/cluster/backend/broker或load frameworks/cluster/backend/zeromq显式加载某个后端或者由上层工具自动注入后集群框架才真正具备通信能力。这一点在 main.zeek 中有强制校验一旦设置了Cluster::node但Cluster::backend仍为NONEZeek 会直接以 fatal 错误退出提示必须选择一个集群后端。固定对等连接策略三句话概括整个拓扑Broker 后端的连接拓扑完全由节点类型决定不需要额外的手工配置。官方文档用三句话概括了这一策略所有节点都与所有 logger 节点建立对等连接所有 worker 节点都与所有 proxy 节点以及 manager 节点建立对等连接所有 proxy 节点都与 manager 建立对等连接。这三条规则意味着logger、manager 和 proxy 节点必须监听 cluster layout 中定义的端口它们是被连接方而 worker 只作为主动连接方。对照源码 main.zeek 的zeek_init分派逻辑可以看得一清二楚switch ( self$node_type ) { case MANAGER: connect_peers_with_type(LOGGER); break; case PROXY: connect_peers_with_type(LOGGER); if ( self?$manager ) connect_peer(MANAGER, self$manager); break; case WORKER: connect_peers_with_type(LOGGER); connect_peers_with_type(PROXY); if ( self?$manager ) connect_peer(MANAGER, self$manager); break; }可见连接规则是严格按节点类型分派的节点类型主动连接的对象MANAGER所有 LOGGER 节点PROXY所有 LOGGER 节点 自己配置的 manager 节点WORKER所有 LOGGER 节点 所有 PROXY 节点 自己配置的 manager 节点LOGGER不主动连接任何节点只负责监听文档中没有单独列出 LOGGER 的连接行为但从switch分派可以看出LOGGER 节点不在任何case中因此它只监听端口、接收来自所有节点的入站对等连接。这与所有节点都与所有 logger 节点互连的规则完全对应——logger 是被动连接方。值得强调的是worker 与 worker 之间永远不会建立直接连接proxy 与 proxy 之间也不会。这正是文档后半部分特别提示的发布-订阅可见性限制的来源见下文。集群布局cluster-layout.zeek 与 Cluster::nodesBroker 后端的连接行为完全依赖于Cluster::nodes中定义的集群布局。cluster 框架文档 对布局文件提出了明确要求需要有一个名为cluster-layout.zeek的脚本存在于 Zeek 的脚本搜索路径ZEEKPATH中其中定义Cluster::nodes变量同时通过CLUSTER_NODE环境变量或Cluster::node指定当前实例扮演的角色并加载load base/frameworks/cluster。⚠️ 官方文档特别警告cluster-layout.zeek只应包含Cluster::nodes的定义避免加载其他 Zeek 脚本或对除Cluster::nodes以外的任何变量使用redef——因为该文件加载时机非常早很容易引发循环加载问题main.zeek。Node 记录结构与节点类型每个节点的配置是一个 Node 记录type Node: record { node_type: NodeType; # 节点类型 ip: addr; # 节点 IP zone_id: string default; # 非全局 IPv6 地址的 zone id p: port default0/unknown; # 监听端口0/unknown 表示不预配置监听 manager: string optional; # worker/proxy 使用的 manager 节点名 id: string optional; # Broker 框架分配的节点标识连接期间才存在 metrics_port: port optional; # Prometheus 指标端口 };其中NodeType枚举定义了六种节点类型types.zeekNONE非集群运行、CONTROL可查看/操作其他节点配置、LOGGER负责日志管理、MANAGER负责策略管理、PROXY中继 worker 通信并同步 worker 状态、WORKER实际执行流量分析。一个可运行的布局示例仓库的测试基础设施中保存了一份完整的 测试用 cluster-layout.zeek它展示了如何通过环境变量动态构建一个含 manager、logger、proxy、worker 的多节点布局。其核心写法如下load frameworks/cluster/backend/broker # 仅配置 manager 时的最小布局 redef Cluster::nodes { [manager] [$node_typeCluster::MANAGER, $ip127.0.0.1, $pto_port(getenv(BROKER_MANAGER_PORT))], }; # 根据环境变量是否设置动态追加节点 if ( getenv(BROKER_LOGGER1_PORT) ! ) redef Cluster::manager_is_logger F; redef Cluster::nodes { [logger-1] [$node_typeCluster::LOGGER, $ip127.0.0.1, $pto_port(getenv(BROKER_LOGGER1_PORT)), $managermanager], }; endif if ( getenv(BROKER_WORKER1_PORT) ! ) redef Cluster::nodes { [worker-1] [$node_typeCluster::WORKER, $ip127.0.0.1, $pto_port(getenv(BROKER_WORKER1_PORT)), $managermanager], }; endif这份文件揭示了布局配置的几个关键约定manager 字段每个 worker/proxy 节点必须通过$managermanager指向自己的 manager 节点名connect_peer()正是依据该字段定位 manager 的 IP 和端口manager_is_logger当集群中没有独立 logger 节点、由 manager 兼任 logger 时应设置redef Cluster::manager_is_logger T;其默认值在 main.zeek 中即为T并注明仅当Cluster::nodes中没有 logger 时才应为真ZeekControl 会自动处理该值端口来源测试中端口来自TEST-PORT注入的环境变量如BROKER_WORKER1_PORT生产环境中则通常是固定端口或由 ZeekControl/systemd-generator 生成$p 0/unknown的含义根据 types.zeek 注释端口为0/unknown表示该节点不预先配置监听——这在 main.zeek 中体现为跳过Broker::listen()调用。Cluster::nodes表本身定义于 集群框架核心以节点名如manager、worker-1为索引。节点启动流程zeek_init 中的三件事Broker 后端在zeek_init()事件中完成全部启动工作main.zeek该处理器被赋予priority-10。源码注释解释了优先级选择的深意订阅设置处理器运行在优先级 -5先于本处理器执行而 -10 的低优先级同时允许用户在zeek_init()中临时改动 cluster-layout 用于测试。启动流程按顺序分为三部分1. 前置检查if ( getenv(ZEEKCTL_CHECK_CONFIG) ! ) return; if ( ! Cluster::is_enabled() ) return; if ( Cluster::backend ! Cluster::CLUSTER_BACKEND_BROKER ) return;ZEEKCTL_CHECK_CONFIG环境变量存在时直接跳过这是 ZeekControl 做配置检查时的通道Cluster::is_enabled()的实现非常朴素return (node ! )main.zeek即CLUSTER_NODE环境变量对应Cluster::node非空即视为集群运行后端类型不匹配例如同时加载了其他后端则直接返回保证互斥。2. 日志订阅与监听switch ( self$node_type ) { case LOGGER: Cluster::subscribe(Broker::default_log_topic_prefix); break; case MANAGER: if ( Cluster::manager_is_logger ) Cluster::subscribe(Broker::default_log_topic_prefix); break; } if ( self$p ! 0/unknown ) { Broker::listen(Broker::default_listen_address, self$p, Broker::default_listen_retry); Cluster::log(fmt(listening on %s:%s, Broker::default_listen_address, self$p)); }订阅规则任何处理日志的节点额外订阅Broker::default_log_topic_prefix——LOGGER 节点无条件订阅MANAGER 仅在manager_is_logger为真即 manager 兼任 logger时订阅。该前缀的默认值为zeek/logs/broker/main.zeek。监听规则只要布局中给本节点定义了端口$p ! 0/unknown就调用Broker::listen()。监听地址使用Broker::default_listen_address其默认值来自ZEEK_DEFAULT_LISTEN_ADDRESS环境变量broker/main.zeek重试间隔使用Broker::default_listen_retry。这印证了文档logger、manager 和 proxy 节点都在监听 cluster layout 定义的端口的论断——只有这些被连接方需要$p。3. 按节点类型发起对等连接如上一节表格所示manager/proxy/worker 分别调用connect_peers_with_type(LOGGER)、connect_peer(MANAGER, self$manager)等函数完成出站连接。这两个函数是后端连接的核心实现main.zeekfunction connect_peer(node_type: NodeType, node_name: string) { local nn nodes_with_type(node_type); for ( i in nn ) { local n nn[i]; if ( n$name ! node_name ) next; if ( ! hook connect_node_hook(n) ) return; local status Broker::peer(cat(n$node$ip), n$node$p, Cluster::retry_interval); Cluster::log(fmt(initiate peering with %s:%s, retry%s, status%s, n$node$ip, n$node$p, Cluster::retry_interval, status)); return; } Reporter::warning(fmt(connect_peer: node %s (%s) not found, node_name, node_type)); }实现细节值得注意nodes_with_type()定义于 集群框架核心从Cluster::nodes中筛出指定类型的节点并按名称排序返回保证连接顺序确定connect_node_hook是用户可干预的扩展点这是 Cluster::connect_node_hook 钩子仅对 Broker 后端生效hook调用返回假被 break时该节点会被跳过从而允许用户阻止特定连接建立Broker::peer()是实际建立对等连接的调用重试间隔使用Cluster::retry_interval——默认1sec且可被ZEEK_DEFAULT_CONNECT_RETRY单位为秒环境变量覆盖main.zeek每次发起连接都会写入Cluster::log流对应cluster.log日志文件流定义见 main.zeek目标节点在布局中不存在时会通过Reporter::warning发出警告——这是排查布局拼写错误时非常有用的信号。节点生命周期hello / node_up / node_downBroker 后端的节点发现完全基于事件驱动由两个 Broker 事件和三个 Cluster 事件协同完成main.zeekevent Broker::peer_added(endpoint: Broker::EndpointInfo, msg: string) priority10 { if ( ! Cluster::is_enabled() ) return; local e Broker::make_event(Cluster::hello, node, Cluster::node_id()); Broker::publish(nodeid_topic(endpoint$id), e); } event Broker::peer_lost(endpoint: Broker::EndpointInfo, msg: string) priority10 { for ( node_name, n in nodes ) { if ( n?$id n$id endpoint$id ) { event Cluster::node_down(node_name, endpoint$id); break; } } }完整的握手链路如下建连节点 A 通过Broker::peer()与节点 B 建立 Broker 对等连接自我介绍B 端触发Broker::peer_added随即通过Broker::publish把Cluster::hello事件发布到nodeid_topic(endpoint$id)——即zeek/cluster/nodeid/id/前缀对应的主题前缀定义见 main.zeek主题构造函数见 main.zeek。Cluster::hello携带两个参数节点名Cluster::node和节点标识Cluster::node_id()默认即Broker::node_id可被其他后端重定义上报在线收到hello的对端在 Cluster::hello 处理器 中校验节点名是否存在于Cluster::nodes不在则报错记录节点id首次见到该 id 时触发本地Cluster::node_up(name, id)事件并将 id 登记进按节点类型分组的active_node_ids集合——该集合是Cluster::get_active_node_count()main.zeek的数据来源manager 可据此统计各类型节点的实际存活数量上报离线连接断开时对端触发Broker::peer_lost在Cluster::nodes中按id反查节点名触发Cluster::node_down(node_name, id)node_down 处理器 会清除节点的id并从active_node_ids移除该 id。这套机制保证了节点标识id是短暂有效的节点重启后重新连接时会获得新的 id旧连接通过node_down清理新连接通过hello/node_up重新登记。发布-订阅可见性直接对等连接是硬边界官方文档在末尾特意强调了一个容易被忽略的重要限制publish-subscribe visibility with Broker is limited to nodes that are directly peered. A worker publishing a message to a topic another worker node is subscribed to will not be visible by the other worker.即Broker 的发布-订阅可见性仅限于直接互连的节点。由于 worker 之间不直接对等连接见固定连接策略一个 worker 向另一个 worker 订阅的主题发布消息另一个 worker 是看不到的。如果业务逻辑依赖跨 worker 的 pub/sub 通信必须通过 manager 或 proxy 中转或者改用其他具备转发能力的后端。这个限制是固定拓扑策略的直接推论也是设计集群内通信机制时务必牢记的前提。相关配置参数速查以下是 Broker 后端涉及的全部核心参数均可在布局文件或节点配置中调整参数默认值作用定义位置Cluster::backendCLUSTER_BACKEND_NONE选择集群后端init-bare.zeekCluster::nodegetenv(CLUSTER_NODE)当前节点名称cluster/main.zeekCluster::nodes{}集群布局表cluster/main.zeekCluster::manager_is_loggerTmanager 是否兼任 loggercluster/main.zeekCluster::retry_interval1sec连接重试间隔可被ZEEK_DEFAULT_CONNECT_RETRY覆盖cluster/main.zeekBroker::default_listen_addressgetenv(ZEEK_DEFAULT_LISTEN_ADDRESS)监听地址broker/main.zeekBroker::default_log_topic_prefixzeek/logs/日志主题前缀broker/main.zeek测试与验证仓库的测试基础设施完整覆盖了 Broker 后端的启动与对等连接场景测试用 cluster-layout.zeek 展示了通过TEST-PORT环境变量注入端口的完整布局构建方式可直接作为自定义集群布局的参考模板start-it-up.zeek 与 start-it-up-logger.zeek 验证了包含/不包含独立 logger 两种拓扑下集群的启动流程topic_distribution.zeek 等测试覆盖了主题分发机制可用于理解 pub/sub 路由在固定拓扑下的实际行为。小结Broker 集群后端是理解 Zeek 集群通信机制的理想起点它的固定对等连接策略简单而明确节点发现流程完全事件化布局定义集中在一个cluster-layout.zeek文件中。掌握本文所述的三条连接规则、zeek_init启动三部曲订阅、监听、连接以及hello/node_up/node_down生命周期你就能够读懂任何现有 Zeek 集群的 Broker 配置也能为自己的集群编写正确、可维护的布局文件——同时时刻记得发布-订阅可见性仅限直接互连节点这一关键边界。赞分享网络安全网络IDS【免费下载链接】zeekZeek is a powerful network analysis framework that is much different from the typical IDS you may know.项目地址https://gitcode.com/gh_mirrors/ze/zeek点击查看免费下载相关推荐Zeek 集群 Broker 后端policy/frameworks/cluster/backend/broker源码级解析对等连接策略、背压处理与遥测指标Zeek 集群 Broker 后端policy/frameworks/cluster/backend/broker源码级解析对等连接策略、背压处理与遥测指网络安全网络IDSZeek 集群 Broker 后端深度解析节点互联策略、backpressure 与遥测监控Zeek 集群 Broker 后端深度解析节点互联策略、backpressure 与遥测监控 Broker 集群后端是 Zeek 集群框架中历史最悠久、使用最网络安全网络IDSZeek Cluster 框架深度解析集群布局、节点角色与核心 API 实战指南Zeek Cluster 框架深度解析集群布局、节点角色与核心 API 实战指南 导读 本文以 Zeek 仓库中集群框架的核心文档 doc/scripts/b网络安全网络IDS创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考