← 返回目录

第 2 卷

第 2 / 4 卷日期:

先说审批闸。Agent 每次调工具都要穿过 dsh 的工具流水线,第 06 讲说过,那条流水线有八道关,其中前两站决定“这一次到底执不执行”。本讲的审批闸就装在这两站上,负责装它

的函数是 mountApproval:

复制代码

1exportfunctionmountApproval(ctx: Context, level: () => AutonomyLevel):void{
2//挂在tools/pre-execute上:先让后续策略跑完,再按级别决定要不要拒绝
3ctx.on('tools/pre-execute',async(exec, next) => {
4constdecision =awaitnext()
5constreason =denyReasonFor(level(), exec.name) //写操作在低级别下被拦
6if(reason)return{kind:'deny', reason }astypeofdecision
7returndecision
8})

9

10//单调守卫:只做"工具在不在白名单里"这一个判断
11ctx.tools.guard((exec) =>guardReasonFor(exec.name))
12}

它不是 Agent 能调用的工具,而是把监听器与守卫注册到流水线上的函数,注册写法见第 06 讲。注册的两段判断的都是“Agent 这次工具调用该不该放行”:

前一段挂在 tools/pre-execute 上:这一站是策略层,能放行、拒绝或转审批;本讲只取“拒绝”,按级别直接 deny,不走 ask,那会把决定权转给审批服务;因为要不要人点

头是事先定好的,不必临时去问;具体怎么定,下面讲。

后一段是白名单守卫(ctx.tools.guard):约束层,只能拒绝或弃权。它按工具名判定,不在白名单里的工具一律拒绝,它只列允许的,没列进来的默认不放(fail-

closed)。这张表在代码里定死,ALLOWED_TOOLS 由治理工具表派生,运行期没有命令能改它,所以它不随自主级别变化。

为什么第二道关卡要用守卫?因为它有第 06 讲讲过的单调性:只能拒绝或弃权,不能主动放

0 / manual / off:Agent 的写操作一律被拦,起草也不例外,它连新候选都提不出来;

人这一侧又没有起草的入口,/firewall 只能查看、调级、上线 / 下线。所以在这个级别下想新增规则,只剩手工改规则文件这一条路。三者在审批上同效,off 另有一层语

义,就是撤下治理工具。

1(默认):起草自动,Agent 可自行起草候选,候选不参与判定、不影响真实流量;上线与下线要人确认。

2:起草与上线都自动,下线仍要人确认。

三档里没有“全自动”这一档:下线(firewall_reject)在任何级别都不自动放行。因为三个动作的后果不对等:

前提:本讲默认就按黑名单模式来讨论,即默认放行、命中 deny 才拦,规则也都是拦截用的。这是第 18 讲的默认模式,也是多数部署的样子。真切成白名单模式,默认拒绝、命

中 allow 才放行,上 / 下线的效果会反过来:那时上线一条 allow 才是打开放行。本讲不涉及那种模式。

三个动作按效果说:起草是提议一条新规则(firewall_draft),只是新增一条不生效的候选;错了也影响不到真实流量。上线是让规则开始生效(firewall_approve),多一道

拦截;错了最坏是误伤,看得见、可回退。下线是让规则停止生效(firewall_reject),撤掉一道正在生效的拦截;错了就是放空,攻击会长驱直入。reject 如果作用在还没生效的

候选上,那就叫驳回。无论叫下线还是驳回,结果都一样:状态标记为 rejected,规则不再进入生效流程,也不再参与拦截。

最后说下 /firewall 命令,它是人手动操作规则系统的通道。上面那些约束管的是 Agent 能自动做什么,而人靠这个命令来操作:查看规则(list)、调整自主级别(level)、上

线或下线(approve / reject)。它注册进了 dsh 的命令系统(ctx.commands.register),敲 /firewall 就能用。

先把两侧分清楚:控制面改规则、下命令,上一节的 /firewall 命令与审批闸都在这一侧;数据面真正拦截请求。上一节讲的是控制面怎么把关;这一节讲剩下的一步:变更记下来

之后,数据面怎么读到新规则,让它真的生效。

最直接的做法是改规则文件,但那不行:改了什么无从追溯、不可审计,两个变更同时改还会互相覆盖。所以本讲用 ledger(账本)串起整条链路,名字借自会计的账簿:每笔变更记一

条流水,当前的规则集由流水累加得出;你不能直接改这个“余额”,只能往后面追加流水。落到技术上,每一条流水就是一条事件,它不另写一个自己管的文件,而是以插件注入的

user/message 作为载体,来源标注成 plugin:firewall-ledger,追加进 dsh 的会话日志,也就是第 08 讲那份只追加的日志;而那个“余额”就是规则文件,它只是这串事件当

前折叠(fold)出来的产物,随时能由账本重建。

具体来说,规则要真正生效,得走三步:变更记成事件,这些事件折叠出规则集,再落成数据面读的文件。

先说账本。账本就是那串事件本身,每一次变更落一条,只往后追加,不改已经记下的。这是 Agent 起草落的那条:新增一条规则,state 是 candidate,刚生成,还不生效。

复制代码 1{

2"type": "rule.add", 3"id": "rule-7a9546c1",

4"state": "candidate", 5"at": 1750000000000,

6"by": "Agent(firewall_draft)", 7"input": {

8"name": "block ip 1.2.3.4",

9"when": {"field":"ip","op":"eq","value":"1.2.3.4"},
10"action":"deny"
11}
12 }
全站分卷索引 · 4 卷
  1. 1第 1 卷
  2. 2第 2 卷
  3. 3第 3 卷
  4. 4第 4 卷