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

146 lines
6.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# chongming-gateway
chongming 网关本体:基于 **FastAPI + Zenoh** 的配置驱动微服务网关。
把宿主项目里的插件(inline 进程内 / external 独立进程)发布的路由统一挂载为 HTTP 端点,
并提供 Worker 服务发现、负载均衡、超时重试、实例定向、MQTT Bridge、内置登录鉴权
与机器码授权(chongming-license)。
网关只提供**代码**:配置、插件与打包骨架由宿主项目提供(见「运行时约定」)。
---
## 安装
```bash
# 作为依赖(宿主项目,从私有源安装)
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>`)可导入。之后任选一种启动方式:
```bash
# 按 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]` 段(缺省视为未配置,不启用校验):
```toml
[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 也能正常出包**(非授权版不是"拒绝所有插件"的砖头包),
授权版则无法被现场改配置/环境变量关掉。宿主打包命令:
```bash
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` 发给厂商即可。
## 目录结构
```text
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 运行)
```
## 开发与发布
```bash
# 在 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 指向目标版本。
## 相关文档
- [项目主 README](../../README.md):整体架构、使用与构建部署
- [Worker 管理与多实例运维](../../docs/worker-management.md):管理端点、扩缩容、实例定向
- [机器码授权指南](../../docs/license.md):密钥管理、签发/激活流程、指纹容错与命令行参考