Skip to content

feat(audit): 每命令结构化审计日志 + 可插拔企业 sink(通用合规能力)#398

Open
PeterGuy326 wants to merge 8 commits into
mainfrom
feat/audit-log
Open

feat(audit): 每命令结构化审计日志 + 可插拔企业 sink(通用合规能力)#398
PeterGuy326 wants to merge 8 commits into
mainfrom
feat/audit-log

Conversation

@PeterGuy326
Copy link
Copy Markdown
Collaborator

@PeterGuy326 PeterGuy326 commented Jun 3, 2026

背景

通用企业合规审计能力:为每一次 dws 命令调用产出一条结构化审计记录,
让任何部署 dws 的企业都能把员工经 dws 的操作留痕。非某一客户专属。

设计:产生与投递分离(开源惯例)

  • 通道A 本地审计文件 <configDir>/logs/audit.jsonl:源头真相,operator 自己拥有、可 grep
  • 通道B 转发到企业自有 sink:可选,endpoint 由部署企业配置DWS_AUDIT_FORWARD_URL),
    不写死厂商默认。富审计数据是企业自己的合规资产,进企业自己的库,厂商不背数据责任。
  • 默认全关:不设 DWS_AUDIT_ENABLED 则零产出,热路径零影响。

字段(尽量不丢,除非该层完全拿不到)

time / trace_id、actor·org(登录 token)、device(os 始终;device_id·sn_no 为 PIPL 个人信息,
opt-in 默认关)、自然语言 intent(仅 agent 层能注入,记录里标 provenance=agent 表不可验真)、
module·command·subcommand、子命令介绍与敏感度(catalog)、操作对象 id·名称(参数)、
数据流向 direction·api·本地路径·peer ids(推断)、outcome·err_class。

脱敏分档(仅作用于转发,本地文件始终全量)

none(企业自有信任域)/ hashed(加盐可关联不可还原)/ minimal(仅维度,供运维监控)。

接入点

runner.goexecuteInvocation 收口 defer,与既有 LogCommandEnd 并列;单点、低风险。

验证

  • go build ./... / go vet 全过。
  • internal/audit:脱敏分档、本地+转发投递、设备指纹 opt-in 门控(darwin 实采 ioreg 验证 IOPlatformUUID/SerialNumber)、配置注册。
  • internal/app:端到端 emitAudit 字段填充 + flow 分类(含 local-export / read)。
  • 配置项 DWS_AUDIT_* 已注册进 configmeta。

复现:
```bash
export DWS_AUDIT_ENABLED=true
export DWS_AUDIT_FORWARD_URL=https://audit.internal.example.com/dws
export DWS_AUDIT_FORWARD_TOKEN=
export DWS_AUDIT_DEVICE_FINGERPRINT=true # 需采集设备指纹时
dws minutes export --minute-id m-77 --output ~/Desktop/q2.md --format json
tail -n1 ~/.dws/logs/audit.jsonl | jq .
```
详见 docs/audit.md

后续(本 PR 未含)

  • stdio:// 本地插件路径的审计 emit(当前仅 HTTP 调用路径)。
  • 官方仓库侧"大面积报错"运维监控:建议走独立的极简匿名 telemetry,与本审计隔离,保持隐私故事干净。

@PeterGuy326 PeterGuy326 changed the title feat(audit): 每命令结构化审计日志 + 可插拔企业 sink feat(audit): 每命令结构化审计日志 + 可插拔企业 sink(通用合规能力) Jun 3, 2026
@PeterGuy326 PeterGuy326 force-pushed the feat/audit-log branch 2 times, most recently from a4ae3ea to 953d1a8 Compare June 3, 2026 06:29
…se sink

面向所有部署 dws 的企业的通用合规审计能力:为每次命令调用产出一条
结构化审计记录,把员工经 dws 的操作留痕。遵循开源惯例,把"产生事件"
与"投递事件"分离:

- 通道A 本地审计文件 <configDir>/logs/audit.jsonl(源头真相,operator 自有)
- 通道B 转发到企业自有 sink(endpoint 由部署企业配,不写死厂商;转发前可脱敏)
- 默认全关:不设 DWS_AUDIT_ENABLED 则零产出、热路径零影响

字段尽量不丢:time/trace_id、actor/org(token)、device(opt-in 含 sn_no)、
自然语言 intent(仅 agent 层能注入,标记 provenance 表不可验真)、
module/command/subcommand、子命令介绍与敏感度(catalog)、操作对象(参数)、
数据流向(推断)、outcome。

接入点为 executeInvocation 收口处的 defer,与既有 LogCommandEnd 并列。
新增 internal/audit(schema/sink/forwarder/device/config)+ docs/audit.md,
全部带测试:脱敏分档、本地+转发投递、设备指纹 opt-in 门控(darwin 实采
ioreg 验证)、配置注册、app 层端到端字段填充与 flow 分类。
…sion), defer forgeable env fields

按可信度分层:只记录不可被调用方伪造的字段。
- 新增 client 块:agent_id(identity.json 装机 id)、source、cli_version(编译注入),
  以及 actor.user_id / org.corp_id(token 网关验签)——均不可 per-call 伪造。
- host_agent(DINGTALK_AGENT) / channel(DWS_CHANNEL) / agent_code 是调用方自报的
  环境变量、可伪造,故先不记录;待网关回带签名 agent 凭证再加(见 docs TODO)。
- schema v2;脱敏:hashed 档哈希 agent_id,minimal 档仅留 cli_version 做运维维度。
- docs/audit.md:字段按可信/暂不上分类,补"日志去向/中心化收集"与 TODO 两节。

真实验证:dws minutes list mine 实跑,client.agent_id/source/cli_version 落盘,
org.corp_id 来自真 token;故意注入 DINGTALK_AGENT=fake 不进审计。
…ward target

examples/audit-ingest:dws 审计转发的最小参考接收端。
- 本地验证版(纯标准库):校验 Bearer + schema,落 JSONL,已端到端实测
  (真 dws minutes 调用 → 转发 → ingest 收下落库)。
- README:阿里云 SLS 生产版(PutLogs 落 Logstore + 索引)、函数计算 FC 部署步骤、
  SLS 控制台开通指引、数据边界提醒。

让运维方照此把 DWS_AUDIT_FORWARD_URL 指向自己的端点即可统一收集审计。
examples/audit-ingest 的任务(本地证明转发链路可通)已完成;作为 CLI 仓库里的
常驻代码它是噪音——收数据的服务端不该由 CLI 仓库承载。删除该目录,把真正有价值的
"接收端契约 + 阿里云 SLS/FC 接入步骤"并入 docs/audit.md,知识留下、toy 去掉。
…y-signed identity

新增 client.channel:来自 DWS_CHANNEL,记录"哪个 agent/渠道在调用"
(OpenClaw / Qoder…)。定位为半可信——网关按 allowedChannels 白名单校验
membership(乱填渠道会被拒),但未做密码学绑定,已登记渠道间仍可冒充;
minimal 脱敏档保留 channel 作为运维维度。

host_agent(DINGTALK_AGENT) / agent_code 仍是无校验纯标签、完全可伪造,
继续不记录。docs/audit.md 补"网关侧支持需求":签发与 token 绑定的签名
agent 凭证 + 调用回带 + 网关验签后回带已验证身份的接口契约草案,让 channel
升为完全可信、解锁 host_agent/agent_code。

真实验证:dws minutes list mine + DWS_CHANNEL=openclaw → client.channel=openclaw 落盘。
@PeterGuy326
Copy link
Copy Markdown
Collaborator Author

PeterGuy326 commented Jun 3, 2026

dws 操作审计日志:字段说明、接入方式与网关侧支持需求

一、审计日志字段(schema v2)

字段按可信度分级。可信字段来源于 token 校验、dws 自管状态或 dws 运行时实测,调用方无法在单次调用中伪造。

已记录字段

字段 含义 来源 可信性
ts / trace_id 时间 / 唯一 trace dws(trace_id 即传输层 execution_id) 实测
actor.user_id / name 操作用户 登录 token(user_id 仅登录流程捕获时存在) token 校验,不可伪造
org.corp_id / name 所属组织 登录 token token 校验,不可伪造
client.agent_id 安装实例唯一标识 identity.json(安装期生成 UUID) dws 自管
client.channel 调用方 Agent / 渠道 环境变量 DWS_CHANNEL 半可信:网关按 allowedChannels 校验合法性,未做密码学绑定
client.source / cli_version 来源标识 / 版本 dws 自管 / 编译注入 dws 自管
device.os(及 device_id / sn_no / hostname,需显式开启) 设备信息 本机硬件读取 实测
intent.nl_input / provenance 自然语言请求原文 上层 Agent 注入 标记 provenance=agent,明示不可验真
module / command / subcommand / subcommand_desc 操作模块与命令链 dws 命令解析 + 命令 catalog 实测
target(id / name / summary / sensitivity) 操作对象 调用参数 + catalog 实测
flow(direction / api / endpoint / local_path / peer_ids) 数据流向 dws 推断 实测
outcome / err_class / exit_code 执行结果与错误分类 dws 实测 实测

暂不记录字段host_agent(环境变量 DINGTALK_AGENT)、agent_code(环境变量 DINGTALK_DWS_AGENTCODE)。二者为调用方自声明的环境变量,网关不做校验,可被任意伪造,故在网关签名能力就绪前不予记录。

二、接入方式

审计能力默认关闭,通过环境变量按需开启:

配置项 说明
DWS_AUDIT_ENABLED=true 启用本地审计文件 <configDir>/logs/audit.jsonl
DWS_AUDIT_FORWARD_URL 转发目标,指向企业自有 sink(非厂商默认端点)
DWS_AUDIT_FORWARD_TOKEN 转发目标的 Bearer 鉴权
DWS_AUDIT_FORWARD_REDACT 转发脱敏档:none / hashed / minimal
DWS_AUDIT_DEVICE_FINGERPRINT=true 采集 device_id / sn_no(PIPL 个人信息,默认关闭)
DWS_AUDIT_NL_INTENT 由上层 Agent 注入的自然语言请求原文
DWS_CHANNEL 声明调用方 Agent / 渠道,记入 client.channel

接入方(如 OpenClaw、Qoder 等 Agent 宿主)在拉起 dws 进程时设置 DWS_CHANNEL 及审计相关配置项即可。数据链路:本地审计文件 →(可选)转发至企业自有 sink → 阿里云 SLS 投递(新版)→ MaxCompute → DataWorks 统计分析。

三、网关侧支持需求

能力目标:使审计记录中的「调用方 Agent / 渠道」身份达到不可伪造,从而支撑安全与合规归因,包括按渠道维度统计调用量与成功率、对指定渠道实施访问控制或封禁、以及在异常时定位真实接入方。

设计依据(为何须由网关侧实现):dws 运行于调用方环境并受其控制,凡 dws 可在本地读取的信息,调用方均可篡改(例如通过 DWS_CHANNEL 声明任意渠道),因此 CLI 无法自证身份。具备签发不可伪造身份能力的唯一主体,是完成了调用方鉴权且不受调用方篡改的服务端,即网关(信任根)。当前网关已通过 allowedChannels 白名单校验渠道合法性(半可信),但渠道码为明文且未与 token 做密码学绑定,已登记渠道之间仍可相互冒充。

需求项

  1. 签发与 token 绑定的签名 Agent 凭证:鉴权阶段基于已校验的 token 与已登记的 channel,签发 agentCredential(JWT 或 HMAC,包含 channel_code、agent_code、有效期及 hash(corp_id + user_id) 指纹),并在鉴权响应中返回 agentCredentialagentCredentialExpiry
  2. 调用链验签与可信身份回传:dws 在后续每次调用中携带 x-dws-agent-credential;网关完成验签(签名、有效期、token 绑定一致性校验)后,通过 x-dws-verified-channel / x-dws-verified-agent 回传已校验身份,dws 审计据此记录网关回传值,而非本地环境变量声明值。
  3. 渠道注册与接入方身份校验:维护 channel_code 与接入方的注册关系;签发凭证时校验接入方身份(AppKey 或证书),确保渠道码仅可由其合法持有者使用。

接口契约(草案)

位置 新增字段 说明
鉴权响应 agentCredential / agentCredentialExpiry 与 token 绑定的签名凭证及有效期
调用请求头 x-dws-agent-credential dws 携带的凭证
调用响应头 x-dws-verified-channel / x-dws-verified-agent 网关验签后回传的已校验身份

网关能力就绪后,dws 侧将把 client.channel 由环境变量声明值切换为网关回传的已校验值,并将 host_agentagent_code 纳入审计并标记为完全可信。

完整说明详见本 PR 内 docs/audit.md

Open-source repo convention: configmeta descriptions, struct-field comments,
test fixtures and docs/audit.md are now English. No behavior change; audit tests
still pass (redaction-boundary assertions updated to English sentinels).
…rmat

Reframe the client-side audit as a best-effort LOCAL diagnostic/troubleshooting
trail, not a mandatory compliance record — a user controls their own machine and
can bypass it, so authoritative/mandatory audit belongs on the MCP gateway. Adds
a 'Scope and limits' note up top and a gateway pointer in the collection section.

Also make the file location explicit (default ~/.dws/logs/audit.jsonl, per-OS
table, DWS_CONFIG_DIR override) and document the JSONL format choice with ready
jq examples.
The single audit.jsonl grew unbounded. Switch to one file per calendar day
(audit-YYYY-MM-DD.jsonl) via a stdlib DateRotatingWriter that rolls at midnight
and prunes files older than DWS_AUDIT_MAX_AGE_DAYS (default 30; 0 = keep all).
Works for both the short-lived CLI and long-running stdio mode. Docs updated;
rotation + pruning covered by tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant