☰
Zeek 常用定制化与功能扩展实践:Community ID、GeoIP 地理定位、Kafka 日志输出与调试工具指南
2026/9/27 23:39:23 网站建设 项目流程
  • 网络安全
  • 网络
  • IDS

【免费下载链接】zeek

Zeek 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 官方文档中"Popular Customizations"章节展开,系统讲解 Zeek 社区中最常用的四类扩展方向:通过 Community ID 流哈希与 GeoIP/ASN 查询增强日志信息、通过 Kafka 与 JSON 流式日志外送、通过长连接日志补齐检测短板,以及借助 jemalloc profiling 定位内存问题。读完本文,你将掌握每项功能的启用方式、配置要点、命令行验证手段,并理解其底层实现原理与源码位置,可直接照搬到自己的 Zeek 部署中。

需要先说明的是,本文涉及的多数扩展来自 Zeek 生态中的第三方包(如 zeek-kafka、json-streaming-logs、zeek-long-connections、zeek-jemalloc-profiling),Zeek 官方团队并不为这些包提供支持与维护;遇到 bug 或功能需求应直接联系对应包的维护者,也可以到 Zeek Slack 的#packages频道或官方论坛向社区求助。下文会区分"Zeek 原生内置功能"与"外部包",便于你判断责任边界。

Log Enrichment:让日志携带更多上下文

Community ID 流哈希

**Community ID(社区 ID)**是一种将网络流(flow)的五元组信息稳定哈希为单一字符串的规范,便于在不同安全工具(Zeek、Suricata、ELK 等)之间关联同一连接。自 Zeek 6.0 起,Zeek 已原生内置 Community ID Flow Hashing 支持(此前需要通过zeek-community-id包提供)。

注意:外部zeek-community-id包目前仍可安装,用于支持运行旧版本 Zeek 的部署。但该包提供的脚本与 Zeek 6.0 自带的脚本存在冲突——两者不能同时加载。如果你已升级到 6.0+,请直接使用内置支持。

启用方式与效果

加载以下两个脚本即可:

  • scripts/policy/protocols/conn/community-id-logging.zeek:为Conn::Info记录追加community_id字段(带有&log属性,会写入 conn.log);
  • scripts/policy/frameworks/notice/community-id.zeek:为Notice::Info记录追加community_id字段,且会自动加载上面的 conn 脚本。

实际运行效果如下(读取离线 pcap 并输出 JSON 格式的 conn.log):

$ zeek -r ./traces/get.pcap protocols/conn/community-id-logging LogAscii::use_json=T $ jq < conn.log { "ts": 1362692526.869344, "uid": "CoqLmg1Ds5TE61szq1", "id.orig_h": "141.142.228.5", "id.orig_p": 59856, "id.resp_h": "192.150.187.43", "id.resp_p": 80, "proto": "tcp", ... "community_id": "1:yvyB8h+3dnggTZW0UEITWCst97w=" }

示例中的get.pcap是本地 trace 文件,若需自行获取测试样本,可使用 doc/traces 目录下的get.trace下载脚本。

直接调用内置函数

Community ID 由内置函数community_id_v1计算,签名如下(见 src/communityid.bif):

function community_id_v1(cid: conn_id, seed: count &default=0, do_base64: bool &default=T): string
  • cid:连接标识符(conn_id 记录);
  • seed:16 位无符号哈希种子,默认 0,可用于自定义命名空间以避免与其他组织撞哈希;
  • do_base64:是否对摘要做 Base64 编码,默认开启(输出更短)。

它既能在自定义脚本中使用,也能直接在命令行上针对构造的 conn_id 求值:

$ zeek -e 'print community_id_v1([$orig_h=141.142.228.5, $orig_p=59856/tcp, $resp_h=192.150.187.43, $resp_p=80/tcp])' 1:yvyB8h+3dnggTZW0UEITWCst97w=

这个输出与上面 conn.log 中的community_id完全一致,说明 conn.log 中的字段就是对该连接c$id调用同一函数得到的。

底层实现要点

从 src/communityid.bif 的 C 段实现可以梳理出完整哈希流程:

  1. 协议归一:Zeek 的传输协议别名与底层 IP 协议号并不总是一一对应,代码针对 TCP/UDP 直接映射为IPPROTO_TCP/IPPROTO_UDP,对 ICMP 则按 IPv4/IPv6 分别映射为IPPROTO_ICMP/IPPROTO_ICMPV6;若协议未知(TRANSPORT_UNKNOWN)则直接采用记录中的协议号,且此时不参与端口哈希(SCTP 除外)。
  2. 单/双向 ICMP 判定:通过ICMP4_counterpart/ICMP6_counterpart判断 ICMP 流是否为单向(如 echo request 无对应 reply),单向流同样不哈希端口。
  3. 端点规范化:为保证双向流哈希一致,先按"地址-端口"规范序比较(addr_port_canon_lt),必要时交换源/目的地址与端口,确保无论观察方向如何都得到同一 ID。
  4. SHA-1 摘要:依次将种子(2 字节)、源/目的地址(IPv4 为 4 字节、IPv6 为 16 字节)、协议号、填充字节,以及(若有端口时)源/目的端口送入 SHA-1;最后按do_base64决定输出 Base64 还是十六进制字符串,统一加"1:"版本前缀。
可调参数

在 community-id-logging.zeek 中,CommunityID 模块导出两个 option:

  • CommunityID::seed: count = 0:哈希种子,可重定义;
  • CommunityID::do_base64: bool = T:是否做 Base64 编码。

在 community-id.zeek 中还有一个运行时开关:

  • CommunityID::Notice::enabled: bool = T:控制 Notice 的 community_id 增强是否生效。关闭后community_id字段仍存在于 notice 日志中,只是不被填充。

notice 侧的填充逻辑通过hook Notice::notice实现:从n$conn$conn读取连接记录,若其中已存在community_id则复制到 notice 记录(该值在new_connection事件中由 conn 脚本写入)。

地址地理定位与 AS 查询

Zeek 支持 IP 地址地理定位(GeoIP)以及自治系统(AS,autonomous system)查询。启用该功能需要满足两个前提:

  1. 编译期支持:以libmaxminddb库及其开发头文件编译 Zeek。若你使用的是官方 Docker 镜像或二进制包,则无需任何操作——它们已内置 GeoIP 支持;
  2. 运行时数据库:在系统上安装对应的 MaxMind 数据库文件(.mmdb)。
检查是否支持

自 Zeek 6.2 起可用zeek-config --have-geoip检查,或者直接执行一次地址查询:

$ zeek -e 'lookup_location(1.2.3.4)' error in <command line>, line 1: Zeek was not configured for GeoIP support (lookup_location(1.2.3.4))

出现上述错误即表示当前 Zeek 未编译 GeoIP 支持。

源码编译时安装 libmaxminddb

自行编译 Zeek 时,需要在configure之前安装 libmaxminddb,各平台安装命令如下:

  • RPM/RedHat 系 Linux:

    sudo yum install libmaxminddb-devel
  • DEB/Debian 系 Linux:

    sudo apt-get install libmaxminddb-dev
  • FreeBSD:

    sudo pkg install libmaxminddb
  • macOS:通过你常用的包管理工具安装(Homebrew、MacPorts 或 Fink),Homebrew 下包名为libmaxminddb。

configure脚本的输出会明确提示是否成功定位到 libmaxminddb。如果库安装在非标准路径,可用./configure --with-geoip=<path>显式指定。

安装与配置 GeoIP 数据库

MaxMind 数据库以独立文件形式分发,可在其官网注册账户后下载。数据库分三类:城市级地理定位(city-level)、国家级地理定位(country-level)、IP 到 AS 映射(含 AS 号与所属组织)。下载后放到文件系统的固定位置;若使用自动化工具或系统包安装,路径可能已被指定(如/usr/share/GeoIP)。

Zeek 提供三种配置数据库访问的方式,可任选其一:

方式一:通过脚本变量指定路径与文件名。设置mmdb_dir变量(默认未设置)指向数据库所在目录;Zeek 默认按GeoLite2-City.mmdb、GeoLite2-Country.mmdb、GeoLite2-ASN.mmdb三个文件名查找。自 Zeek 6.2 起,可用mmdb_city_db、mmdb_country_db、mmdb_asn_db三个变量分别重定义文件名。

redef mmdb_dir = "/opt/maxmind"; redef mmdb_city_db = "GeoLite2-City.mmdb"; redef mmdb_asn_db = "GeoLite2-ASN.mmdb";

方式二:依赖预配置的搜索路径与文件名。mmdb_dir_fallbacks变量保存了默认搜索路径,当mmdb_dir未设置时 Zeek 会依次尝试。6.2 之前这些路径是硬编码的,现在可重定义。默认值(见 scripts/base/init-bare.zeek):

const mmdb_dir_fallbacks: vector of string = vector( "/usr/share/GeoIP", "/var/lib/GeoIP", "/usr/local/share/GeoIP", "/usr/local/var/GeoIP", ) &redef;

进行地理定位查询时,Zeek 会优先尝试精度更高的城市级数据库,失败再回退到国家级。

方式三:通过脚本显式打开数据库。mmdb_open_location_db与mmdb_open_asn_db函数接受数据库文件的完整路径。Zeek 始终只使用一个地理定位数据库和一个 ASN 数据库,显式加载会覆盖之前加载的任何数据库,且可以在任意时刻执行。

mmdb_open_location_db("/data/db/GeoLite2-City.mmdb"); mmdb_open_asn_db("/data/db/GeoLite2-ASN.mmdb");
查询函数与返回结构

两个内置函数(见 src/mmdb.bif)提供 GeoIP 能力:

function lookup_location(a:addr): geo_location function lookup_autonomous_system(a:addr): geo_autonomous_system
  • lookup_location返回geo_location记录,包含国家/地区等字段。geo_location的定义(见 scripts/base/init-bare.zeek)包括:country_code、region、city、latitude、longitude等可选字段;
  • lookup_autonomous_system返回geo_autonomous_system记录,含number(AS 号)与organization(所属组织)。

由于不同 IP 的查询结果中某些字段可能未初始化,访问前务必用存在性测试守卫,例如loc?$region。

数据库的新鲜度与自动重载

Zeek 会检查数据库文件是否过期,一旦检测到文件被更新(inode 或修改时间变化),会自动重新加载。底层实现在 src/MMDB.cc 的IsStaleDB():默认每mmdb_stale_check_interval(scripts/base/init-bare.zeek,默认 5 分钟)检查一次文件 inode 与 mtime,发生变化即关闭并重开数据库(MMDB_open采用MMDB_MODE_MMAP内存映射方式打开)。将该间隔设为负数可禁用过期检查。若查询出现错误,也会触发关闭重开以自愈。

与日志/告警的集成

Zeek不会自动把 GeoIP 情报写入日志,需要借助以下脚本或包:

  1. Notice 框架:通过Notice::lookup_location_types指定希望附加位置信息的 notice 类型,配合Notice::ACTION_ADD_GEODATA动作使用。实现见 scripts/base/frameworks/notice/actions/add-geodata.zeek:它会为连接中的"远端"主机查询位置并写入remote_location字段,且要求Site::local_nets已定义才能区分本地/远端。

    redef Notice::lookup_location_types += { SSH::Login };
  2. 现成策略脚本:

    • scripts/policy/protocols/smtp/detect-suspicious-orig.zeek:基于SMTP::suspicious_origination_countries(可疑发件国家集合,如 "US")与SMTP::suspicious_origination_networks检测可疑邮件来源,需要 GeoIP 支持;
    • scripts/policy/protocols/ssh/geo-data.zeek:基于SSH::watched_countries(默认含"RO")在成功登录来自/发往关注国家的 SSH 会话时产生SSH::Watched_Country_Loginnotice,并给 SSH 日志附加remote_location字段。
  3. 其他社区 Zeek 包。

快速自检

配置完成后,建议先验证一切就绪:

zeek -e "print lookup_location(8.8.8.8);"

若看到类似 "Failed to open GeoIP location database" 的错误,说明数据库配置有问题,可能需要重命名或移动 GeoIP 数据库文件使其匹配预期路径/文件名。

实战示例

下面的脚本打印所有来自美国俄亥俄州(OH)主机的 FTP 连接:

event ftp_reply(c: connection, code: count, msg: string, cont_resp: bool) { local client = c$id$orig_h; local loc = lookup_location(client); if (loc?$region && loc$region == "OH" && loc?$country_code && loc$country_code == "US") { local city = loc?$city ? loc$city : "<unknown>"; print fmt("FTP Connection from:%s (%s,%s,%s)", client, city, loc$region, loc$country_code); } }

注意对region、country_code、city均使用了?$存在性测试,避免未初始化字段报错。

Log Writers:换一种方式写日志

Kafka

若希望以流式方式将日志导出到 Apache Kafka,外部维护的zeek-kafka包是社区热门选择,配置也很简单。它依赖librdkafka(Kafka 的 C/C++ 客户端库)。

配置要点:将默认日志写入器切换为 Kafka writer,并通过Kafka::kafka_conf设置 broker 列表:

redef Log::default_writer = Log::WRITER_KAFKAWRITER; redef Kafka::kafka_conf += { ["metadata.broker.list"] = "192.168.0.1:9092" };

kafka_conf中可继续追加任意 librdkafka 支持的配置键(如acks、compression.codec等),实现更细粒度的投递语义与吞吐调优。

Logging:日志格式与时机优化

JSON 流式日志

外部维护的json-streaming-logs包专为配合 Filebeat、fluentd 等日志转发器设计。它做三件事:

  1. 额外生成一批以json_streaming_为前缀的日志文件;
  2. 为日志记录追加_path与_write_ts字段(分别标识日志来源与写出时间戳,便于下游索引与时间对齐);
  3. 按流式消费场景配置合适的日志轮转策略。

如果你的部署没有日志归档(archive)需求、又不想引入 Kafka,只想把所有日志实时送出 Zeek 所在主机,这个包就是现成方案。

长连接日志

Zeek 默认只在连接终止或因不活动超时过期时才把连接条目写入conn.log。受协议与超时值影响,一个连接出现在日志中的时间可能被显著推迟——例如长时间挂起的 SSH 连接,或 FIN/RST 报文被漏掉的连接,延迟甚至可达一小时。这对流式检测场景显然不够友好。

zeek-long-connections包正是为此而生:它创建与conn.log格式相同的conn_long.log,但会按可配置的时间间隔为"存活中的连接"输出条目。默认在连接存在10 分钟后记录第一条。在流式环境中,即使 10 分钟对检测来说也可能太长,可按需调低该间隔。

Profiling and Debugging:内存问题定位

jemalloc profiling

排查 Zeek 的内存泄漏或状态增长(state-growth)问题时,jemalloc 的 profiling 能力非常关键。社区包zeek-jemalloc-profiling提供了配置 jemalloc profiling 功能所需的少量支持。

关于内存 profiling 的更多通用信息,可参考 doc/advanced/troubleshooting.rst 中的 Troubleshooting 章节。

小结:常用定制化的选用建议

需求方案性质关键配置项
日志增加流哈希 ID内置 Community IDZeek 原生(6.0+)加载 community-id-logging.zeek、CommunityID::seed
日志/告警增加地理位置内置 GeoIPZeek 原生(需 libmaxminddb + .mmdb 库)mmdb_dir、mmdb_dir_fallbacks、Notice::lookup_location_types
日志导出 Kafkazeek-kafka外部包Log::WRITER_KAFKAWRITER、Kafka::kafka_conf
JSON 流式外送json-streaming-logs外部包直接加载
长连接即时落盘zeek-long-connections外部包间隔时间(默认 10 分钟)
内存 profilingzeek-jemalloc-profiling外部包配合 Troubleshooting 章节

原生功能(Community ID、GeoIP)随 Zeek 发布、行为稳定且有源码级文档可查,建议优先采用;外部包功能成熟但维护责任在各自项目,生产环境引入前应在测试环境验证其与自身 Zeek 版本的兼容性,尤其要注意本文开头提到的zeek-community-id与内置实现的冲突问题。

  • 网络安全
  • 网络
  • IDS

【免费下载链接】zeek

Zeek is a powerful network analysis framework that is much different from the typical IDS you may know.

项目地址:https://gitcode.com/gh_mirrors/ze/zeek
点击查看免费下载

相关推荐

上一篇:HTTP响应时间告警阈值:async-http-client最佳实践
下一篇:终极指南:如何在WSL2环境中配置node-notifier实现Linux子系统通知

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询