- 网络安全
- 网络
- IDS
【免费下载链接】zeek
Zeek is a powerful network analysis framework that is much different from the typical IDS you may know.
本文基于 Zeek 仓库中 doc/scripts/base/frameworks/netcontrol/shunt.zeek.rst 及 shunt.zeek 源码,深入讲解 NetControl 框架的 shunt(分流)功能。shunt 用于告诉网络设备"停止把某个单向流的报文继续转发给 Zeek",从而让 Zeek 在识别出良性流量后节省分析资源。读完本文,你将掌握NetControl::shunt_flow的完整签名与参数语义、NetControl::ShuntInfo日志记录结构、netcontrol_shunt日志流的字段含义,并能写出可运行的 shunt 触发脚本,理解其底层规则机制与测试验证方式。
NetControl 框架中的 Shunt 定位
Zeek 的 NetControl 框架(见 doc/frameworks/netcontrol.rst)是一个基于插件的流量控制框架:它既能控制发往 Zeek 自身的"监控路径",也能(在具备转发路径访问权限时)控制网络设备的"转发路径"。默认情况下框架放行所有流量,脚本可以添加规则对特定实体施加限制。
Shunt 正是框架在监控路径上的典型应用。框架类型定义(types.zeek)将规则目标分成两类:
NetControl::FORWARD:把规则主动应用到网络转发路径上,影响所有网络流量;NetControl::MONITOR:把规则被动应用到发往 Zeek 的监控流量上。
shunt_flow创建的是target=MONITOR的规则,含义即"Zeek 告诉网络硬件,它不想再看到已被判定为良性的流量"。这与 drop 系列函数(target=FORWARD,阻断转发)形成鲜明对照:shunt 不改变网络中报文的去向,只是让 Zeek 不再接收它们。
官方框架文档对shunt_flow的定位是:
Calling this function causes NetControl to stop forwarding a uni-directional flow of packets to Zeek. This allows Zeek to conserve resources by shunting flows that have been identified as being benign.
shunt_flow:接口签名与参数语义
shunt_flow在 shunt.zeek 中声明,是 NetControl 高层 API 中专门面向单向流的一个入口:
global shunt_flow: function(f: flow_id, t: interval, location: string &default="") : string;| 参数 | 类型 | 说明 |
|---|---|---|
f | flow_id | 要被 shunt 的单向流,包含源/目的 IP 与端口 |
t | interval | shunt 保留的时间;0表示无限期保留 |
location | string(可选,默认"") | 描述 shunt 触发位置的字符串 |
返回值:成功时返回插入规则的 ID 字符串;失败时返回空字符串("")。注意返回值语义与add_rule保持一致:所谓"成功"指有插件接受了这条规则,并不保证硬件层面已真正生效,因为硬件下发是异步的(见 main.zeek)。
参数细节
- flow_id 与单向流:
flow_id是 Zeek 内建类型,由src_h/src_p与dst_h/dst_p(外加可选的 MAC 字段)构成。shunt 针对的是单向流,因此调用方应传入对应方向(如 orig 方向或 resp 方向)的四元组。 - expire 语义:
t即规则过期时间。传入0sec表示不过期,规则会一直保留直到被显式remove_rule;传入具体时长则到期后规则超时,NetControl 会通过rule_timeout事件通知插件进行清理(参见 main.zeek)。 - location 的审计价值:该字段会被写入日志,用于追溯"是哪段脚本/哪个策略触发了这次 shunt",在多脚本协同的部署中尤其重要。
源码实现:shunt_flow 内部发生了什么
shunt_flow的实现位于 shunt.zeek,核心流程分为三步:构造 Flow 实体 → 构造 DROP/MONITOR 规则 → 调用add_rule并写日志。
function shunt_flow(f: flow_id, t: interval, location: string &default="") : string { local flow = NetControl::Flow( $src_h=f$src_h as subnet, $src_p=f$src_p, $dst_h=f$dst_h as subnet, $dst_p=f$dst_p ); local e = Entity($ty=FLOW, $flow=flow); local r = Rule($ty=DROP, $target=MONITOR, $entity=e, $expire=t, $location=location); local id = add_rule(r); # Error should already be logged if ( id == "" ) return id; local log = ShuntInfo($ts=network_time(), $rule_id=id, $f=f, $expire=t); if ( location != "" ) log$location=location; Log::write(SHUNT, log); return id; }关键实现细节
- 地址升格为子网:
f$src_h as subnet与f$dst_h as subnet把 IP 地址转成/32(或/128)子网。这是因为 NetControl 的Flow类型(types.zeek)中src_h/dst_h声明为subnet,未设置的字段会被解释为通配符。也就是说,你可以手动构造带有通配符的NetControl::Flow来实现对整段网段的 shunt。 - 实体类型:
Entity($ty=FLOW, ...)对应EntityType枚举中的FLOW——"涉及单向流活动的实体"(types.zeek)。 - 规则类型:
Rule($ty=DROP, $target=MONITOR, ...)。DROP规则类型表示"停止转发匹配实体的所有报文"(types.zeek),配合MONITOR目标即实现"不再把该流送给 Zeek"的语义。 - 失败即短路:
add_rule返回空串表示没有插件接受规则(错误已由框架写入 netcontrol 日志),此时直接返回,不再写 shunt 日志。 - 日志只记成功:只有规则被接受后才构造
ShuntInfo并调用Log::write(SHUNT, log),保证netcontrol_shunt.log中的每行都对应一次成功的 shunt。
规则生命周期与优先级
规则加入框架后由 main.zeek 全生命周期跟踪:rule_new在add_rule时触发,rule_added/rule_exists/rule_timeout/rule_error/rule_removed分别汇报各阶段状态,rule_destroyed表示规则彻底退出跟踪。多条规则同时命中同一实体时,按priority排序取第一条(priority越大优先级越高,默认值由NetControl::default_priority决定,见 types.zeek)。若希望 shunt 规则能压过已有的低优先级 drop 规则,可以手动构造更高优先级的规则;高层shunt_flow本身使用默认优先级。
netcontrol_shunt 日志:ShuntInfo 记录结构
每次成功的 shunt 都会写入netcontrol_shunt日志流。日志列由ShuntInforecord 定义(shunt.zeek),各字段均带&log属性:
| 字段 | 类型 | 说明 |
|---|---|---|
ts | time | 记录活动发生的时间 |
rule_id | string | 规则 ID;在一次 Zeek 运行内唯一 |
f | flow_id | 被 shunt 的流的 ID(四元组) |
expire | interval | shunt 的过期时长 |
location | string(可选) | 触发底层动作的位置描述 |
日志流在zeek_init(优先级 5)中注册(shunt.zeek):
event zeek_init() &priority=5 { Log::create_stream(NetControl::SHUNT, Log::Stream($columns=ShuntInfo, $ev=log_netcontrol_shunt, $path="netcontrol_shunt", $policy=log_policy_shunt)); }$path="netcontrol_shunt"决定日志文件名,即netcontrol_shunt.log。$ev=log_netcontrol_shunt绑定事件,任何脚本都可以订阅它来在记录落地前访问ShuntInforecord。$policy=log_policy_shunt绑定策略钩子NetControl::log_policy_shunt(Log::PolicyHook),可在写日志前增删改字段或过滤记录。
可订阅的事件与钩子
event NetControl::log_netcontrol_shunt(rec: NetControl::ShuntInfo):日志写往框架前触发,用于统一加工或转发 shunt 记录。hook NetControl::log_policy_shunt(rec: Log::PolicyHook, rec: any)(类型为Log::PolicyHook):标准的日志策略钩子,可用break阻止该条记录落盘,或直接改写rec字段。
下面的示例演示如何利用策略钩子为 shunt 日志附加过滤(例如只保留 location 非空的记录):
hook NetControl::log_policy_shunt(rec: NetControl::ShuntInfo) { if ( ! rec?$location ) break; # 丢弃没有 location 信息的 shunt 记录 }实战:在脚本中触发一条 shunt
要实际触发 shunt,至少需要一个已激活的 NetControl 后端(插件)。最简单的后端是 debug 插件(plugins/debug.zeek),它把所有动作打印到标准输出。下面是一个完整可运行的示例,参照官方集成测试 testing/btest/scripts/base/frameworks/netcontrol/basic.zeek 的结构编写:
@load base/frameworks/netcontrol event NetControl::init() { local netcontrol_debug = NetControl::create_debug(T); NetControl::activate(netcontrol_debug, 0); } event NetControl::init_done() &priority=-5 { # 把 192.168.17.1:32/tcp -> 192.168.17.2:32/tcp 的单向流 shunt 30 秒 NetControl::shunt_flow( [$src_h=192.168.17.1, $src_p=32/tcp, $dst_h=192.168.17.2, $dst_p=32/tcp], 30sec, "example-script" ); }运行zeek basic.zeek后,debug 插件会打印规则内容;同时生成netcontrol_shunt.log。官方测试的基线文件 testing/btest/Baseline/scripts.base.frameworks.netcontrol.basic/netcontrol_shunt.log 展示了真实输出结构:
#fields ts rule_id f.src_h f.src_p f.dst_h f.dst_p expire location #types time string addr port addr port interval string XXXXXXXXXX.XXXXXX 2 192.168.17.1 32 192.168.17.2 32 30.000000 -可见f字段被展开为f.src_h/f.src_p/f.dst_h/f.dst_p四列;本例未传location,因此取值为-(unset)。
测试验证:shunt 功能如何被自动化检验
Zeek 仓库用 btest 对 shunt 功能做了端到端验证。测试 testing/btest/scripts/base/frameworks/netcontrol/basic.zeek 在NetControl::init_done中依次调用shunt_flow、drop_address、whitelist_address、redirect_flow、quarantine_host等高层函数,并对比netcontrol.log、netcontrol_shunt.log、netcontrol_drop.log与标准输出。其测试头注释:
# @TEST-EXEC: zeek -b %INPUT # @TEST-EXEC: btest-diff netcontrol.log # @TEST-EXEC: btest-diff netcontrol_shunt.log- 测试先通过
NetControl::create_debug(T)激活 debug 后端(T表示接受所有操作); - 再调用
shunt_flow([$src_h=192.168.17.1, $src_p=32/tcp, $dst_h=192.168.17.2, $dst_p=32/tcp], 30sec); - 基线文件确认
netcontrol_shunt.log中只有一行成功记录,且rule_id为2(与同一脚本中其他规则按插入顺序递增的 ID 对应)。
这组测试同时验证了:shunt 只影响被指定流、规则被 debug 后端接受、日志字段(尤其是f的四元组展开)序列化正确。
使用注意事项与边界
- 必须有活动后端:
add_rule只有在至少一个插件接受规则时才返回非空 ID。若NetControl::init中未激活任何后端,shunt_flow会静默返回""(错误信息写入 netcontrol 日志),netcontrol_shunt.log中不会有对应记录。 - 成功≠已生效:与框架整体语义一致,返回值只代表"有插件接管了规则",硬件层面的实际下发是异步的;如需精确状态,应订阅
rule_added/rule_error等事件(main.zeek)。 - 单向流语义:
flow_id是单向的,若要屏蔽双向流量需要分别对两个方向调用shunt_flow(或改用面向连接的drop_connection)。 - 资源回收:
t传0表示永久 shunt,需要后续显式调用NetControl::remove_rule(id)才能撤销,注意避免规则表无限增长;固定时长则到期自动清理。 - location 是可选的审计线索:在大型策略集中建议始终传入,便于从日志反查触发点。
延伸阅读
- 框架整体架构与高层 API 总览:doc/frameworks/netcontrol.rst(其中
shunt_flow被列为与drop_address、redirect_flow、quarantine_host并列的高层函数) - 同系列实现对比:drop.zeek(
DROP/FORWARD语义)、main.zeek(规则生命周期与add_rule低层 API) - 核心类型定义:types.zeek(
Flow、Entity、Rule、TargetType、RuleType) - 后端插件示例:plugins/debug.zeek
- 官方集成测试:testing/btest/scripts/base/frameworks/netcontrol/basic.zeek 及其 netcontrol_shunt.log 基线
- 网络安全
- 网络
- IDS
【免费下载链接】zeek
Zeek is a powerful network analysis framework that is much different from the typical IDS you may know.
相关推荐
Zeek NetControl DROP 功能实现解析:drop_address / drop_connection 的规则构建、netcontrol_drop 日志与策略钩子
Zeek NetControl DROP 功能实现解析:drop_address / drop_connection 的规则构建、netcontrol_drop
网络安全网络IDSZeek NetControl 框架深入解析:从 `__load__.zeek` 入口理解规则驱动的网络流量控制体系
Zeek NetControl 框架深入解析:从 __load__.zeek 入口理解规则驱动的网络流量控制体系 NetControl 是 Zeek 内置的插件
网络安全网络IDSZeek dns.log 深度解析:DNS 日志字段、查询流程与实战分析方法
Zeek dns.log 深度解析:DNS 日志字段、查询流程与实战分析方法 导读 dns.log 是 Zeek 生成的最重要数据源之一,它记录了网络内每一笔应
网络安全网络IDS
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考