
网络安全网络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 集群框架Cluster framework的 WebSocket 接入能力深入讲解两个核心生命周期事件Cluster::websocket_client_added与Cluster::websocket_client_lost。这两个事件由 Zeek 内置插件Zeek_Cluster_WebSocket提供分别在外部 WebSocket 客户端成功接入完成订阅和客户端断开时触发是构建基于 WebSocket 的 Zeek 集群外部控制面、监控客户端连接状态与排查断连问题的关键入口。读完本文你将掌握这两个事件的完整签名、参数语义、底层触发链路、默认日志行为以及如何在 Zeek 脚本中订阅它们并配合Cluster::listen_websocket()搭建可观测的 WebSocket 服务器。背景Cluster 框架与 WebSocket 接入Zeek 的集群框架允许把多个 Zeek 实例组织成 worker / proxy / manager / logger 的拓扑协同工作。传统上集群节点之间通过 Broker 库通信而Cluster::backend机制则提供了可插拔的通信后端例如 Broker 与 ZeroMQ。在此基础上Zeek 内置的Zeek_Cluster_WebSocket插件允许把集群节点本身暴露为一个 WebSocket 服务器让外部工具如 Web 客户端、脚本或管理端通过标准 WebSocket 协议接入集群外部客户端连接服务器、在握手handshake中携带订阅的 topic 列表服务器端把这些订阅注册到集群后端backend上使客户端能够接收集群中发布到这些 topic 的消息客户端也可以通过 WebSocket 连接向集群发送事件消息发布到 topic。这两个生命周期事件就定义在这个插件对应的 BIF 声明中src/cluster/websocket/events.bif命名空间为Cluster与GLOBAL事件触发时机Cluster::websocket_client_added一个新的 WebSocket 客户端完成连接并成功订阅后Cluster::websocket_client_lost一个 WebSocket 客户端断开连接时事件速览事件类型签名说明Cluster::websocket_client_addedevent(endpoint: Cluster::EndpointInfo, subscriptions: string_vec)生成于一个新的 WebSocket 客户端连接成功之后Cluster::websocket_client_lostevent(endpoint: Cluster::EndpointInfo, code: count, reason: string)生成于一个 WebSocket 客户端丢失断开之时两个事件都以Cluster::EndpointInfo描述客户端核心差异在于第二个事件携带关闭码code与关闭原因reason用于区分正常关闭、协议错误、服务端主动断开等不同情形。Cluster::websocket_client_added客户端接入事件签名与参数event Cluster::websocket_client_added(endpoint: Cluster::EndpointInfo, subscriptions: string_vec)参数类型含义endpointCluster::EndpointInfo关于该 WebSocket 客户端的各种信息标识、网络地址、可选的应用名subscriptionsstring_vec客户端在握手handshake阶段提供的订阅 topic 列表EndpointInfo 与 NetworkInfo 类型该事件使用的endpoint参数类型定义于 scripts/base/frameworks/cluster/types.zeektype NetworkInfo: record { address: string; # 端点监听的 IP 地址或主机名 bound_port: port; # 端点绑定的端口 }; type EndpointInfo: record { id: string; # 客户端的唯一标识 network: NetworkInfo; # 网络信息 application_name: string optional; # X-Application-Name HTTP 头的值若存在 };其中id是后端backend为该客户端生成的节点标识NodeId可用于在集群日志中追踪特定客户端network由make_endpoint_info()填充包含客户端的远端 IPgetRemoteIp()与远端端口getRemotePort()传输类型固定为 TCP见 src/cluster/websocket/WebSocket.ccapplication_name是可选的取自己在 HTTP 握手时携带的X-Application-Name请求头方便区分不同类型的接入方。触发链路订阅激活之后才上报该事件并非在 TCP 连接建立或 HTTP 握手完成的瞬间触发而是在客户端订阅全部生效后才触发。从源码实现看触发路径位于WebSocketEventDispatcher::HandleSubscriptionsActive()src/cluster/websocket/WebSocket.cc客户端完成连接后服务器将其握手中的订阅逐一注册到集群后端entry.backend-Subscribe(...)每个 topic 的订阅结果返回后标记SetSubscriptionActive()直到所有订阅均激活AllSubscriptionsActive()同时等待后端进入可发布状态WebSocketBackendReadyToPublish二者齐备后调用HandleSubscriptionsActive()该函数构造EndpointInfo使用后端的NodeId()与客户端的远端地址/端口与订阅向量GetSubscriptions()然后通过zeek::event_mgr.Enqueue(Cluster::websocket_client_added, ...)入队触发事件紧接着向客户端发送 ACKSendAck(entry.backend-NodeId(), zeek::zeek_version())告知客户端接入成功以及服务端 Zeek 版本。也就是说事件触发时该客户端已经具备收发集群消息的能力订阅列表是握手后实际生效的集合。如果某个订阅在后端注册失败服务器会直接以关闭码1011内部错误关闭该连接而不会触发websocket_client_added见 src/cluster/websocket/WebSocket.cc。框架自带的默认处理框架在 scripts/base/frameworks/cluster/main.zeek 中为websocket_client_added提供了默认 handlerevent websocket_client_added(endpoint: EndpointInfo, subscriptions: string_vec) { local msg fmt(WebSocket client %s subscribed to %s, format_endpoint_info(endpoint), subscriptions); Cluster::log(msg); }其中format_endpoint_info()scripts/base/frameworks/cluster/main.zeek会把端点格式化为id (address:port)若存在application_name则追加输出。日志写入Cluster::LOG流对应的cluster.log默认列包含ts、node、message定义见 scripts/base/frameworks/cluster/main.zeek。因此即使不编写任何自定义逻辑只要集群框架已加载每次客户端接入都会在cluster.log留下类似WebSocket client nodeid (127.0.0.1:54321) subscribed to /zeek/event/my_topic, ...的记录。你可以通过load base/frameworks/cluster并设置CLUSTER_NODE环境变量或 ZeekControl 自动处理启用该框架见 scripts/base/frameworks/cluster/main.zeek。Cluster::websocket_client_lost客户端断开事件签名与参数event Cluster::websocket_client_lost(endpoint: Cluster::EndpointInfo, code: count, reason: string)参数类型含义endpointCluster::EndpointInfo关于该 WebSocket 客户端的各种信息codecount客户端在 CLOSE 帧中发送的关闭码若服务器主动断开客户端则为服务器内部生成的关闭码reasonstring客户端在 CLOSE 帧中发送的关闭原因若服务器主动断开则为服务器内部生成的原因关闭码code的语义code遵循 WebSocket 协议标准的关闭码约定常见取值包括关闭码含义1000正常关闭normal closure1001端点离开如服务器关闭、客户端跳转页面1002协议错误1003收到不支持的数据类型1008违反策略1011服务器内部错误1015TLS 握手失败客户端侧使用不可在线发送框架内部在订阅失败等场景使用1011主动断开客户端见 src/cluster/websocket/WebSocket.cc而客户端不响应 PING 导致服务器断开时则由服务器生成内部 code 与 reason。需要注意code的取值取决于对端客户端或服务端的实际实现上述表格是 WebSocket 标准的通用约定可作为解读依据但不要假设具体实现一定会使用某个码。触发链路后端终止之后上报websocket_client_lost的触发路径位于WebSocketEventDispatcher::Close()src/cluster/websocket/WebSocket.cc服务器收到客户端的 CLOSE 帧或内部决定断开该客户端时先调用backend-Terminate()终止该客户端对应的后端实例关键设计在确认后端终止完成后才入队websocket_client_lost事件。源码注释明确指出这是为了确保websocket_client_lost是与该客户端相关的最后一个事件——即使此前还有排队中的消息/事件未处理也不会出现在该事件之后见 src/cluster/websocket/WebSocket.cc事件参数中code取自关闭帧的close.codereason取自close.reason若服务器主动断开则使用内部生成的值最后从clients表中移除该客户端条目。框架自带的默认处理同样地scripts/base/frameworks/cluster/main.zeek 中提供了默认 handler将客户端断开信息写入cluster.logevent websocket_client_lost(endpoint: EndpointInfo, code: count, reason: string) { local msg fmt(WebSocket client %s gone with code %d%s, format_endpoint_info(endpoint), code, |reason| 0 ? fmt( and reason %s, reason) : ); Cluster::log(msg); }当reason为空字符串时不追加原因输出避免日志中出现冗余的and reason 。实战监听客户端接入与断连在实际部署中通常需要自定义 handler 来感知客户端生命周期。下面是一个参照官方测试用例testing/btest/cluster/websocket/one.zeek的完整示例# 启动 WebSocket 服务器并监听客户端生命周期 event zeek_init() { # 监听本机指定端口生产环境可改为实际监听地址 Cluster::subscribe(/zeek/event/my_topic); Cluster::listen_websocket([$listen_addr127.0.0.1, $listen_port27759/tcp]); } event Cluster::websocket_client_added(info: Cluster::EndpointInfo, subscriptions: string_vec) { print fmt(client added: %s (%s:%d) application%s, info$id, info$network$address, info$network$bound_port, info?$application_name ? info$application_name : (none)); print fmt(subscriptions: %s, subscriptions); } event Cluster::websocket_client_lost(info: Cluster::EndpointInfo, code: count, reason: string) { print fmt(client lost: %s code%d reason%s, info$id, code, reason); }要点说明必须同时调用Cluster::subscribe(...)或依赖既有订阅配置与Cluster::listen_websocket(...)前者让本节点具备可订阅的 topic后者启动监听listen_websocket()是 scripts/base/frameworks/cluster/main.zeek 中对 BIFCluster::__listen_websocket(options: WebSocketServerOptions): bool声明于 src/cluster/cluster.bif的脚本层封装订阅列表通常用于校验/记录客户端意图例如判断某客户端是否订阅了管理员专用 topic再决定是否授权其后续消息。配置 WebSocket 服务器选项Cluster::listen_websocket()接受Cluster::WebSocketServerOptions记录定义见 scripts/base/frameworks/cluster/types.zeek字段类型默认值说明listen_addraddr可选无监听地址与listen_host互斥listen_portport必填WebSocket 服务器监听端口max_event_queue_sizecount32default_websocket_max_event_queue_size事件派发队列上限达到上限后外部客户端的事件会被阻塞直到队列被排空触发时内部指标cluster_onloop_queue_stalls标签为WebSocketEventDispatcher:host:port会递增ping_intervalinterval5 secdefault_websocket_ping_interval心跳 PING 间隔不响应 PING 的客户端将被断开设为负值可禁用 PING目前不支持亚秒级间隔tls_optionsWebSocketTLSOptions空TLS 关闭TLS 选项WebSocketTLSOptionsscripts/base/frameworks/cluster/types.zeek字段如下字段类型默认值说明cert_filestring可选无证书文件与key_file同时设置则启用 TLS两者均未设置则禁用 TLS其余组合视为错误key_filestring可选无私钥文件enable_peer_verificationboolF是否要求对端提供客户端证书ca_filestring用于对端验证的 CA 证书或 CA 包为空时在enable_peer_verificationT时使用实现默认值ciphersstring使用的密码套件为空时使用实现默认值独立 WebSocket 服务器脚本仓库还提供了一键启动独立 WebSocket 服务器的策略脚本 scripts/policy/frameworks/cluster/websocket/server.zeek。其行为要点默认监听127.0.0.1:27759/tcp可通过环境变量ZEEK_WEBSOCKET_LISTEN_ADDRESS、ZEEK_WEBSOCKET_LISTEN_PORT覆盖加载该脚本时若Cluster::backend还是CLUSTER_BACKEND_NONE会自动加载 ZeroMQ 集群后端并启动本地的 XPUB/XSUB 代理线程若希望改用 Broker 的 hub 功能则需先加载policy/frameworks/cluster/backend/broker若Cluster::backend为CLUSTER_BACKEND_NONE但Cluster::nodes已填充条目脚本会报错并退出该脚本定位是临时测试用在 ZeekControl 环境中应使用UseWebSocket选项代替见 scripts/policy/frameworks/cluster/websocket/server.zeek。测试用例与验证方式仓库中的 btest 测试覆盖了这两个事件的多种场景是验证行为和编写自定义脚本时最好的参考测试文件验证重点testing/btest/cluster/websocket/one.zeek单节点 manager WebSocket 服务器 单个客户端连接客户端接入后收到websocket_client_added断开时收到websocket_client_losttesting/btest/cluster/websocket/ping-timeout.zeek专门验证websocket_client_lost携带code与reason两个不响应 PING 帧的客户端被服务器断开testing/btest/cluster/websocket/cluster-log.zeek验证默认 handler 对两个事件的cluster.log输出testing/btest/cluster/websocket/server/broker.zeek、server/ipc.zeek、server/default.zeek验证独立 WebSocket 服务器脚本在 Broker / IPC / 默认后端下的行为testing/btest/cluster/websocket/bad-subscriptions.zeek、bad-url.zeek、bad-event-args.zeek异常场景订阅失败、URL 非法、事件参数错误等testing/btest/cluster/websocket/tls.zeek、tls-nocert.zeekTLS 场景下客户端接入/断开仍能正确触发两个事件testing/btest/cluster/websocket/one-ipv6.zeekIPv6 环境下的客户端生命周期例如 testing/btest/cluster/websocket/ping-timeout.zeek 中的 handler 会累计lost计数当两个客户端都断开后调用terminate()结束测试同时其测试注释明确要求断言websocket_client_lost()事件包含 code 和 reason 两个参数。运行此类测试的常规方式需要已构建的 Zeek 与websocketsPython 库cd testing/btest btest -d cluster/websocket常见场景与注意事项接入即订阅websocket_client_added的subscriptions是握手时声明、且已成功注册到后端的集合。若客户端未声明任何订阅或订阅注册失败事件不会触发后者会以 1011 关闭。断连时序保证websocket_client_lost永远是该客户端相关事件中最后触发的一个因为后端终止完成之后才入队。在 handler 中做清理如释放与客户端绑定的状态是安全的。区分关闭来源code/reason若来自客户端 CLOSE 帧则为客户端提供的内容若服务器主动断开如 PING 超时、订阅失败、内部错误则是服务器生成的值。结合application_name与id可以精确识别是哪个接入方异常。心跳与队列默认 5 秒 PING 间隔、32 的事件队列上限均可通过WebSocketServerOptions调整。对高吞吐外部接入方应结合实际负载评估max_event_queue_size与ping_interval。TLS 配置约束cert_file与key_file必须同时设置或同时不设置否则报错启用对端证书验证enable_peer_verification时可通过ca_file指定信任的 CA。小结Cluster::websocket_client_added与Cluster::websocket_client_lost构成了 Zeek 集群 WebSocket 接入的完整生命周期视图前者在客户端订阅全部生效、具备收发能力时通知你谁来了、订阅了什么后者在后端清理完毕、事件流收尾时通知你谁走了、为什么走。配合默认的cluster.log记录、EndpointInfo的丰富元数据以及 scripts/base/frameworks/cluster/types.zeek 中可调的服务器选项你可以低成本地构建客户端接入审计、异常断连告警与多后端 WebSocket 网关。相关源码入口BIF 声明、事件入队实现、框架默认 handler、独立服务器脚本。赞分享网络安全网络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点击查看免费下载相关推荐FAST SSR组件生命周期服务端与客户端的差异处理FAST SSR组件生命周期服务端与客户端的差异处理 你是否在开发FAST应用时遇到过服务端渲染 SSR 与客户端激活 hydration 的行为不一致表单前端UI组件gh_mirrors/api1/api 事件系统详解RequestWasMatched 与 Response 生命周期gh_mirrors/api1/api 事件系统详解RequestWasMatched 与 Response 生命周期 作为一个为 Laravel 和 Lum后端API网关Kubernetes容器生命周期事件处理机制详解Kubernetes容器生命周期事件处理机制详解 概述 在Kubernetes生态系统中容器生命周期管理是确保应用高可用性和可靠性的核心机制。Kubernet文档教程云原生上一篇Slidev 配置 Vue 应用setup/main.ts 与 defineAppSetup 扩展机制详解下一篇为什么选择txt.wav5大理由让你的文字动起来创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考