Files
chakcy 93fed12d14 feat: 集成 chongming-license 机器码授权门禁(打包可选、授权版不可关)
三处收口,未授权插件不加载、不暴露端点、不可管理:
- core/licensing:授权管理器单例 + 构建期烘焙决策(chongming/_build_flags.py,
  随代码编译、部署侧不可改);开发/测试默认放行,CHONGMING_LICENSE_MODE 可临时开
- lifespan_setup:_scan_plugins(盘扫)与 _discover_plugins(授权收口)分离,
  未授权插件连同 TOML 路由/MQTT 主题/副本数一起从启动流程消失(契约校验因此
  不会硬失败),启动打印授权横幅
- route_registry.set_denied_types:未授权类型经 Zenoh 推来的描述符一律忽略
  (否则把 load_mode 改成 external 即可绕过)
- worker_admin:扩缩容/重启/读改配置对未授权插件一律 403,实例盘点附 licensed 标注
- routes/licensing:/license/{info,reload,file}(仅超管),导入先验签后原子落盘,
  坏文件不覆盖现有授权;导入不即时加载插件,如实告知需重启

打包可选:不加 --license 出"非授权版"(不校验、插件照常加载);授权版强制校验
且公钥随代码内置(现场换 config.toml 公钥自签也无效)。未烘焙的旧打包产物沿用
"打包即强制"的旧语义;烘焙标记只在产物内生效,不影响源码运行。

补依赖 greenlet(chongming-auth→database 的 sqlalchemy asyncio import 期即要求)
与 python-multipart(上传端点注册路由即要求);新增 test/test_license_gate.py
(52 项:门禁/契约存活/描述符拦截/管理接口/上传安全/烘焙三态,自造夹具直接运行)。
2026-10-14 10:57:09 +08:00

6.7 KiB
Raw Permalink Blame History

chongming-gateway

chongming 网关本体:基于 FastAPI + Zenoh 的配置驱动微服务网关。

把宿主项目里的插件(inline 进程内 / external 独立进程)发布的路由统一挂载为 HTTP 端点, 并提供 Worker 服务发现、负载均衡、超时重试、实例定向、MQTT Bridge、内置登录鉴权 与机器码授权(chongming-license)。

网关只提供代码:配置、插件与打包骨架由宿主项目提供(见「运行时约定」)。


安装

# 作为依赖(宿主项目,从私有源安装)
uv add chongming-gateway

# 开发期可编辑安装(在 chongming-core 仓库内)
uv pip install -e utils/gateway

网关自带完整依赖清单(fastapi / granian / zenoh / chongming-worker 等), 宿主项目无需重复声明;需要哪些第三方包,见本包 pyproject.toml。

快速开始

宿主项目(cwd)下准备 config.toml 与 config/workers/<type>/config.toml, 并保证插件包(plugins.<type>)可导入。之后任选一种启动方式:

# 按 config.toml [<env>.server] 启动(三个入口等价,仅默认服务器不同)
uv run serve            # uvicorn
uv run granian          # granian
uv run gunicorn         # gunicorn + uvicorn worker

# 或把 ASGI 应用交给任意服务器
uvicorn chongming.app:app --host 0.0.0.0 --port 8000

网关按 config/workers/<name>/config.toml 的 [workers] 节逐插件读取加载方式 (load_mode = "inline" | "external")与副本数,启动时自动发现并挂载路由。

运行时约定

  • 配置:cwd 下 config.toml(环境、服务器、安全、MQTT、缓存、授权等)与 config/workers/**(插件注册与路由定义),运行期读取,随宿主项目部署。
  • 插件:网关按 plugins.<type> 约定导入插件包,来源不限——.mbank 模块银行、 源码目录(开发态 src/plugins)或已安装包均可。
  • 工作目录:日志、上传、数据等相对路径均以 cwd 为基准。

机器码授权(chongming-license)

厂商用私钥离线签发 license.lic(绑定机器指纹 + 插件白名单 + 有效期), 网关内置公钥验签,全程无需联网。未授权的插件不加载、不注册路由 (external 插件经 Zenoh 推来的描述符同样被忽略)、管理接口一律 403。 是否启用校验见下方「打包」一节:非授权版产物完全不校验,授权版产物强制校验。

宿主 config.toml 的 [license] 段(缺省视为未配置,不启用校验):

[license]
# 是否执行拒绝。**只在非打包态生效**(uv run serve / 测试)——打包产物的
# 开关由构建期烘焙决定(部署目录的 config.toml 明文可写,以它为准等于放行
# 现场改一行就绕过授权)
enabled = false
path = "license.lic"                # 授权文件路径(相对网关工作目录)
public_key = ""                     # 厂商公钥(base64 裸 32 字节或 PEM);由构建脚本注入,
                                    # 授权版还会把公钥烘焙进产物代码,此处仅作镜像/兜底
min_match = 3                       # 指纹命中阈值(N-of-M),容忍单块网卡/eMMC 更换

非打包态也可用 CHONGMING_LICENSE_MODE=on(或 off)临时开启/关闭, 便于在开发机上验证门禁逻辑。

打包:license 是可选构建项

"是否强制校验"由构建期烘焙决定,而不是运行时可写的配置 —— 打包脚本在编译前 生成 chongming/_build_flags.py(随代码编译进产物,部署侧不可改):

构建方式 产物 行为
不加 --license 非授权版 不校验授权,插件照常加载(与开发态一致)
--license + 厂商公钥 授权版 强制校验;公钥随代码内置,现场换 config.toml 里的公钥也无法自签绕过
旧打包脚本(未烘焙) 兼容旧语义 打包即强制校验

即不配 license 也能正常出包(非授权版不是"拒绝所有插件"的砖头包), 授权版则无法被现场改配置/环境变量关掉。宿主打包命令:

uv run build --encrypt --license      # 授权版产物(读 CHONGMING_LICENSE_PUBKEY / license_public.key)
uv run build --encrypt                # 非授权版产物:不校验、插件照常加载

烘焙标记只对打包产物生效——源码运行(含可编辑安装)仍只由上面的配置/环境变量 决定,不会因为上次打了授权版包就把开发态也锁上。

授权管理端点(/api/v1/license/...,仅超级管理员):

端点 用途
GET /license/info 机器码、申请码、授权状态、各插件授权情况与硬件来源明细
POST /license/reload 重新读取磁盘上的授权文件(不改插件加载状态)
POST /license/file 导入 license.lic:先验签/机器码/有效期,通过才原子替换

导入接口先校验后落盘:坏文件一律拒绝且不覆盖现有可用授权;通过校验但 白名单含此前未授权的插件时,需重启网关才会加载(响应里的 restart_required 如实告知)。申请授权时把 /license/info 的 request_code 发给厂商即可。

目录结构

src/chongming/
├── __init__.py        # 启动入口(serve / granian / gunicorn)与服务器参数转换
└── app/
    ├── __init__.py    # FastAPI 应用装配(lifespan、中间件、异常处理)
    ├── core/          # WorkerManager / RouteGenerator / MQTT Bridge / 日志 / 鉴权 / 授权门禁
    └── routes/        # 内置管理端点(stats / worker_admin / auth / license)

test/
└── test_license_gate.py   # 授权门禁测试(自造夹具,直接 python 运行)

开发与发布

# 在 chongming-core 仓库内改代码后,让 venv 用本地源码(联调用)
uv pip install -e utils/gateway

# 授权门禁测试(自造夹具,可在任意目录直接运行)
python test/test_license_gate.py

# 发版:构建 wheel 并推私有源
uv build                                    # dist/chongming_gateway-<version>-py3-none-any.whl
uv run twine upload --repository <私有源> dist/*

发版后依赖方执行 uv lock --upgrade-package chongming-gateway 刷新版本 pin。 注意 uv sync 会用锁文件版本替换可编辑安装——打包/发版前先确认 venv 指向目标版本。

相关文档