Fleet 与 osquery 实战:用 Community ID 把网络监控日志和端点进程行为关联起来
【免费下载链接】fleetOpen device management项目地址: https://gitcode.com/GitHub_Trending/fl/fleet
本文以 Fleet 官方文章《Correlate network connections with community ID in osquery》为主体,讲解如何利用 Community ID 哈希将 Zeek、Suricata、Arkime 等网络监控工具的日志与 osquery 的端点数据精确关联:读完你将掌握从网络日志中提取 Community ID、用 osqueryi 实时定位连接所属进程、扩展二进制哈希富化,以及在 Fleet 中通过调度报告 + 外部日志目的地实现回溯式威胁调查的完整链路。
为什么需要把网络监控和端点监控关联起来
网络监控工具(Zeek、Suricata、Arkime 等)能看到"哪个 IP、哪个端口、什么协议在通信",但无法回答"这条连接是哪台主机上的哪个进程、以什么命令行参数发起的";反过来,osquery 的端点数据看不到完整的数据流与会话生命周期。两者之间缺少一个统一的关联键。
osquery 对 Community ID 哈希的支持恰好补上了这个缺口:只要网络侧和端点侧使用同一套哈希算法,就可以用一个确定性的字符串把两边的记录匹配起来。这套策略同样适用于任何支持 Community ID 的工具,包括 Arkime(原 Moloch)和 Suricata。
Community ID:连接参数的确定性哈希
Community ID 是对网络连接参数计算出的一个哈希值,用于在支持该哈希的多个监控方案之间匹配同一条连接。
其生成逻辑是:对**源和目的 IP 地址、源和目的端口、传输协议以及一个共享的 seed(种子)**共同执行哈希运算。关键特性在于:
- 确定性:相同的五元组 + 协议 + seed 永远产生相同的哈希,因此不同软件、不同时间、不同机器计算出的结果可以逐字节比较;
- 跨工具可比:网络监控侧(如 Zeek)算出的 Community ID,可以直接拿到端点侧(如 osquery)去反查,无需依赖 IP、时间戳等容易漂移的字段;
- 粒度为连接级:它标识的是"这条连接",而不是"这个进程"或"这个会话",所以一条连接对应一个稳定的关联键。
osquery 在 SQL 层把该哈希暴露为community_id_v1()函数,接收本地地址、远端地址、本地端口、远端端口和协议五个参数,这正是后续所有查询的基础。
实战第一步:从 Zeek 的 conn.log 中提取 Community ID
假设我们用 Zeek 采集网络流量,其conn.log的最后一列就是每条连接的 Community ID。原文给出的真实日志片段如下:
$ cat /usr/local/zeek/logs/current/conn.log ... #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state local_orig local_resp missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents community_id #types time string addr port addr port enum string interval count count string bool bool count string count count count count set[string] string 1582669704.509068 CEMnOh4OZTnzaUHWi 172.17.0.2 47434 192.168.65.1 53 udp dns 0.030783 0 36 SHR T T 0 Cd 0 0 164 - 1:M9OoSr2um69x5G8SikO3S7CVlKk= 1582669709.819653 CVTuEs4iasnpIw8nb7 172.17.0.2 35173 192.168.65.1 53 udp dns 0.004224 0 92 SHR T T 0 Cd 0 0 1120 - 1:1/6GSKCm6A8zk8jXFOEeZKTYccY= 1582669709.844549 CZX3af2jpkfeJzNItb 172.17.0.2 42716 13.227.76.23 443 tcp - 30.017714 0 0 SHR T F 0 ^hCf 0 0 288 - 1:l7a9beh8+bQe2MRtUW92OFmMMsM= 1582669741.641209 CQFgp24cLxMDaBPr03 172.17.0.2 50944 13.227.76.89 443 tcp - 1.691777 0 9653625 SHR T F 0 ^hCadCCCfA 1 406827 9926713 - 1:13GuW4mT6PnRwUi+zetPtsWWD3I= 1582669741.587538 CDvjoz2xAkMs4vbpo8 172.17.0.2 38330 192.168.65.1 53 udp dns 0.033607 0 352 SHR T T 0 Cd 0 0 2408 - 1:6tMXxnnFDfuiCIYTYXXCPMfB3fA=假设安全分析师关注的是172.17.0.2:42716到13.227.76.23:443的这条 TCP 连接。查看日志最后一列即可取出它的 Community ID:1:l7a9beh8+bQe2MRtUW92OFmMMsM=。
注意该 ID 的版本前缀1:,以及 Base64 编码中出现的+、/、=字符——在 SQL 里引用它时务必原样用单引号包裹,不要做任何转义或截断处理。
实战第二步:用 osqueryi 反查连接背后的进程
拿到 Community ID 后,就可以把它当作连接句柄,直接在目标主机的 osquery 会话中反查。核心思路是:用community_id_v1()函数对process_open_sockets表中的每个 socket 现场计算 Community ID,与目标值比对,再 JOINprocesses表补全进程信息:
osquery> SELECT community_id_v1(local_address,remote_address,local_port,remote_port,protocol) AS community_id, * ...> FROM processes JOIN process_open_sockets USING (pid) ...> WHERE local_address AND remote_address AND community_id = '1:l7a9beh8+bQe2MRtUW92OFmMMsM='; community_id = 1:l7a9beh8+bQe2MRtUW92OFmMMsM= pid = 23689 name = nc path = /bin/nc.traditional cmdline = nc osquery.io 443 state = T cwd = /root root = / uid = 0 gid = 0 euid = 0 egid = 0 suid = 0 sgid = 0 on_disk = 1 wired_size = 0 resident_size = 2088000 total_size = 10880000 user_time = 0 system_time = 0 disk_bytes_read = 0 disk_bytes_written = 0 start_time = 1582669708 parent = 1 pgroup = 23689 threads = 1 nice = 0 fd = 3 socket = 138425 family = 2 protocol = 6 local_address = 172.17.0.2 remote_address = 13.227.76.23 local_port = 42716 remote_port = 443 path = state = CLOSE_WAIT net_namespace = 4026532309这一份输出对 Zeek 看到的那条网络连接提供了大量上下文:
cmdline = nc osquery.io 443——实际发起连接的命令行;path = /bin/nc.traditional——可执行文件路径;uid = 0——以 root 身份运行的进程;start_time = 1582669708——进程启动时间,与 Zeek 日志中连接建立时间(1582669709 附近)吻合,进一步佐证了关联的正确性。
结论非常清晰:这条172.17.0.2:42716 → 13.227.76.23:443的连接就是主机上用netcat工具连向osquery.io的行为。仅凭网络侧日志,这个判断是下不出来的。
Fleet 仓库的查询库 docs/queries.yml 中同样采用了process_open_sockets JOIN processes这一模式,例如"MITRE - Process Network Connections"查询(约 L3404-L3416):
name: MITRE - Process Network Connections platform: linux, darwin, windows query: |- select DISTINCT p.name, p.path, pos.remote_address, pos.remote_port from process_open_sockets pos LEFT JOIN processes p ON pos.pid = p.pid WHERE pos.remote_port != 0 AND p.name != '';从源码结构看,processes与process_open_sockets按pid关联是 Fleet 内置查询包中标准的网络连接取证模式,本文 Community ID 的用法只是在此之上多算了一个哈希列作为跨系统关联键。
实战第三步:再 JOIN hash 表,富化出二进制哈希
processes + process_open_sockets的回答止步于"哪个进程连的"。如果需要进一步回答"这个二进制的指纹是什么",可以在同一条查询里继续 JOINhash表(按path关联),取出该可执行文件的 MD5:
osquery> SELECT pid, processes.path, cmdline, md5 ...> FROM processes JOIN process_open_sockets USING (pid) JOIN hash USING (path) ...> WHERE local_address AND remote_address AND community_id_v1(local_address,remote_address,local_port,remote_port,protocol) = '1:l7a9beh8+bQe2MRtUW92OFmMMsM='; pid = 23689 path = /bin/nc.traditional cmdline = nc osquery.io 443 md5 = 1c50c85a1472d0124194f21a12ade35e拿到的md5 = 1c50c85a1472d0124194f21a12ade35e可以继续用于威胁情报比对、恶意样本库检索等下游动作。整个调查链条因此是:Zeek 发现可疑连接 → Community ID 反查进程 → 二进制哈希富化 → 情报比对,全程只靠一个确定性哈希串串联。
从实时调查到回溯分析:调度查询与日志管道
上面的例子是在事发时用osqueryi做的实时调查。但更常见的场景是:网络监控先报了警,事后才要去主机上复盘。这时需要让端点数据持续留痕。
做法是在osqueryd中调度一条定时查询,把每条网络连接的 Community ID 连同关联进程细节一起记录下来:
SELECT *, community_id_v1(local_address,remote_address,local_port,remote_port,protocol) AS community_id FROM processes JOIN process_open_sockets USING (pid) JOIN hash USING (path)这些结果日志可以汇入日志聚合管道/SIEM,之后网络监控侧出现的任何 Community ID 都能直接在历史结果里检索出对应的进程、命令行和二进制哈希。
在 Linux 上,socket_events表还能提供额外价值:它捕获的是所有发生过的socket 连接,而不只是查询执行瞬间仍然活跃的连接,对短暂连接的回溯分析尤其有用。
在 Fleet 中落地这条链路
Fleet 把这条"调度—上报—外发"链路做成了产品化能力,仓库中有两处可直接参照的实现依据:
1. 调度报告的执行流程。docs/Contributing/reports/scheduled-queries.md 描述了 Fleet 定时查询架构的三步数据流:
1. Fleet 用户创建报告(UI 或 API)→ Server → DB 2. osquery agent 向 Server 拉取配置(含调度报告),Server 合并 fleet 级与全局配置下发 3. agent 按调度执行报告,结果回传 Server,Server 可选转发到外部日志系统把上面那条带community_id_v1的 SQL 建成报告并指派给目标主机/team,即可让每台主机的连接级端点数据按周期回传。
2. 结果日志外发到 SIEM。Fleet 的日志目的地机制(result log / status log)支持把报告结果转发到外部系统,articles/log-destinations.md 列出了可用的目的地。例如发送到 Splunk 时,通过 HEC 端点直发,配置形如:
osquery: status_log_plugin: splunk result_log_plugin: splunk splunk: url: https://splunk.example.com:8088 token: your-hec-token index: main source: fleet source_type: fleet:json此外还支持 Amazon Kinesis Data Firehose(可再经 S3/Snowpipe 进入 Snowflake)、Webhook 等目的地。事件按批聚合后发送,超过批量上限的事件会被丢弃并在 Fleet 服务端日志中记录通知——在规划管道容量时需要注意这一点。
由此形成完整的闭环:
Zeek/Suricata 网络日志 ──(community_id)──▶ SIEM 告警 osquery 调度报告(含 community_id) ──(Fleet 日志目的地)──▶ 同一 SIEM └─▶ 按 community_id 关联出进程/cmdline/md5适用前提与限制
- 两侧算法必须一致:Community ID 的可比性建立在"相同参数 + 相同 seed"之上。若网络侧与端点侧使用了不同版本算法或不同 seed,哈希将无法匹配,关联失效;
- 粒度限制:Community ID 标识的是连接而非会话,NAT 之后不同主机的连接在网络侧可能呈现为同一 ID,跨 NAT 边界做主机级归因时要结合其他字段;
- 实时查询的时机性:
process_open_sockets只反映查询执行时刻仍存在的连接,事发即断开的短暂连接要靠调度留痕或 Linux 的socket_events表回溯; - 哈希富化的前提:JOIN
hash表要求path字段有效且文件在盘上可读(on_disk = 1),动态加载、无文件进程拿不到哈希。
小结
Community ID 用"连接五元组 + 协议 + seed 的确定性哈希"为网络监控与端点监控之间提供了一个稳定、跨工具、可直接比对的关联键。osquery 的community_id_v1()函数让端点侧可以现场计算该哈希,配合processes JOIN process_open_sockets(必要时再 JOINhash)即可把一条网络日志还原成"哪个进程、什么命令行、什么二进制、哪个用户"的完整画像;在 Fleet 中,把这条 SQL 建成调度报告并接入外部日志目的地,还能把这种关联能力从实时调查扩展到事后的全量回溯分析。
【免费下载链接】fleetOpen device management项目地址: https://gitcode.com/GitHub_Trending/fl/fleet
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考