共享网关别再把所有 profile 都拉起来:multiplex_profile_allowlist 实战

你在一台服务器上跑一个 Discord 网关,机器里装了 5 个 profile:一个日常用的 default、一个给孩子用的受限 profile、两个开发用的。结果网关一启动,把所有 profile 的适配器和定时任务全拉起来了;更糟的是,某条消息本来该路由到受限 profile,它一时不可用,网关竟然悄悄退回 default 来处理——对受限场景来说,这可不是优雅降级,而是越过了安全边界。8 月 11 日合并的 PR #83400 补上了这个缺口:gateway.multiplex_profile_allowlist 让你明确圈定网关服务的 profile 集合,路由落空时直接拒绝(fail closed),而不是降级。
一个配置键,圈定服务范围
在 config.yaml 里加上白名单即可:
gateway:
multiplex_profiles: true
multiplex_profile_allowlist:
- worker
- guest
加上这 4 行之后,这个网关就只会启动并服务 default、worker、guest 三个 profile——机器上装的其他 profile 既不会被拉起,也不会暴露任何接口。
服务集合的完整语义
白名单的规则(官方文档 multi-profile-gateways.md)值得逐条过一遍,因为边界情况都处理好了:
default永远被服务,不需要列出来;- 不设置这个键 = 保持历史行为:服务所有已安装的命名 profile;
- 空列表
[]= 只服务default; - 名字会归一化、去重;无效条目或未安装的 profile 会被跳过并给出警告;非列表的畸形值会安全退化为“只服务 default”,不会崩;
- 服务集合同时决定
/p/<profile>/API 与 webhook 前缀、运行时状态、profile_routes路由资格,以及进程内 cron 调度器是否 tick 该 profile; - 白名单外的 profile 不会被这个网关启动,但它仍然可以自己单独跑一个独立网关——“不在共享网关里”不等于“不能用”。
Fail closed:路由落空时拒绝,而不是悄悄降级
这是这次改动里最有安全价值的部分。之前的风险是:profile_routes 里显式把某个频道/群路由到受限 profile,可一旦那个 profile 不可用(没装、坏了、不在服务集合里),流量会静默回退到 default。现在规则反过来了:
- 显式路由命中,但目标 profile 未安装或不在白名单 → 网关拒绝该消息并记录日志(含路由与目标 profile),绝不拿
default顶上; - 未匹配任何路由的流量,保持历史行为(走
default); profile_routes需要multiplex_profiles: true才生效。
真实场景:受限 profile 的共享网关
最典型的用法是“家庭共享网关”:同一个 Discord bot,普通频道走 default,孩子的频道走受限 profile。配置上分两层:
- 网关层:
multiplex_profile_allowlist只列default+kids-safe,profile_routes把孩子的频道路由到kids-safe; - profile 层:受限 profile 自己的
config.yaml里做加固——只启用它需要的工具集、不继承default的凭据等。profile 是独立配置目录,隔离本来就在这一层做。
最后一条边界要说清楚:官方文档明确提醒,profile 路由 + 白名单不是完整的安全沙箱。需要操作系统级的硬边界时(比如代码要跑在隔离环境里),还是用独立进程或容器隔离,别指望配置层面的路由来兜底。
小结
multiplex_profile_allowlist 是个小改动,解决的是共享网关的真实痛点:装了一堆 profile,网关却“一视同仁”全部拉起,路由落空还悄悄降级。现在你可以精确声明“这个网关只服务谁”,并把降级行为改成拒绝。想了解 profile 多实例的完整概念,可以看多开指南;profile 路由在飞书场景的具体用法见那篇文章;相关命令面在 hermes-gateway 与 hermes-profile 参考页。