feat(ca): 认证独立化 CA 模块 v1.0.0 + gateway v0.9.0 纯转发瘦身
拓扑:waf:443 → ca:8443(认证+前置转发)→ gateway:8445(仅 loopback 纯转发), WAF upstream 不变,core/dnfs2/前端零改动。 ca v1.0.0(新模块): - 认证资产逐字搬迁自 gateway v0.8.0(9 条认证路由、Token 双轨吊销、 PBKDF2、限流、WS 代理鉴权、wrapperHttp、优雅关闭) - handleForward 前置转发层:固定上游 gateway:8445;无 Target-Url 公开直转, 有则本地验签后注入 X-Auth-User/X-Auth-Admin/Authorization(strip-then-set) - is_special_header 新增剥离 x-auth-user/x-auth-admin;上游不可达统一 502 - confs/configs/ca.ini 新增([Upstream] gateway + UPSTREAM_GATEWAY env) gateway v0.9.0(2470→约 560 行): - 删除全部认证代码,退居 127.0.0.1:8445 - 启动 loopback 强制自检(非 loopback 拒绝启动) - X-Auth-User 存在性不变量:显式 Target-Url 无该头一律 401 - gateway.ini 精简至 [Server]/[Proxy]/[Downstream db] 验收:黑盒 34 断言对 8443 原样全绿(32 passed/2 skipped,与基线一致); 探针实测伪造 X-Auth-User 经 CA 被剥离(echo 实证)、直连 8445 无头 401、 白名单外 403、gateway 宕机 CA 502。core/init.txt 加入 ca/_run.sh。
This commit is contained in:
1 parent
bbea1ee9d8
commit
407ce3c366
19 files changed
+3991
-2523
No files matched your search
+265
-283
@@ -1,136 +1,146 @@
|
||||
# 权限系统独立化计划书
|
||||
# 认证独立化(CA 模块)开发文档
|
||||
|
||||
## 一、现状分析
|
||||
**日期**: 2026-08-09(v2 重写)
|
||||
**背景**: gateway v0.8.0 单文件 [main.cpp](../../gateway/src/main.cpp)(2470 行),认证逻辑(约 65%)与反向代理耦合;core/dnfs2 通过 HTTP 回调 gateway 验签,gateway 既是流量入口又是认证权威,形成环形依赖
|
||||
**范围**: `ca`(新模块)、`gateway`(瘦身为纯代理);**WAF / core / dnfs2 / static / 前端零改动**
|
||||
**前版**: 2026-08-08 初版计划书(auth 双栈方案)。本版按已选定拓扑重写:**数据流 `waf → ca → gateway`,gateway 不再验签**。原双栈/后置方案见 §2.2 决策记录
|
||||
|
||||
### 1.1 当前架构
|
||||
---
|
||||
|
||||
## 0. 摘要
|
||||
|
||||
新建 CA(Central Auth)模块:C++20 服务,接管 gateway 当前监听的 `127.0.0.1:8443`(WAF upstream 不变),承载**全部认证面**(登录/注册/Token 生命周期/CAPTCHA 代理/限流/WS 代理),并作为**认证前置层**:对业务请求本地验签后注入可信身份头,转发给退居 `127.0.0.1:8445` 的 gateway。gateway 删除全部认证代码,不再验签,仅保留白名单反代。
|
||||
|
||||
核心收益:验签在 CA 本地完成(RSA 密钥同进程,**零新增网络跳**);core/dnfs2 仍调 `8443` 的 `/admin/verify`、`/whoami`,**无感知切换**;前端 URL 不变;现有黑盒 34 断言原样充当验收门。
|
||||
|
||||
---
|
||||
|
||||
## 1. 现状分析
|
||||
|
||||
### 1.1 当前调用链
|
||||
|
||||
```
|
||||
客户端 ──HTTPS──▶ WAF:443 ──HTTP──▶ gateway:8443 ──HTTP──▶ 下游服务
|
||||
( auth + proxy + captcha + rate-limit + ws-proxy )
|
||||
│
|
||||
┌───────────────┼───────────────┐
|
||||
▼ ▼ ▼
|
||||
db:8005 captcha:8009 smtp:8008
|
||||
(Users表) (图形验证码) (邮件发送)
|
||||
客户端 ──HTTPS──▶ WAF:443 ──HTTP──▶ gateway:8443 ──HTTP──▶ static:9200 / dnfs2:8006 / core:5200
|
||||
│ ├─ 本地验签(RSA 内存密钥)+ Cookie 回退 + Authorization 注入
|
||||
│ ├─ /login /register /logout /refresh_token /whoami /admin/verify
|
||||
│ ├─ /register/send_code /reset/send_code /reset_password
|
||||
│ ├─ GET /captcha/image(pymsg 代理 captcha2:8009)
|
||||
│ └─ WS 代理 /ws/aibox(握手验签 is_admin)
|
||||
└── pymsg: db:8005 / captcha2:8009 / smtp3:8008 / confs:8010
|
||||
|
||||
下游鉴权调用链:
|
||||
core:5200 ──HTTP POST──▶ gateway:/admin/verify (管理员验签)
|
||||
dnfs2:8006 ──HTTP POST──▶ gateway:/whoami (Token 验签)
|
||||
下游验签回调(HTTP 回环):
|
||||
core:5200 ──HTTP──▶ gateway:8443/admin/verify (HttpTask.cpp,GATEWAY_HOST/GATEWAY_PORT 可配,fail-closed)
|
||||
dnfs2:8006 ──HTTP──▶ gateway:8443/whoami (gateway_url 配置项,fail-closed)
|
||||
```
|
||||
|
||||
### 1.2 Gateway 职责清单(main.cpp 2508 行,单文件)
|
||||
### 1.2 gateway 职责盘点与归属
|
||||
|
||||
| 职责域 | 函数/组件 | 行数占比 |
|
||||
|--------|-----------|---------|
|
||||
| **用户认证** | handleLogin, handleRegister, handleLogout, handleResetPassword, handleSendEmailCode, handleResetSendCode, PBKDF2Hasher, PasswordValidator | ~40% |
|
||||
| **Token 管理** | verify_token, generate_jti, revoke_jti, revoke_all_user_tokens, handleRefreshToken, handleWhoami, handleAdminVerify, TokenInfo, JtiDenylist, UserVerMap | ~25% |
|
||||
| **CAPTCHA** | handleCaptcha, handleCaptchaImage, CaptchaEntry, generate_captcha_text, verify_captcha | ~8% |
|
||||
| **速率限制** | 8 个 RateLimiter 实例(login/register/email/refresh/reset 等) | ~5% |
|
||||
| **HTTP 反向代理** | handleProxy, is_special_header, is_safe_target | ~12% |
|
||||
| **WebSocket 代理** | verifyWsAdminToken, ws_proxy 路由注册 | ~5% |
|
||||
| **配置/基础设施** | main(), registerRoutes(), ConfIni, DbC, LogC, 信号处理 | ~5% |
|
||||
| 职责域 | 函数/组件 | 归属 |
|
||||
|--------|-----------|------|
|
||||
| 用户认证 | `handleLogin` / `handleRegister` / `handleLogout` / `handleResetPassword` / `handleSendEmailCode` / `handleResetSendCode` | → CA |
|
||||
| Token 管理 | `verify_token` / `generate_jti` / `revoke_jti` / `revoke_all_user_tokens` / `handleRefreshToken` / `handleWhoami` / `handleAdminVerify` / jti 黑名单 / 用户版本号表 / RSA 密钥对 | → CA |
|
||||
| 密码体系 | `PBKDF2Hasher` / `PasswordValidator` / 防枚举 dummy 校验 | → CA |
|
||||
| 验证码/邮箱 | `handleCaptchaImage` / `verify_captcha_remote` / `verify_email_code_remote` | → CA |
|
||||
| 速率限制 | 全部 RateLimiter 实例(登录 5/5min、注册 3/10min、发码 5/10min + 邮箱级 3/h+10/d、验证码 10/min 等) | → CA |
|
||||
| WS 代理 | `verifyWsAdminToken` + `_WS_PROXY_ROUTES` 注册循环 | → CA(验签+桥接一体) |
|
||||
| Cookie | `q4_token` 签发/清除、`extract_token_from_request`(Cookie 回退)、代理时 `Authorization: Bearer` 注入 | → CA |
|
||||
| HTTP 反代 | `handleProxy`(白名单/路径安全/头转发/XFF) | 留 gateway(去验签化) |
|
||||
| 头卫生 | `is_special_header` / `is_safe_target` | 留 gateway |
|
||||
| 基础设施 | `wrapperHttp` / `ConfIni` / `DbC` / `LogC` / 信号处理 / 优雅关闭 | 两侧各持一份(模板见 smtp3) |
|
||||
|
||||
### 1.3 核心问题
|
||||
|
||||
1. **职责严重耦合**:auth(~65%代码量)与 proxy(~17%)捆绑在同一进程,任一域的重构/扩展都影响全局
|
||||
2. **不可独立扩展**:认证密集型场景(批量登录/注册)和代理密集型场景(大文件传输)无法分别扩容
|
||||
3. **安全隔离缺失**:密码哈希、RSA 私钥、Token 黑名单等敏感数据与代理逻辑共存于同一进程内存空间
|
||||
4. **下游耦合 gateway**:core/dnfs2 通过 HTTP 回调 gateway 做验签,gateway 既是流量入口又是认证权威,形成环形依赖
|
||||
5. **单文件维护瓶颈**:2508 行 C++ 单文件,所有功能交叉耦合,测试和 code review 成本高
|
||||
6. **Token 状态纯内存**:jti 黑名单和版本号在 gateway 重启后丢失(虽然 RSA 密钥同步重建可兜底,但限制了多实例部署)
|
||||
1. **职责耦合**:认证与代理同进程,任一域改动爆炸半径为全文件
|
||||
2. **环形依赖**:gateway 是流量入口又是验签权威,core/dnfs2 须 HTTP 回调入口
|
||||
3. **敏感面混杂**:RSA 私钥、密码哈希校验、jti 黑名单与转发逻辑共用内存空间
|
||||
4. **不可独立扩展**:认证密集与代理密集负载无法分别扩容;认证状态纯内存,绑定单实例
|
||||
|
||||
---
|
||||
|
||||
## 二、目标架构
|
||||
## 2. 目标架构
|
||||
|
||||
### 2.1 拓扑
|
||||
|
||||
```
|
||||
客户端 ──HTTPS──▶ WAF:443 ──HTTP──▶ gateway:8443 ──HTTP──▶ 下游服务
|
||||
( 纯反向代理 + Token 透传 )
|
||||
│
|
||||
▼
|
||||
auth:8444 (独立权限服务)
|
||||
┌────┼────┐
|
||||
▼ ▼ ▼
|
||||
db:8005 captcha smtp
|
||||
|
||||
下游鉴权调用链(改造后):
|
||||
core:5200 ──pymsg──▶ auth:8444:/verify (管理员验签)
|
||||
dnfs2:8006 ──pymsg──▶ auth:8444:/whoami (Token 验签)
|
||||
gateway ──pymsg──▶ auth:8444:/verify (代理转发前的 Token 校验)
|
||||
客户端 ──HTTPS──▶ WAF:443 ──HTTP──▶ CA:8443 ──HTTP──▶ gateway:8445 ──▶ static:9200 / dnfs2:8006 / core:5200
|
||||
│
|
||||
├─ 本地处理:/login /register /logout /refresh_token /whoami /admin/verify
|
||||
│ /register/send_code /reset/send_code /reset_password
|
||||
│ GET /captcha/image
|
||||
├─ WS:/ws/aibox 握手验签(is_admin) → 直接桥接 ws://127.0.0.1:8765
|
||||
├─ 其余路径:本地验签 → 注入 X-Auth-User/Authorization → 转发 gateway:8445
|
||||
└─ pymsg: db:8005 / captcha2:8009 / smtp3:8008 / confs:8010
|
||||
|
||||
端口安排:CA 接管 8443(WAF upstream 配置不变);gateway 迁 8445(仅 loopback,仅 CA 可达)。
|
||||
```
|
||||
|
||||
### 2.1 职责划分
|
||||
职责划分:**CA = 认证权威 + 认证前置代理**;**gateway = 纯路由/转发策略**(opened 白名单、路径安全、XFF 透传)。
|
||||
|
||||
| 模块 | 改造后职责 |
|
||||
|------|-----------|
|
||||
| **auth(新)** | 用户注册/登录/注销、Token 签发/验签/刷新/吊销、CAPTCHA、密码重置、邮箱验证码、速率限制、管理员验证 |
|
||||
| **gateway(精简)** | HTTP/WebSocket 反向代理、Target-Url 白名单过滤、XFF 注入、CORS 预检;认证路由(/login, /register 等)作为代理目标指向 auth |
|
||||
| **core / dnfs2** | 验签调用目标由 gateway 改为 auth(pymsg 协议,消除 HTTP 回调) |
|
||||
### 2.2 决策记录
|
||||
|
||||
| 方案 | 拓扑 | 结论 |
|
||||
|------|------|------|
|
||||
| **选定:CA 前置** | `waf → ca → gateway` | ✅ 验签在 CA 本地(零网络跳);core/dnfs2 调 8443 不变;WAF/前端不变;gateway 无需新增路径路由机制 |
|
||||
| 否决 A:CA 后置双栈 | `waf → gateway → ca`(HTTP+pymsg 双栈) | gateway 需新增路径级路由表;CORS/Set-Cookie/错误映射下沉 CA;两服务改动面都大 |
|
||||
| 否决 B:CA 后置纯 pymsg | `waf → gateway →(pymsg) ca` | gateway 保留 HTTP↔pymsg 翻译层与 Cookie 职责,认证面仍在入口进程内,瘦身不彻底;且每请求多一跳 pymsg 验签或引入吊销延迟的缓存 |
|
||||
| 否决 C:验签缓存 | gateway 缓存验签结果 60s | logout/改密后 Token 在窗口内仍可用,吊销语义弱化(随拓扑选定自然消失) |
|
||||
|
||||
### 2.3 信任模型(重要)
|
||||
|
||||
1. **loopback 信任基线不变**:本架构 loopback 内部服务本就不互鉴权(db:8005 execute 无认证即先例)。gateway:8445 仅绑 `127.0.0.1`,与本基线一致。
|
||||
2. **身份头契约(strip-then-set)**:CA 在边缘剥离客户端伪造的 `X-Auth-User` / `X-Auth-Admin`(Beast fields 大小写不敏感,`erase` 后 `set`),验签通过才注入。模式 A 下 CA 的唯一上游是 WAF(loopback)。
|
||||
3. **gateway 存在性不变量**:显式 `Target-Url` 请求必须携带 `X-Auth-User`,否则 401(非验签,仅防线编程,防本地误直连/SSRF 兜底)。
|
||||
4. **模式 B 警示迁移**:CA 直连公网(无 WAF)时 XFF 可被伪造、限流可绕过——原 gateway README 的 A4 降级提示随限流一并迁入 CA README;根治方案 [libhttp_remote_addr_dev_doc.md](libhttp_remote_addr_dev_doc.md) 中的 `trust_xff`/`rate_limit_key()` 配套改在 CA 落地。
|
||||
|
||||
---
|
||||
|
||||
## 三、实施阶段
|
||||
## 3. CA 模块设计
|
||||
|
||||
### 阶段一:auth 服务搭建(核心剥离)
|
||||
### 3.1 目录与构建(沿用 smtp3/captcha2 约定)
|
||||
|
||||
**目标**:将认证逻辑整体迁入独立 C++ 服务
|
||||
|
||||
**新建目录结构**:
|
||||
```
|
||||
auth/
|
||||
├── src/
|
||||
│ └── main.cpp # 入口 + 路由注册
|
||||
ca/
|
||||
├── src/main.cpp # 入口 + HTTP 路由 + 认证逻辑 + 前置转发(迁移代码为主)
|
||||
├── inc/
|
||||
│ ├── AuthService.hpp # 核心认证逻辑(PBKDF2、Token 签发/验签/吊销)
|
||||
│ ├── RateLimiter.hpp # 速率限制器(从 gateway 迁移)
|
||||
│ ├── CaptchaService.hpp # CAPTCHA 生成/验证
|
||||
│ ├── DbC.hpp # 复用 gateway 的 DbC(提取为公共库或复制)
|
||||
│ ├── LogC.hpp # 复用 LogC
|
||||
│ ├── Conf.hpp # 复用 ConfIni
|
||||
│ ├── LocalTime.hpp # 复用
|
||||
│ └── ExpBase.hpp # 复用
|
||||
├── Makefile
|
||||
├── _run.sh
|
||||
├── _set.sh
|
||||
│ ├── Conf.hpp # ConfIni(confs 拉取 + env 覆盖 + 完整性校验)
|
||||
│ ├── DbC.hpp # 自 gateway 随迁(已基于 libmsg us/Msg.hpp;client 名 "gateway"→"ca")
|
||||
│ ├── LogC.hpp # 异步日志(smtp3 长连接版)
|
||||
│ ├── ExpBase.hpp # 异常基类
|
||||
│ └── LocalTime.hpp # 时间工具
|
||||
├── Makefile # 链接 us_http / us_msg / us_crypt / ssl / crypto / boost_json / curl
|
||||
├── _set.sh / _run.sh
|
||||
└── README.md
|
||||
```
|
||||
|
||||
**迁移清单**(从 gateway/src/main.cpp 提取):
|
||||
- 二进制 `ca.bin`;版本从 `1.0.0` 起;日志服务名 `ca`
|
||||
- 依赖:libhttp(HttpServer/HttpClient/ws_proxy)、libmsg(MsgClient/RpcClient)、libcrypt(MyRSA/Base64)、OpenSSL(PBKDF2/RAND_bytes)、boost::json/program_options/property_tree、libcurl
|
||||
- **不依赖 libsqlite3**:Users 表访问全部经 db:8005(现状即如此)
|
||||
|
||||
| 组件 | 源位置 | 目标文件 |
|
||||
|------|--------|---------|
|
||||
| PBKDF2Hasher | main.cpp L62-85 | AuthService.hpp |
|
||||
| PasswordValidator | main.cpp L87-160 | AuthService.hpp |
|
||||
| TokenInfo / generate_jti / verify_token / revoke_* | main.cpp L340-760 | AuthService.hpp |
|
||||
| handleLogin | main.cpp L1346-1525 | main.cpp (auth) |
|
||||
| handleRegister | main.cpp L1891-1981 | main.cpp (auth) |
|
||||
| handleLogout | main.cpp L1527-1554 | main.cpp (auth) |
|
||||
| handleWhoami | main.cpp L1556-1606 | main.cpp (auth) |
|
||||
| handleAdminVerify | main.cpp L1608-1657 | main.cpp (auth) |
|
||||
| handleRefreshToken | main.cpp L1672-1837 | main.cpp (auth) |
|
||||
| handleSendEmailCode | main.cpp ~L1983-2080 | main.cpp (auth) |
|
||||
| handleResetSendCode | main.cpp ~L2080-2170 | main.cpp (auth) |
|
||||
| handleResetPassword | main.cpp ~L2170-2308 | main.cpp (auth) |
|
||||
| handleCaptcha / handleCaptchaImage | main.cpp ~L1158,1845-1890 | CaptchaService.hpp |
|
||||
| RateLimiter 实例(8 个) | main.cpp L417-427 | RateLimiter.hpp |
|
||||
| CAPTCHA_STORE | main.cpp L438-499 | CaptchaService.hpp |
|
||||
| JTI_DENYLIST / USER_VER_MAP | main.cpp L447-465 | AuthService.hpp |
|
||||
### 3.2 配置(`confs/configs/ca.ini` 新增)
|
||||
|
||||
**新增配置**(`confs/configs/auth.ini`):
|
||||
```ini
|
||||
[Server]
|
||||
address = 127.0.0.1
|
||||
port = 8444
|
||||
port = 8443 # 接管 gateway 现端口,WAF upstream 不变
|
||||
|
||||
[Token]
|
||||
[Upstream]
|
||||
gateway = http://127.0.0.1:8445 # 业务请求唯一上游
|
||||
|
||||
[Token] # 自 gateway.ini 原样迁入
|
||||
keep_time = 3600
|
||||
refresh_grace_multiplier = 2
|
||||
max_refresh = 10
|
||||
|
||||
[Admin]
|
||||
[Admin] # 原样迁入
|
||||
users = 1776177662@qq.com;277665039@qq.com;
|
||||
|
||||
[Downstream]
|
||||
[WsProxy] # 自 gateway.ini 迁入
|
||||
routes = /ws/aibox=ws://127.0.0.1:8765;
|
||||
|
||||
[CORS] # 原样迁入(P2-3:预检反射由 libhttp 处理,白名单暂仍不生效)
|
||||
allowed_origins = https://qsont.me;https://www.qsont.me;http://127.0.0.1:8443;http://localhost:8443
|
||||
|
||||
[Downstream] # 原样迁入
|
||||
db_host = 127.0.0.1
|
||||
db_port = 8005
|
||||
captcha_host = 127.0.0.1
|
||||
@@ -139,235 +149,207 @@ smtp_host = 127.0.0.1
|
||||
smtp_port = 8008
|
||||
```
|
||||
|
||||
**对外接口**(pymsg TCP 协议 + HTTP 双栈):
|
||||
环境变量覆盖(与 gateway 现机制一致):`LISTEN_ADDR`/`LISTEN_PORT`、`UPSTREAM_GATEWAY`、`DB_*`、`CAPTCHA_*`、`SMTP_*`、`CONFS_HOST`/`CONFS_PORT`。配置加载:confs `get` 拉取(retry_on_refused 阻塞)→ env 覆盖 → 完整性校验。
|
||||
|
||||
| 路由 | 协议 | 说明 |
|
||||
### 3.3 HTTP 路由表(对外契约与原 gateway 完全一致)
|
||||
|
||||
| 路由 | 处理 | 说明 |
|
||||
|------|------|------|
|
||||
| POST /login | HTTP | 登录(兼容前端直访) |
|
||||
| POST /register | HTTP | 注册 |
|
||||
| POST /logout | HTTP | 注销 |
|
||||
| POST /refresh_token | HTTP | Token 刷新 |
|
||||
| POST /whoami | HTTP | 身份查询 |
|
||||
| POST /register/send_code | HTTP | 发送注册验证码 |
|
||||
| POST /reset/send_code | HTTP | 发送重置验证码 |
|
||||
| POST /reset_password | HTTP | 重置密码 |
|
||||
| POST /captcha | HTTP | 生成验证码 |
|
||||
| GET /captcha/image | HTTP | 验证码图片代理 |
|
||||
| verify | pymsg | 内部验签(替代 /admin/verify,供 core/dnfs2/gateway 调用) |
|
||||
| whoami | pymsg | 内部身份查询(供 dnfs2 调用) |
|
||||
| POST `/login` | 本地 | 强制远端 CAPTCHA;dummy PBKDF2 防枚举;签发 Token + `q4_token` Cookie |
|
||||
| POST `/register` | 本地 | 密码强度→邮箱格式→CAPTCHA→邮箱验证码(smtp fail-closed)→写 Users |
|
||||
| POST `/register/send_code`、`/reset/send_code` | 本地 | IP+邮箱级限流、强制 CAPTCHA、smtp 60s 冷却 429 透传 |
|
||||
| POST `/reset_password` | 本地 | 防枚举恒成功文案;改密后版本号递增全设备吊销 |
|
||||
| POST `/logout` | 本地 | jti 吊销 + 清 Cookie |
|
||||
| POST `/refresh_token` | 本地 | 宽限期刷新、旧 jti 吊销、max_refresh 上限 |
|
||||
| POST `/whoami` | 本地 | 恒 200,`{login:false}` 不泄露细节;core/dnfs2 依赖此契约 |
|
||||
| POST `/admin/verify` | 本地 | fail-closed 401;core 依赖此契约 |
|
||||
| GET `/captcha/image` | 本地 | pymsg 调 captcha2,PNG + `X-Captcha-Token` + `no-store`,10/min/IP |
|
||||
| WS `[WsProxy]` 路径 | 本地 | 握手验签 `is_admin=true` → 桥接后端(ws/wss),双向透传 |
|
||||
| GET/POST `*`(通配) | **转发** | 见 §3.4 |
|
||||
|
||||
### 阶段二:gateway 瘦身
|
||||
### 3.4 前置转发层(通配路由 → gateway:8445)
|
||||
|
||||
**目标**:gateway 仅保留反向代理 + WebSocket 代理
|
||||
迁移 `handleProxy` 的转发骨架,认证语义前置:
|
||||
|
||||
**保留内容**:
|
||||
- `handleProxy`(HTTP 反代 + Token 透传)
|
||||
- WebSocket 代理路由注册 + `verifyWsAdminToken`(改为调 auth 的 pymsg verify 接口)
|
||||
- `is_special_header` / `is_safe_target` / `get_cookie_value`
|
||||
- 配置加载、信号处理、LogC
|
||||
1. **路径安全**:`is_safe_target()` 校验(`..`/`%2e`/`%2f`/`%5c`/空字节 → 400),CA/gateway 双层各执行一次(防线叠加,gateway 侧保留原校验)。
|
||||
2. **公开 vs 受保护**:无 `Target-Url` 头 → 公开目标(Proxy.default 语义在 gateway),**剥离**客户端 `X-Auth-User`/`X-Auth-Admin` 后直接转发,不注入任何凭证。
|
||||
3. **受保护**:有 `Target-Url` → 提取 Token(`Authorization` 优先,回退 `q4_token` Cookie)→ `verify_token` 本地验签(RSA→过期→jti→版本号)→ 失败按现契约 400/401,**不转发**;成功则注入:
|
||||
- `Authorization: Bearer <token>`(Cookie 来源也统一补齐,dnfs2 等下游零改动)
|
||||
- `X-Auth-User: <email>`、`X-Auth-Admin: true`(仅管理员;供 gateway 不变量与下游未来免回调使用)
|
||||
4. **头卫生**:剥离客户端自带的 `X-Auth-User`/`X-Auth-Admin`(strip-then-set);XFF 透传 WAF 值(缺省 `unknown`);`Host`/`connection`/`Accept-Encoding` 等逐跳头处理沿用现 `is_special_header` 集合 + `x-auth-user`/`x-auth-admin` 新增。
|
||||
5. **转发失败语义**:gateway:8445 不可达 → 502(与现 captcha/smtp fail-closed 风格一致),记 warning 日志。
|
||||
6. **响应**:状态码与响应头原样回传(现 `handleProxy` 行为),`Connection: close`。
|
||||
|
||||
**删除内容**:
|
||||
- 所有 auth handler(handleLogin ~ handleResetPassword)
|
||||
- PBKDF2Hasher / PasswordValidator / TokenInfo / JTI_DENYLIST / USER_VER_MAP
|
||||
- CAPTCHA 相关代码
|
||||
- 8 个 RateLimiter 实例(迁移到 auth,auth 路由不再经过 gateway 限流)
|
||||
- RSA 密钥对生成(`_KEYGEN`,迁移到 auth)
|
||||
### 3.5 迁移资产清单(gateway `main.cpp` → CA `main.cpp`)
|
||||
|
||||
代码以**整体搬迁**为主,保持实现逐字不变(降低回归面),仅做两处适配:日志服务名 `gateway`→`ca`;DbC client 名同上。
|
||||
|
||||
| 资产 | 备注 |
|
||||
|------|------|
|
||||
| PBKDF2Hasher / PasswordValidator / 常数时间比较 | 含 SHA256 旧哈希迁移注释块(保持注释状态) |
|
||||
| TokenInfo / verify_token / generate_jti / revoke_jti / revoke_all_user_tokens / jti 黑名单(5000 上限清理)/ 用户版本号表 | RSA 密钥对启动内存生成(重启即全吊销,语义不变) |
|
||||
| 8 个 RateLimiter 实例 + 邮箱级双窗口限流 | 限流键仍取 XFF(模式 A 可信;模式 B 警示见 §2.3-4) |
|
||||
| handleLogin ~ handleResetPassword 全部 handler | 错误文案/状态码契约逐字保留(黑盒断言依赖) |
|
||||
| handleWhoami / handleAdminVerify / handleRefreshToken / handleLogout | 同上 |
|
||||
| handleCaptchaImage / verify_captcha_remote / verify_email_code_remote | fail-closed 语义保留 |
|
||||
| verifyWsAdminToken + ws_proxy 注册 | 桥接目标直接为后端(不再经 gateway) |
|
||||
| handleProxy 转发骨架 / is_safe_target / is_special_header / get_cookie_value / extract_token_from_request | 按 §3.4 改造 |
|
||||
| wrapperHttp / 信号处理 / 优雅关闭(8s+3s 超时)/ ExpBase / ConfIni / LocalTime / DbC / LogC | 基础设施随迁 |
|
||||
|
||||
### 3.6 不变量自检(启动时)
|
||||
|
||||
- `[Upstream] gateway` 非空且为 `http://127.0.0.1:*`(防误配到公网)
|
||||
- 模式 B 检测:`Server.address` 非 loopback 时启动日志打印 A4 安全降级警告
|
||||
|
||||
---
|
||||
|
||||
## 4. gateway 瘦身设计(v0.9.0)
|
||||
|
||||
### 4.1 删除清单(预计 -1700 行,2470 → ~750 行)
|
||||
|
||||
- 全部认证 handler 与 Token/PBKDF2/RSA/jti/版本号/限流/CAPTCHA/邮箱验证码代码
|
||||
- `q4_token` Cookie 读写、`extract_token_from_request`、代理内验签与 Authorization 注入
|
||||
- `[Token]`/`[Admin]`/`[WsProxy]`/`[Database]` 配置读取;下游 captcha/smtp 配置(db 保留供 LogC)
|
||||
- `/login` 等 9 条认证路由 + `/captcha/image` 路由 + WS 代理注册循环
|
||||
|
||||
### 4.2 保留清单
|
||||
|
||||
- `handleProxy`(去验签化)+ `is_safe_target` + `is_special_header` + `wrapperHttp`
|
||||
- 存在性不变量:显式 `Target-Url` 且无 `X-Auth-User` → 401(§2.3-3)
|
||||
- 公开目标规则:无 `Target-Url` → `Proxy.default`,不校验身份头
|
||||
- XFF 处理:透传 CA 值(缺省 `unknown`)
|
||||
- 配置加载、信号处理、优雅关闭、LogC
|
||||
|
||||
### 4.3 精简后 `confs/configs/gateway.ini`
|
||||
|
||||
**gateway.ini 精简**:
|
||||
```ini
|
||||
[Server]
|
||||
address = 127.0.0.1
|
||||
port = 8443
|
||||
port = 8445 # 迁出 8443;仅 CA 可达
|
||||
tls = off
|
||||
|
||||
[Proxy]
|
||||
default = http://127.0.0.1:9200
|
||||
opened = http://127.0.0.1:9200;http://127.0.0.1:8006;http://127.0.0.1:5200;
|
||||
closed = http://127.0.0.1:8005;http://127.0.0.1:8007;
|
||||
opened = http://127.0.0.1:9200;http://127.0.0.1:8006;http://127.0.0.1:5200;
|
||||
|
||||
[Auth]
|
||||
host = 127.0.0.1
|
||||
port = 8444
|
||||
|
||||
[WsProxy]
|
||||
routes = /ws/aibox=ws://127.0.0.1:8765;
|
||||
|
||||
[CORS]
|
||||
allowed_origins = https://qsont.me;https://www.qsont.me
|
||||
[Downstream] # 仅供 LogC 日志落库
|
||||
db_host = 127.0.0.1
|
||||
db_port = 8005
|
||||
```
|
||||
|
||||
**认证路由处理方案**(二选一):
|
||||
### 4.4 启动自检
|
||||
|
||||
- **方案 A(推荐):gateway 代理认证路由**
|
||||
- gateway 将 `/login`、`/register` 等路径加入代理白名单,Target-Url 自动指向 auth 服务
|
||||
- 前端请求路径不变:`POST /login` → WAF → gateway → auth
|
||||
- 优势:前端零改动,gateway 仍是唯一入口
|
||||
|
||||
- **方案 B:auth 直暴露**
|
||||
- WAF 配置分流规则,认证路径直接转发到 auth:8444
|
||||
- 需改动 WAF 配置,增加路由复杂度
|
||||
|
||||
### 阶段三:下游服务适配
|
||||
|
||||
**目标**:core 和 dnfs2 的验签调用目标从 gateway 切换为 auth
|
||||
|
||||
#### 3.1 core 适配
|
||||
|
||||
**当前**(`core/src/main/HttpTask.cpp` L155-204):
|
||||
```cpp
|
||||
// HTTP POST → gateway:8443/admin/verify
|
||||
std::string url = "http://" + host + ":" + std::to_string(port) + "/admin/verify";
|
||||
pmc::net::HttpClient client;
|
||||
client.set_header("Authorization: " + raw);
|
||||
client.post_stream_sync(url, "", ...);
|
||||
```
|
||||
|
||||
**改造后**:
|
||||
```cpp
|
||||
// pymsg → auth:8444 verify 路由(消除 HTTP 回调,统一内部协议)
|
||||
pmc::net::MsgClient client(auth_host, auth_port, "core");
|
||||
json::value req = {{"_route", "verify"}, {"token", raw_token}};
|
||||
std::string resp = client.call(json::serialize(req), 5.0);
|
||||
```
|
||||
|
||||
#### 3.2 dnfs2 适配
|
||||
|
||||
**当前**(`dnfs2/src/dnfs2.cpp` L446-495):
|
||||
```cpp
|
||||
// HTTP POST → gateway:8443/whoami
|
||||
http.post_stream_sync(_cfg.gateway_url, "{}", ...);
|
||||
```
|
||||
|
||||
**改造后**:
|
||||
```cpp
|
||||
// pymsg → auth:8444 whoami 路由
|
||||
RpcClient auth_client(_cfg.auth_host, _cfg.auth_port, "dnfs2", timeout);
|
||||
json::value result = auth_client.call("whoami", {{"token", token}});
|
||||
```
|
||||
|
||||
**配置文件变更**(`confs/configs/dnfs.ini`):
|
||||
```ini
|
||||
[auth] # 新增,替代 [gateway]
|
||||
host = 127.0.0.1
|
||||
port = 8444
|
||||
timeout = 5
|
||||
```
|
||||
|
||||
### 阶段四:启动编排与部署
|
||||
|
||||
**core/init.txt 更新**:
|
||||
```
|
||||
bash /opt/micro_servers/db2/_run.sh
|
||||
bash /opt/micro_servers/confs/_run.sh
|
||||
bash /opt/micro_servers/auth/_run.sh # 新增,须在 gateway 之前启动
|
||||
bash /opt/micro_servers/ai_review/_run.sh
|
||||
bash /opt/micro_servers/dnfs2/_run.sh
|
||||
bash /opt/micro_servers/smtp/_run.sh
|
||||
bash /opt/micro_servers/static/_run.sh
|
||||
bash /opt/micro_servers/waf/_run.sh
|
||||
bash /opt/micro_servers/gateway/_run.sh
|
||||
bash /opt/micro_servers/captcha/_run.sh
|
||||
bash /opt/micro_servers/aibox/_run.sh
|
||||
bash /opt/micro_servers/redirect80/_run.sh
|
||||
```
|
||||
|
||||
**docker-compose.yml 新增 auth 服务**:
|
||||
```yaml
|
||||
auth:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: auth/Dockerfile
|
||||
container_name: micro-auth
|
||||
ports:
|
||||
- "127.0.0.1:8444:8444"
|
||||
environment:
|
||||
- LISTEN_ADDR=0.0.0.0
|
||||
- LISTEN_PORT=8444
|
||||
- DB_HOST=db
|
||||
- DB_PORT=8005
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
```
|
||||
- `Server.address` 必须为 loopback(`127.0.0.1`/`::1`):gateway 不再验签后,绑定公网等于裸奔,**非 loopback 直接拒绝启动**并打印致命错误。
|
||||
|
||||
---
|
||||
|
||||
## 四、接口契约设计
|
||||
## 5. 接口契约
|
||||
|
||||
### 4.1 auth 内部 pymsg 接口(供其他微服务调用)
|
||||
### 5.1 对外 HTTP(不变量)
|
||||
|
||||
#### verify 路由(替代 /admin/verify)
|
||||
```json
|
||||
// 请求
|
||||
{"_route": "verify", "token": "base64payload_signature", "require_admin": true}
|
||||
`https://<host>/login`、`/register`、`/whoami` 等全部 URL、请求/响应 JSON 结构、状态码(400/401/403/429/500/502)、`q4_token` Cookie 属性、`X-Captcha-Token` 头——**逐字不变**。前端、WAF、core、dnfs2 零改动的依据即此。
|
||||
|
||||
// 响应(成功)
|
||||
{"success": true, "user": "admin@example.com", "is_admin": true}
|
||||
### 5.2 内部头契约(CA ↔ gateway ↔ 下游)
|
||||
|
||||
// 响应(失败)
|
||||
{"success": false, "error": "token expired"}
|
||||
```
|
||||
| 头 | 方向 | 语义 |
|
||||
|----|------|------|
|
||||
| `X-Auth-User` | CA→gateway→下游 | 已验签身份;CA strip-then-set;gateway 对显式 Target-Url 要求其存在 |
|
||||
| `X-Auth-Admin` | CA→gateway→下游 | `true` 表示管理员;供下游免回调豁免(可选消费) |
|
||||
| `Authorization` | CA→gateway→下游 | Cookie 来源统一补齐为 Bearer;下游解析不变 |
|
||||
| `X-Forwarded-For` | WAF→CA→gateway→下游 | 逐层透传,缺省 `unknown` |
|
||||
| `Target-Url` | 客户端→CA→gateway | 路由策略头,语义不变 |
|
||||
|
||||
#### whoami 路由(替代 /whoami)
|
||||
```json
|
||||
// 请求
|
||||
{"_route": "whoami", "token": "base64payload_signature"}
|
||||
### 5.3 Token 格式(不变)
|
||||
|
||||
// 响应(已登录)
|
||||
{"success": true, "login": true, "user": "user@example.com", "is_admin": false}
|
||||
`Base64(payload)_RSA-Sign(payload)`;payload 字段(user/jti/ver/iat/created_at/keep_time/is_admin/refresh_count/max_refresh)不变。切换瞬间旧 Token 全部失效(CA 新密钥),用户重新登录——一次性事件,cutover 通告中说明。
|
||||
|
||||
// 响应(未登录/Token 无效)
|
||||
{"success": true, "login": false}
|
||||
```
|
||||
### 5.4 下游零改动验证点
|
||||
|
||||
### 4.2 Token 格式不变
|
||||
|
||||
保持现有 `Base64(payload)_RSA-Sign(payload)` 格式,payload 字段不变:
|
||||
```json
|
||||
{
|
||||
"user": "email@example.com",
|
||||
"jti": "random_hex_32",
|
||||
"ver": 1,
|
||||
"iat": "2026-08-08 12:00:00",
|
||||
"created_at": "2026-08-08 12:00:00",
|
||||
"keep_time": 3600,
|
||||
"is_admin": true,
|
||||
"refresh_count": 0,
|
||||
"max_refresh": 10
|
||||
}
|
||||
```
|
||||
| 消费方 | 现状调用 | 切换后 |
|
||||
|--------|----------|--------|
|
||||
| core `verify_admin_token` | `http://127.0.0.1:8443/admin/verify` | 同 URL 由 CA 服务,契约不变 |
|
||||
| dnfs2 Token 验签 | `http://127.0.0.1:8443/whoami` | 同上 |
|
||||
| static `/api/check_user` | 直连 db 查 Users(账号旁路) | 不受影响;遗留项另行收口 |
|
||||
|
||||
---
|
||||
|
||||
## 五、风险评估与缓解
|
||||
## 6. 上线步骤(cutover 与回滚)
|
||||
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
|------|------|---------|
|
||||
| auth 服务不可用导致全系统鉴权失败 | 高 | auth 的 pymsg 客户端启用 `retry_on_refused`;gateway 对 auth 路由的代理失败返回 503(而非静默放行) |
|
||||
| 前端请求路径变化 | 中 | 采用方案 A(gateway 代理认证路由),前端零改动 |
|
||||
| Token 验签增加一跳网络延迟 | 低 | pymsg TCP 本机通信延迟 <1ms;gateway 代理层可增加 Token 缓存(TTL 60s) |
|
||||
| RSA 密钥迁移 | 中 | auth 启动时自生成密钥对(与当前 gateway 行为一致);过渡期可共享密钥文件 |
|
||||
| 过渡期双系统并存 | 中 | 分阶段切换:先 auth 上线 → gateway 认证路由代理到 auth → 删除 gateway 内置 auth 代码 |
|
||||
| Token 黑名单持久化需求 | 低 | 初期保持纯内存方案(与现状一致);后续可选 Redis 持久化 |
|
||||
| 步骤 | 内容 |
|
||||
|------|------|
|
||||
| 1 | CA 开发完成,`LISTEN_PORT=18443` 并行实例联调(不影响现网 8443) |
|
||||
| 2 | gateway 瘦身版(v0.9.0)构建,`gateway.ini` 改 8445 |
|
||||
| 3 | cutover(秒级窗口):停 gateway:8443 → 启 CA:8443 → 启 gateway:8445 → 黑盒全量验证 |
|
||||
| 4 | `core/init.txt` 增加 `ca/_run.sh`(gateway 之前);README/INDEX 文档同步 |
|
||||
| 5 | 观察 24h:CA/gateway 日志、db Logs 表 4xx/5xx 基线对比 |
|
||||
|
||||
**回滚**:停 CA → 旧 gateway 二进制 + 旧配置回 8443 → 重启。Token 全部重签(用户重新登录),无数据迁移。
|
||||
|
||||
---
|
||||
|
||||
## 六、时间估算
|
||||
## 7. 测试计划
|
||||
|
||||
| 阶段 | 工作量 | 说明 |
|
||||
|------|--------|------|
|
||||
| 阶段一:auth 服务搭建 | 2-3 天 | 代码迁移 + 新配置 + 测试 |
|
||||
| 阶段二:gateway 瘦身 | 1 天 | 删除代码 + 代理路由配置 + 回归测试 |
|
||||
| 阶段三:下游适配 | 1 天 | core/dnfs2 验签调用改造 + 集成测试 |
|
||||
| 阶段四:部署编排 | 0.5 天 | init.txt + docker-compose + 端到端测试 |
|
||||
| **合计** | **4.5-5.5 天** | 含测试缓冲 |
|
||||
### 7.1 验收门(零改动复用)
|
||||
|
||||
- `tests/blackbox/test_gateway.py` 34 断言**原样**对 8443 执行:认证路径打到 CA、代理路径经 CA→gateway,外部契约不变则全绿(当前基线 32 passed / 2 env-skipped)
|
||||
- 修复期两个红测试(A3 缺字段 400)已在 v0.8.0 转绿,迁移后必须保持
|
||||
|
||||
### 7.2 新增探针(test_ca.py,黑盒补充)
|
||||
|
||||
| 探针 | 预期 |
|
||||
|------|------|
|
||||
| 客户端伪造 `X-Auth-User: admin@x` + 显式 Target-Url(无 Token) | CA 剥离后 401,不到下游 |
|
||||
| 伪造 `X-Auth-User` + 有效非管理员 Token 访问 WS 路由 | 403(以验签结果为准,不以头为准) |
|
||||
| 直连 gateway:8445,显式 Target-Url、无 `X-Auth-User` | 401(存在性不变量) |
|
||||
| 经 CA 全链路(8443)带 Token 访问 dnfs2 | 下游收到 `X-Auth-User` 与 `Authorization` |
|
||||
| CA 存活、gateway:8445 停止 | 业务路径 502(不静默放行),认证路径正常 |
|
||||
| gateway 存活、CA 停止 | WAF 502(入口不可达,符合预期 fail-closed) |
|
||||
| 公开目标(无 Target-Url)带伪造身份头 | 下游收不到 `X-Auth-User`/`Authorization` |
|
||||
|
||||
### 7.3 集成回归
|
||||
|
||||
- core HTTP 管理接口(`/admin/verify` 链路)实测;dnfs2 笔记 CRUD(`/whoami` 链路 + AI 审核)实测
|
||||
- WS 代理端到端:管理员 Token 升级 `/ws/aibox` 双向帧;非管理员 403
|
||||
- 限流全谱在 CA 复测(随机 XFF 技巧沿用);cutover/回滚演练各一次
|
||||
|
||||
### 7.4 测试资产演进(后续)
|
||||
|
||||
- `test_gateway.py` 按进程边界拆分为 `test_ca.py`(A/B/C/E 段)+ 精简 `test_gateway.py`(D 段代理矩阵 + 8445 直连探针),README/INDEX 同步
|
||||
- 认证 happy path 仍依赖 captcha2/smtp 测试钩子(既有遗留,CA 落地后可仅在 CA 层加 `[Security] captcha_required` 开关,单点解锁)
|
||||
|
||||
---
|
||||
|
||||
## 七、验收标准
|
||||
## 8. 风险与缓解
|
||||
|
||||
1. auth 服务独立启动,通过 pymsg 和 HTTP 双栈提供全部认证接口
|
||||
2. gateway 代码量从 ~2500 行降至 ~800 行,仅含代理逻辑
|
||||
3. 前端所有认证操作(登录/注册/注销/刷新/重置密码)路径不变、行为不变
|
||||
4. core/dnfs2 通过 pymsg 调用 auth 验签,延迟 ≤ 5ms(本机)
|
||||
5. auth 服务重启不影响 gateway 代理转发(已认证请求在 Token 过期前仍有效)
|
||||
6. 所有现有安全特性(PBKDF2、RSA 签名、常数时间比较、jti 黑名单、版本号吊销)完整保留
|
||||
| 风险 | 等级 | 缓解 |
|
||||
|------|------|------|
|
||||
| CA 不可用 → 全站入口中断 | 高 | CA 职责内聚后可用性等同于现 gateway;core 守护 + 黑盒监控;回滚方案秒级 |
|
||||
| 迁移遗漏导致认证行为漂移 | 中 | 代码逐字搬迁 + 34 断言验收门 + §7.2 探针矩阵 |
|
||||
| 身份头伪造/泄露 | 中 | strip-then-set + gateway 存在性不变量 + 公开目标不注入 + 直连探针回归 |
|
||||
| gateway:8445 被公网暴露 | 高 | 启动强制 loopback 自检(§4.4);部署审计 checklist 增加端口扫描项 |
|
||||
| cutover 窗口 Token 全失效 | 低 | 预期内(RSA 换钥),通告用户重新登录 |
|
||||
| 双重转发延迟 | 低 | 本机 HTTP 转发 <1ms;CA→gateway 短连接可后续优化为 keep-alive |
|
||||
| 模式 B 限流绕过(A4 迁移) | 中 | CA README 降级警示;根治按 libhttp_remote_addr_dev_doc(配套改落 CA) |
|
||||
|
||||
---
|
||||
|
||||
## 9. 工作量估算
|
||||
|
||||
| 部分 | 规模 | 估时 |
|
||||
|------|------|------|
|
||||
| CA 脚手架(目录/Makefile/_set.sh/_run.sh/LogC 等) | 模板复制 + 适配 | 0.5 天 |
|
||||
| 认证资产搬迁 + 前置转发层改造 | ~1700 行搬迁 + ~150 行改造 | 1.5 天 |
|
||||
| gateway 瘦身 + 启动自检 | -1700 行 + ~20 行 | 0.5 天 |
|
||||
| 配置/init.txt/文档(README×2、INDEX、confs) | — | 0.5 天 |
|
||||
| 黑盒验收 + §7.2 探针 + 集成回归 + cutover 演练 | — | 1 天 |
|
||||
| **合计** | | **4 天**(含缓冲) |
|
||||
|
||||
---
|
||||
|
||||
## 10. 与既有文档的关系
|
||||
|
||||
- 本文档**替代** 2026-08-08 初版计划书中的双栈拓扑与阶段划分(初版的职责盘点/迁移清单仍然有效,已并入 §1.2/§3.5)
|
||||
- [libhttp_remote_addr_dev_doc.md](libhttp_remote_addr_dev_doc.md)(A4 根治):实施时 `trust_xff`/`rate_limit_key()` 配套由 gateway 改为落 CA;`is_special_header` 两侧同步加 `x-remote-addr`
|
||||
- [gateway_SECURITY_AUDIT_20260809.md](../security/gateway_SECURITY_AUDIT_20260809.md):A2(白名单 403)断言、P2-3(CORS 白名单)随转发层/配置迁入 CA;P1-7(CSRF Token)在 CA 层评估
|
||||
- gateway README / 项目 README 的架构图、端口表、请求链路在上线步骤第 4 步同步更新
|
||||
Reference in new issue
Block a user