AgentProvenance

Agent 执行观测与证据验证示例 文档
示例/文档

原生采集的持久化与迟到关联

English | 简体中文

agentprov sensor stream 通过本地遥测缓冲队列保存原生 eBPF 事件。事件先经过格式规范化和隐私过滤,写入磁盘后,再异步关联并存入证据库。采集器按批处理,不再将所有事件 ID 留在一个结果列表中,直到进程退出。

默认值与配置

参数 默认值 用途
--batch-events 256 单批事件数上限,允许范围为 1~4096
--batch-bytes 1048576 单批规范化事件的字节数上限
--flush-interval 1s 定期封存和处理批次的间隔
--pending-ttl 2m 等待执行上下文的期限,从批次创建时计算
--max-queued-bytes 268435456 活跃队列的字节数上限
--max-queued-batches 4096 活跃队列的批次数上限
--keep-uncorrelated false 立即保留未归属事件,不等待上下文或到期丢弃
--no-policy false 不对已提交批次执行策略评估

创建采集文件前,队列会预留一个完整批次的容量。因此,新批次要求至少有 batch-bytes 的剩余容量。队列已满时,新事件会被丢弃,并持久化增加 dropped_queue_full 计数;事件过大时,增加 dropped_oversize。

为了确保能够重新读取,编码后的单行规范化事件必须小于 1 MiB,即使批次上限更大,也不能超过这一限制。这些限制控制的是临时缓冲占用,并不保证任意负载下都能保留全部内核事件。

持久化与恢复

每条被接收的规范化事件都会追加到权限为 0600 的文件,并执行 fsync。创建文件后,还会同步它所在的目录。批次达到字节数或事件数上限,或等到下一次刷新时,会被封存,随后进入处理流程。

数据库格式版本 17 区分五种批次状态:initializing、capturing、queued、processing 和 processed。批次仍处于初始化状态时,不允许写入事件。必须先创建文件、同步目录并提交状态变更。SQLite 连接显式使用 synchronous=FULL。

同一个证据库同时只允许一个原生采集器持有。旧程序遇到较新的数据库格式会在写入迁移前拒绝打开;不要用旧程序操作已经升级的证据库。

定时封存与入库处理分别运行,积压处理不会拖延封存计时器。每处理完一个批次,入库线程都会重新检查队列优先级,及时处理新到达的事件。

重启后的处理

重启时,采集器会封存上次采集文件中的完整行,并重新处理。末尾未写完整的一行会被截断,计入 dropped_partial_recovery。处理中断的批次会重新排队。

每个批次的每一行都有处理回执。回执与事件、图关系变更、事件时间窗口及证据批次清单在同一事务中提交。重试同一行不会重复生成已接收的事件。两次独立采集即使字节完全相同,也仍然是两个事件。重新处理前,会先用哈希校验缓冲文件内容。

每个缓冲文件分段处理:每段最多 16 行,处理满 50 毫秒后会在当前行结束处提交。事件、回执、清单和计数在同一事务中保存;后续段失败时,前面已提交的结果仍然有效。短暂的 SQLite 写锁争用最多尝试三次,其他错误直接返回。打开已经升级到当前版本的证据库时,不再重复写入迁移信息。

初始化中的文件如果为空或不存在,可以在恢复时丢弃,因为此时还不可能接收事件。已经进入采集状态的文件若丢失,则会导致启动失败,并记录错误,不能静默忽略已接收证据的缺失。

关联查询与事件写入使用同一事务。如果同批较早的事件记录了进程退出,会先关闭该进程的绑定,再处理后续可能复用了同一 PID 的事件。

文件清理与空间占用

批次处理成功后,采集器会删除载荷文件并同步目录,同时删除该批次的行回执。证据事件、有大小限制的批次清单、队列处理结果和累计丢失计数仍保留在数据库中。

数据库会随着证据积累而增长。队列容量上限不等于证据保留期限,也不会自动限制整个数据库的大小。

文件清理失败后,采集器会在继续采集时分批重试,不会因清理失败而拒绝重启。等待清理的已处理文件仍占用队列容量,显示在 cleanup_pending_batches 中;其中已提交的事件不会再次处理。

写盘前的隐私处理

磁盘缓冲保存规范化后的事件,而不是传感器标准输出的未过滤副本。TLS 预览与正文选取、敏感信息脱敏都在写入缓冲之前完成。批次哈希对应这些规范化后的字节。

迟到的执行上下文

无法归属的原生事件会留在持久批次中,等待上下文到达或超过 TTL。每次重试均使用原始采集时间。已关闭的绑定时间区间仍可用于关联,因此,即使短命容器已经退出,informer 后到的绑定也可能补上归属。

重新处理不会改写事件时间或时间窗口。同一批中已能匹配的行会立即提交,不受未匹配行阻塞。证据清单按执行记录分别生成,只包含当次处理实际接收的事件。

等待上下文的批次至少间隔一秒重试,新批次仍可继续处理。处理失败时会保留批次和错误,增加 retry_failures,五秒后重试。

超过等待期限的事件计入 expired_uncorrelated。这里也包含未跟踪的宿主机活动,不能据此认定每条到期事件都来自被跟踪的工作负载。如果既没有稳定的容器或 cgroup 身份,也没有可用的历史绑定,归属会保持未知,不进行猜测。

迟到的进程退出事件不会关闭在该退出时间之后才开始、复用了同一 PID 的新绑定。

新原生采集使用启动 ID、PID 和内核进程启动时间标识一次进程生命周期。fork 事件让短命 子进程在消失前获得锚点;进程迁移 cgroup 后,精确生命周期绑定仍然有效。只有已观测的 作用域或已绑定的内核父进程能建立该绑定,不凭 PID/PPID 接近程度猜测,也不把子进程的新 cgroup 整体归入当前运行。两个架构都保留内核采集时间,不用用户态排空队列的时间替代。 缺少这些字段的旧事件继续沿用原关联行为。

fork 记录读取子进程自己的 cgroup,而不是父进程当前的 cgroup。 clone3(CLONE_INTO_CGROUP) 可让子进程出生时就位于另一个 cgroup。 后续迁移改变观测到的 cgroup ID,不改变进程的启动 ID/PID/出生时间身份或既有 run 绑定。

正常关闭时,采集器在 launch 封存报告前执行有界排空:最多三秒、八轮,跳过重试计时器, 用新到达的绑定补关联。仍未知的事件保留原 TTL 和持久队列,不通过删除数据把积压清零; 超时或处理失败会明确报告。

.、../file 等原始 syscall 路径是合法证据;缺少 cwd/dirfd 解析时,不据此建立已解析 制品节点。非法行增加拒收计数,并在 logs/native-rejections.json 中保留最多 64 条有界 身份及原因样本供诊断,不保存被拒正文、路径或命令。样本数量不等于全部丢失数量。

open 路径在 syscall 入口不可读时,使用有界线程表在 syscall 返回时重读一次,保留入口 时间,并明确记录 path_observation=syscall_exit_retry 和 syscall_result。返回时看到的 路径不证明入口字节完全相同,失败的 open 也不代表成功写入。重读仍缺路径时保留失败诊断。

状态与保证范围

agentprov --data-dir /var/lib/agentprov sensor status --json

返回结果包含 collector_running、capabilities_historical、最近一次带时间戳的探针能力快照,以及 capture 计数、排队字节数、批次数和待关联事件数。

采集器是否仍在运行,通过进程持有的锁判断。保存过一份 ready 报告,不能证明进程在崩溃后仍存活。状态报告也只是读取时的快照,读取后即可发生变化。

captured 统计已封存的行,包括崩溃恢复出的完整行。当前正在写入的批次要等到下一次封存才进入该计数。输入量和排除量等辅助计数定期刷新,突然崩溃时可能滞后。队列拒收、到期丢弃和恢复处理结果分别持久化。

内核报告的 resource_pressure 丢失事件不等待上下文,直接保留,供采集器健康状态使用。

生产者健康接口以 native_node_capture 展示相同的节点级计数,即使查询按执行记录筛选,也不会将未知事件强行归属给该记录。仍有待处理事件或已记录丢失时,不能报告完整覆盖。

持久化保证从传感器已向写入器交付完整一行之后开始。内核环形缓冲区丢失、交付前进程被终止,以及无法兑现 fsync 的存储设备,都不在这一保证范围内。如果存储无法继续持久化事件,采集器会返回错误。

提交后的策略评估不属于证据事务。它按尽力完成的方式执行,失败单独计入 policy_failures。

原生批次由 sensor stream 负责恢复。通用 Falco 缓冲队列的工作进程不会处理原生批次;要恢复这些批次,需要重启原生采集器。

回归验证

需显式以 root 运行的 scripts/accept_sensor_live.py 在 syscall/TLS 负载之外,检查真实 进程出生、文件事件、退出、cgroup 迁移与 clone3 出生位置。加上 --require-cgroup-lifecycle 后,cgroup 权限或 clone3 不可用会失败,不能静默省略。 --pause-drain 1.5 验证排队事件仍保留内核采集时间。导出的 .events.jsonl 只保留 测试负载进程的事件,不包含无关宿主机活动。

用该文件验证持久摄取后的归属:

AGENTPROV_LIVE_LIFECYCLE_EVENTS=/path/to/acceptance.events.jsonl \
  go test ./internal/telemetry -run '^TestNativeLiveLifecycleAttribution$' -count=1 -v

该检查确认真实迁移/clone 事件保留 run 归属,同时不把目标 cgroup 内无关进程整体归入该 run。 未提供真实事件文件时,测试会跳过;跳过不等于实机验收。Go TLS 响应采集仍是 amd64 专属路径,ARM64 的 Go fixture 验证请求侧。

internal/telemetry/native_stream_test.go 覆盖以下情况:

  • 绑定窗口已关闭后,迟到上下文仍能补关联。
  • 批次封存前崩溃恢复,以及处理中断后的去重。
  • 事务回滚和重试、TTL 到期、队列容量边界。
  • 持续采集时限制清单大小,区分内容相同的独立事件。
  • 写盘前隐私处理,以及多个采集器互斥。

internal/correlation/binding_test.go 覆盖 PID 复用后的迟到退出事件。

补充回归还覆盖同批退出事件、规范化后单行大小增长、采集文件丢失、接收事件前的初始化恢复,以及重启时的清理错误。测试覆盖不等于长期运行验收;长期负载和不同存储设备的验证应单独记录。

界面语言

CLI 和独立传感器默认使用英文。显式选择中文:

agentprov --lang zh-CN --data-dir /var/lib/agentprov sensor stream
agentprov --lang zh-CN --data-dir /var/lib/agentprov sensor status
agentprov-sensor --lang zh-CN --help

中文状态输出包含队列计数、历史快照提示、各探针挂载结果及 TLS 发现限制。保存的能力报告仍保留原始错误;展示时翻译 AgentProvenance 已知的诊断模板。路径、探针名称和未知的外部库诊断保留原文。

独立传感器和监督采集器的就绪标记、带前缀的能力 JSON 不变,中文可读说明额外写入标准错误。标准输出中的事件 JSONL、sensor status --json、回调报告及能力文件不受界面语言影响。提示文字完成翻译,不代表增加了采集覆盖或恢复保证。