Skip to content

插件权限与审计 ​

权限强制(白名单、审批、审计)为规划中,见 openspec/changes/keel-plugin-core/specs/plugin-system/spec.md(「权限与沙箱」「审计」)。 它所基于的凭据引用纪律与进程/环境隔离在 internal/gateway、internal/host、 internal/servicehost 中已实现,下文就地给出出处。

1. 先讲诚实的基线 ​

keel 不对插件代码做沙箱,并且把这句话说清楚——与本设计所参照的 DSH 模型立场一致:

  • 进程内插件被编译进宿主,以宿主权限运行;permissions 块是策略契约,在宿主可控的接缝 处强制(工具派发、密钥解析、进程派生)并写入审计——它不是内存沙箱;
  • sidecar 插件运行在自己的进程里;该进程边界加环境变量白名单就是隔离;
  • 安装插件意味着执行第三方代码。因此安装/启用/升级都需要显式的 durable 审批——权限模型 的全部意义在于:「它能运行」是一个决定,而不是默认值。

2. 清单声明什么 ​

yaml
permissions:
  fs: [workspace-write]        # 文件效应类别
  net: [api.example.com, …]    # 允许的 host
  secrets: [OPENAI_API_KEY, …] # 允许的密钥引用(只有名字,绝无值)
  tools: [example_status, …]   # 允许调用的工具

强制点(规划)及规格要求的失败模式:

授予在哪强制越权时
tools工具派发:执行前按调用方插件 id 校验白名单调用以明确错误失败 + 审计 (插件, 动作, 目标, 结果)
net宿主可控的出站接缝(今天是 provider 调用;未来直连出口按声明 host 集)拒绝 + 审计
secrets密钥引用解析:只有声明的名字可以解析拒绝 + 审计;值永不进日志
fs工作区访问外的文件效应类别检查拒绝 + 审计
sidecar env进程构造:MinimalEnv + 仅声明白名单白名单外的名字不出现在子进程环境中

最后一行对外部网关已实现,对 sidecar 原样适用:被 spawn 的进程只拿到 MinimalEnv 加 显式声明的变量,引用经 ${NAME} / env:NAME / secret:NAME 解析,且字面密钥在校验期即 被拒绝(internal/gateway/process.go:106、internal/servicehost/models.go:546、 internal/host/config.go:203)。同一条纪律今天也在保护模型凭据:租户密钥存储不可用时 secret: 引用 fail-closed——绝无明文存取(cmd/keel/serve.go:165)。

3. 审批:由 decision 原语发问 ​

安装/启用/升级就是 durable、可回答的问题;Harness 已经为这种形状准备了原语 (审批与问句):

安装请求 ──▶ decision(kind=approval, options=[allow, deny])
               │
               ├─ 作答 allow ──▶ 校验清单 + 版本门 + hash → installed → 审计
               ├─ 作答 deny  ──▶ 不生效;审计记录拒绝
               └─ 超时 / 未作答 ──▶ 不生效(fail-closed)

规格明确的规则(spec: 权限与沙箱):审批门禁安装/启用/升级;拒绝则不生效;审批本身是带 答案日志的 durable 行(绝不是一次性的弹窗)。超时按 fail-closed 处理。

4. 审计 ​

每一次生命周期动作(install/enable/disable/uninstall/update)与每一次权限拒绝,都 记录时间、操作者、插件 id、版本、动作、结果,可通过 GET /v1/plugins/audit 查询 (spec: 审计)。当前代码库已在两个层面演示了该模式:

  • 触发器审计保留逐次触发历史与最近 N 条审计窗口 (internal/agentext/triggers.go:86、:135);
  • 内核审计 sink 把每次拒绝投影为结构化日志字段(reason/op/tenant/session/kind/owner/epoch) 并限频(internal/servicehost/audit.go、docs/LOADOUT.md §6)。

插件审计沿用同一风格:有界记录、只含标识、无载荷无密钥。

5. 存量风险,如实列出 ​

以下风险已知且刻意不隐藏(spec + docs/DSH-REFERENCE.md §4):

风险为何存在缓解路径
进程内插件代码可以越出其声明权限无内存沙箱官方插件按构造受信;第三方逻辑应放 sidecar
无视 stdin EOF 的 sidecar 能在宿主被杀后存活正常回收只走关闭路径协议义务(子进程 EOF 即退)+ 规划中的 pidfile sweep / 外部 supervisor
暂无 per-plugin CPU/内存预算预算今天属部署侧规划在 spawn 处接 per-backend 预算(cgroup/rlimit),越限拒绝激活
密钥引用是名字而非作用域授予名字即授予值只声明最窄的名字;值永不进日志或账本

6. 复核者应能核对的清单 ​

对任一启用插件,仅凭 API 面就应能回答:

  1. 它声明了什么(清单)——清单;
  2. 它被允许触碰什么(权限)——本页;
  3. 它实际做了什么(审计)——§4;
  4. 谁在何时批准了它(decision 行)——§3;
  5. 它此刻提供什么(快照:tools/hooks/triggers/diagnostics)—— GET /v1/plugins(internal/agentapi/ext.go:18)。

Apache-2.0(自 pigo 搬运的文件保留 MIT 声明)