网关扩展开发
用 Rust 或 Lua 编写 WASM 网关扩展:获取模板、构建校验、运行限制与发布
网关扩展开发
网关扩展是运行在网关请求处理过程中的 WASM 组件,用于在调用上游前放行或拒绝请求、改写请求,或检查流式响应。官方 SDK 支持 Rust 和 Lua,两种语言最终都发布为只含 manifest.json 与 plugin.wasm 的扩展包,由同一套 Wasmtime 宿主执行。
安全厂商用网关扩展接入检测能力时,先读《安全厂商接入指南》确认这条路适合你的场景。
1. 运行限制
两种语言共用同一组限制,超出即中断执行:
| 项 | 限制 |
|---|---|
| 指令预算(fuel) | 每次执行 2,000,000 |
| 执行时长 | 每次执行 20 ms |
| 内存 | 4 MiB |
| 受控 HTTP 请求 | 每次执行最多 4 次,只能访问清单声明并由安装密钥绑定授权的逻辑目的地 |
| 扩展包 | 不超过 140 MiB;plugin.wasm 不超过 128 MiB;manifest.json 不超过 64 KiB |
| Lua 源码 | 不超过 2 KiB,UTF-8 |
执行出错按安装的失败策略处理;安全关键的扩展应配置 fail_closed。
2. 包合同
当前包合同为 zenova.gateway-plugin/v4,宿主接口版本为 zenova:[email protected]。
- 清单必须声明
supportedServiceKinds,取值为model(模型服务)、mcp(MCP 服务)、http_proxy(API 服务)或a2a(外部智能体)。 capabilities.executionSurface: "model"使用模型执行阶段,且只能声明model;通用请求头扩展使用request_header与P2_request_transform,可声明上述四类服务。- 后端校验声明与绑定;升级时移除仍有绑定的服务类型会被拒绝,提示先删除对应绑定。
身份头扩展读取宿主提供的 trusted.subject.iam_subject、trusted.subject.user_id、trusted.service.kind、trusted.service.id。身份缺失时保持缺失;与服务原生身份头同名的写入会被后端拒绝,调用方请求头不能伪造这些可信键。
3. 获取开发模板
租户管理员在管理控制台打开「网关扩展」,选择「下载 SDK」,选择 Rust 或 Lua、扩展标识和要实现的网关接口。页面下载与命令行 zr-plugin init 使用同一个模板生成器。
命令行入口(zr-plugin 位于产品源码 zstack-router/crates/zr-plugin):
zr-plugin init ./tenant-gate \
--language lua \
--world request-gate \
--plugin-id tenant-gateRust 把 --language 改为 rust。三个可选的接口(world):
| world | 作用 |
|---|---|
request-gate | 调用上游前放行或拒绝请求 |
request-transform | 修改发往上游的请求正文或请求头 |
gateway-response-transform | 增量检查流式响应,并声明可安全下发的文本前缀 |
4. 编写业务逻辑
Rust:模板把当前版本的 SDK 与 WIT 快照固定在 .zr/sdk/,已包含清单、示例逻辑和后续命令说明,只需编辑 src/lib.rs。构建前安装 stable Rust 与 wasm32-wasip2:
rustup target add --toolchain stable wasm32-wasip2Lua:不需要 Rust、WIT bindgen 或 WASM 工具链,只需编辑 plugin.lua。模板同时包含:
.zr/sdk/zr.lua:LuaLS / EmmyLua 类型标注;.zr/sdk/zr-mock.lua:21 个宿主函数的本地无副作用 mock;.luacheckrc:Lua 5.4 与zr全局声明;harness.lua:与所选 world 匹配的合法输入和结果断言。
本机装有 Lua 5.4 时可先运行 lua harness.lua 与 luacheck plugin.lua 获得快速反馈;安全与合同校验以下一步的 zr-plugin validate 为准。
Lua 上手快、适合快速迭代,但多一层解释执行,性能通常低于 Rust。对延迟和吞吐敏感的扩展优先用 Rust。
5. 构建与校验
zr-plugin build . --output ./dist/plugin.zip
zr-plugin validate ./dist/plugin.zipRust 构建使用 stable、--locked --release 与 wasm32-wasip2。Lua 构建把源码、字节数、SHA-256 和固定 runner 版本写进不可变清单,并装入该 world 的官方 Lua runner 组件,客户机器不编译 runner。
validate 与服务端上传共用同一个包校验器,并用生产宿主真实编译、预实例化和执行一次合成请求。Lua 包还会校验:
- 源码大小与 SHA-256 必须匹配;
- runner 版本必须是当前唯一受支持版本(
lua54-0.1.0); plugin.wasm必须逐字节等于该 world 的官方 runner,不能替换;- 脚本语法、入口函数、参数与返回值必须能在生产宿主中执行。
6. Lua 运行合同
Lua runner 固定使用 PUC Lua 5.4.8。每次执行创建独立 Lua 状态,只加载 table、string、utf8、math 标准库,Lua heap 另限 2 MiB;io、os、package、debug、load、loadfile、dofile、require 不可用。死循环会被宿主的指令预算或 20 ms 时限中断。
清单中的 Lua 源码是该扩展版本的唯一事实源,安装配置由租户单独维护。平台只接受 Lua 源码,不接受字节码。脚本内部可用 pcall 处理预期错误;未捕获的错误按绑定的失败策略处理。
zr 暴露与 WIT 同名的 21 个 snake_case 函数。当前宿主可用:日志、时钟、计数器、固定窗口限流、释放、心跳、Token 计数、受控 HTTP 请求、请求上下文与本地上下文。以下函数在 Lua 与 WIT 中有入口,但当前宿主会明确中断执行,不会伪装成功:
state_get、state_set、state_add、state_delete、state_expire;breaker_check、concurrency_acquire、verify_signature。
http_request 的 destination 是清单声明、由安装密钥绑定授权的逻辑目的地,不是任意 URL。
7. 发布
publish 没有默认目标,必须显式传 --server 或设置 LP_SERVER。凭据必须是具有网关扩展管理权限的用户令牌;智能体 API 密钥和模型调用 API 密钥不能发布扩展。
zr-plugin --server https://gateway.example.com \
publish ./dist/plugin.zip发布请求为 POST /api/v1/plugins/packages,multipart 字段为 package。服务端再次执行与本地相同的包、运行时、摘要、WIT world 和生产宿主校验,然后把制品与不可变版本清单接入扩展生命周期。也可以在「网关扩展」目录选择「上传插件」提交扩展包。
8. 安装与绑定
- 在「网关扩展」打开该扩展,在「权限」标签核对声明的网络目标、密钥要求、安全与 PII 要求。
- 在「配置」标签填写配置并安装。扩展以停用状态安装,状态显示「已生效」后再打开「启用」。
- 在「绑定」标签先选扩展声明支持的服务类型,再选「该类型全部」或指定服务多选。「全部」覆盖本租户该类型当前和以后新建的服务;模型以模型服务 ID 识别。
- 绑定配置按顶层键覆盖安装默认配置;指定服务的绑定优先于同一安装的「全部」绑定;同一次请求每阶段执行一次。流量规则引用只能进一步缩小已绑定范围。
- 扩展需要访问外部服务时,在「绑定」的密钥绑定中以逻辑名称引用密钥,并限定允许的目的地。
- 用命中与不命中的请求分别验证,再到「执行统计」核对执行次数、拦截、失败和耗时。