三处收口,未授权插件不加载、不暴露端点、不可管理:
- 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 项:门禁/契约存活/描述符拦截/管理接口/上传安全/烘焙三态,自造夹具直接运行)。
146 lines
6.7 KiB
Markdown
146 lines
6.7 KiB
Markdown
# 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):密钥管理、签发/激活流程、指纹容错与命令行参考
|