feat: strengthen P0 security and operations

This commit is contained in:
2026-08-01 11:20:02 +08:00
parent f776056834
commit 84c2b0b21d
77 changed files with 24185 additions and 355 deletions

View File

@@ -9,14 +9,14 @@
| [本地开发与运行](quickstart.md) | PostgreSQL 初始化、启动 API、验证命令、常见问题 | 新开发者 |
| [系统架构与业务边界](architecture/overview.md) | 项目依赖、运行时组件、当前业务模块和后台任务链路 | 开发与评审人员 |
| [认证、授权与租户隔离](architecture/security-and-tenancy.md) | 登录、Session、JWT、Cookie/CSRF、Realm、RBAC、Capability、DataScope、租户隔离 | API 与安全开发者 |
| [配置与后台任务](operations.md) | 环境配置、Production 启动门禁、Redis、Hosted Service、健康检查 | 开发与运维人员 |
| [配置与后台任务](operations.md) | 环境配置、Production 启动门禁、Redis、Worker、ClamAV 和健康检查 | 开发与运维人员 |
## 权威来源
- API 契约:`Tiku.Api/Controllers`、请求/响应 DTO 和运行时 OpenAPI。
- 数据模型:`Tiku.Domain``Tiku.Infrastructure/Persistence/Configurations` 和 EF Core Migration。
- 认证授权:`Tiku.Api/Configuration``Tiku.Api/Middleware``Tiku.Application/Security``Tiku.Infrastructure/Security`
- 后台任务:`Tiku.Api/BackgroundProcessing``Tiku.Application/Jobs``Tiku.Infrastructure/Jobs`
- 后台任务:`Tiku.Worker``Tiku.Application/Jobs``Tiku.Infrastructure/Jobs`
- 外部服务Application 接口与 Infrastructure 实现;运行时租户配置存储在 `TenantExternalProvider``TenantSecret`
## 维护规则

View File

@@ -5,13 +5,13 @@
## 分层与依赖
```text
+-------------------------+ +-----------------+
| Tiku.Api | | Tiku.DbMigrator |
| HTTP + Hosted Services | | Migrate + Seed |
+------------+------------+ +--------+--------+
| |
+--------------+--------------+
v
+------------------+ +------------------+ +-----------------+
| Tiku.Api | | Tiku.Worker | | Tiku.DbMigrator |
| HTTP API | | Background loops | | Migrate + Seed |
+--------+---------+ +--------+---------+ +--------+--------+
| | |
+---------------------+---------------------+
v
+---------------------+
| Tiku.Infrastructure |
+----------+----------+
@@ -28,7 +28,7 @@
- `Tiku.Domain` 保存领域实体、枚举和基础类型。除 Identity stores 抽象外,不依赖持久化或 Provider SDK。
- `Tiku.Application` 定义用例契约、Provider 接口、安全上下文和业务目录,依赖 Domain。
- `Tiku.Infrastructure` 实现 EF Core、PostgreSQL、Identity、外部 Provider 和后台任务,依赖 Application 与 Domain。
- `Tiku.Api` 是唯一运行时入口,`Tiku.DbMigrator` 是部署时迁移和 seed 入口。
- `Tiku.Api` `Tiku.Worker` 是独立运行时入口,`Tiku.DbMigrator` 是部署时迁移和 seed 入口。
## 运行时组件
@@ -63,20 +63,20 @@ OpenAPI 和 Scalar 只在 Development 映射。平台管理端位于独立的 `T
API 不自动迁移数据库。
### API 后台处理
### Worker 后台处理
`Tiku.Api``BackgroundProcessing:Enabled=true` 注册四个 Hosted Service
`Tiku.Worker` 独立注册四个 Hosted ServiceAPI 进程不注册后台循环
| Hosted Service | 周期 | 当前职责 |
| Worker | 周期 | 当前职责 |
| --- | --- | --- |
| `TenantDomainBackgroundService` | `TenantDomains:PollSeconds`,限制为 103600 秒 | 校验自定义域名 CNAME/TXT调用网关 TLS 接口并失效租户缓存 |
| `SaasSubscriptionBackgroundService` | 60 秒 | 处理到期、宽限期等 SaaS 订阅生命周期 |
| `FeatureUsageBackgroundService` | `FeatureUsageReconciliation:IntervalMinutes`,限制为 11440 分钟 | 按真实业务数据校准租户 Feature 用量 |
| `BackgroundJobsBackgroundService` | 默认 2 秒、4 个分区 | 使用租约处理 PostgreSQL 中的即时、延时和待重试任务 |
| `TenantDomainWorker` | `TenantDomains:PollSeconds`,限制为 103600 秒 | 校验自定义域名 CNAME/TXT调用网关 TLS 接口并失效租户缓存 |
| `SaasSubscriptionWorker` | 60 秒 | 处理到期、宽限期等 SaaS 订阅生命周期 |
| `FeatureUsageWorker` | `FeatureUsageReconciliation:IntervalMinutes`,限制为 11440 分钟 | 按真实业务数据校准租户 Feature 用量 |
| `BackgroundJobsWorker` | 默认 2 秒、4 个分区 | 使用租约处理 PostgreSQL 中的即时、延时和待重试任务 |
后台任务当前支持 `content_import``content_export``statistics_aggregation``commerce_reconciliation``tenant_domain_recheck``asset_security_scan` 会明确失败,直到配置实际扫描 Provider不能把它描述为已接通扫描服务
后台任务支持 `content_import``content_export``asset_security_scan``tenant_export``statistics_aggregation``commerce_reconciliation``tenant_domain_recheck`安全扫描通过 ClamAV `INSTREAM` 协议流式处理对象;未通过扫描或扫描不可用时资源访问 fail-closed
即时任务与 `RunAfter` 延时任务统一写入 `background_jobs`Hosted Service 使`FOR UPDATE SKIP LOCKED` 认领任务,五分钟租约支持 API 重启后的恢复;当前生产部署按单 API 实例设计
即时任务与 `RunAfter` 延时任务统一写入 `background_jobs`后台任务`FOR UPDATE SKIP LOCKED` 和五分钟租约协调;四个周期处理器使用 PostgreSQL advisory lock允许部署多个 Worker 实例而不重复执行同一周期循环
## 数据与持久化

View File

@@ -110,11 +110,11 @@ Development 默认平台 Host 是 `localhost` 和 `127.0.0.1`。Production 启
## System Scope 与后台处理
跨租户 Hosted Service、迁移、seed 和平台级后台操作必须通过 `ITenantContextInitializer.InitializeSystem` 或受审计的 `ITenantExecutionScope` 进入 System Scope并提供明确原因。业务代码不得直接关闭 Query Filter。
跨租户 Worker、迁移、seed 和平台级后台操作必须通过 `ITenantContextInitializer.InitializeSystem` 或受审计的 `ITenantExecutionScope` 进入 System Scope并提供明确原因。业务代码不得直接关闭 Query Filter。
- Session、成员、租户和套餐状态始终从 PostgreSQL 重新校验。
- 租户、套餐和 Feature 变更在数据库提交后直接失效当前 API 进程与 Redis 中的相关缓存。
- 后台任务在业务事务提交后持久化到 PostgreSQLHosted Service 使用租约执行;延时和重试由 `RunAfter` 控制。
- 后台任务在业务事务提交后持久化到 PostgreSQL独立 Worker 使用租约执行;延时和重试由 `RunAfter` 控制。
## 安全配置门禁

View File

@@ -1,12 +1,13 @@
# 配置与后台任务
本文列出 API 和 DbMigrator 当前实际读取的配置。敏感值应通过环境变量、Secret Manager 或部署平台密钥注入,不能提交到仓库。
本文列出 API、Worker 和 DbMigrator 当前实际读取的配置。敏感值应通过环境变量、Secret Manager 或部署平台密钥注入,不能提交到仓库。
## 进程与依赖
| 进程 | PostgreSQL | Redis | 说明 |
| --- | --- | --- | --- |
| `Tiku.Api` | 必需 | Development 可选Production 必需 | 提供 HTTP API、认证、缓存和 Hosted Service 后台处理 |
| `Tiku.Api` | 必需 | Development 可选Production 必需 | 提供 HTTP API、认证、授权和缓存 |
| `Tiku.Worker` | 必需 | 不需要 | 承载周期任务、任务队列、租户导出和安全扫描 |
| `Tiku.DbMigrator` | 必需 | 不需要 | 执行 Migration、内置目录 seed 和管理员引导 |
Development 未配置 Redis 时,安全服务使用进程内/数据库防线。Production 不允许 Redis 降级;后台任务在所有环境统一使用 PostgreSQL。
@@ -54,11 +55,11 @@ dotnet run --project Tiku.DbMigrator -- --bootstrap-platform-admin
Redis key 使用环境前缀;配置解析会强制 `AbortOnConnectFail=false`。Redis 不是用户、Session、权限、套餐或用量的权威数据源。
## 后台处理配置
## Worker 与后台处理配置
```json
{
"BackgroundProcessing": {
"Worker": {
"Enabled": true,
"JobPollSeconds": 2,
"JobParallelism": 4,
@@ -89,7 +90,18 @@ Redis key 使用环境前缀;配置解析会强制 `AbortOnConnectFail=false`
域名只有在 `AllowedCnameTargets`、DNS JSON endpoint、Gateway URL 和 API key 配置完成后,才可能从 Pending/Failed 进入 Active。仅 DNS 验证成功不代表 TLS 已就绪。
后台任务状态和 `RunAfter` 存在 PostgreSQL。API Hosted Service 使用 `FOR UPDATE SKIP LOCKED`、五分钟租约和有限重试处理即时、延时及失败待重试任务`BackgroundProcessing:Enabled=false` 会关闭全部四个后台循环,通常只用于测试或维护。
后台任务状态和 `RunAfter` 存在 PostgreSQL。Worker 使用 `FOR UPDATE SKIP LOCKED`、五分钟租约和有限重试处理即时、延时及失败待重试任务;周期循环使用 PostgreSQL advisory lock 防止多实例重复执行。`Worker:Enabled=false` 会关闭全部四个后台循环,通常只用于测试或维护。
API 和 Worker 必须使用同一 PostgreSQL 数据库与一致的对象存储配置。迁移必须在两者启动前由 `Tiku.DbMigrator` 单独执行。
容器镜像从仓库根目录构建:
```bash
docker build -f Tiku.Api/Dockerfile -t tiku-api .
docker build -f Tiku.Worker/Dockerfile -t tiku-worker .
```
API 和 Worker 应独立设置副本数与资源限制。先完成 Migration再启动 Worker最后开放 API 流量;不要在容器入口自动执行 Migration。
## 安全与网络配置
@@ -120,21 +132,53 @@ Production 启动至少需要核对:
## 对象存储与外部 Provider
对象存储读取 `Storage` / `Storage:AliyunOss`支持 `STORAGE_*``ALIYUN_OSS_*` 环境变量。当前默认实现是阿里云 OSS并强制租户 key 前缀、上传大小和 MIME allowlist。
对象存储读取 `Storage` / `Storage:AliyunOss`API 与 Worker 都支持 `STORAGE_*``ALIYUN_OSS_*` 环境变量。当前默认实现是阿里云 OSS并强制租户 key 前缀、上传大小和 MIME allowlist。租户导出使用 `application/gzip`,该类型不能从 allowlist 删除。
## ClamAV 资源安全扫描
API 和 Worker 都读取 `Security:ClamAV`
```json
{
"Security": {
"ClamAV": {
"Host": "clamav",
"Port": 3310,
"TimeoutSeconds": 30,
"ChunkBytes": 65536,
"StreamMaxLength": 524288000
}
}
}
```
Worker 使用 ClamAV `INSTREAM` 协议,不在本地落盘待扫描对象。启动校验要求 `StreamMaxLength >= Storage:MaxUploadBytes`;同时必须把 clamd 自身的 `StreamMaxLength` 配到相同或更高值。扫描结果为 FOUND 时资源标记为 Failed 并记录病毒签名ClamAV 超时或不可用时任务保留 Pending 并退避重试。只有 `Passed` 或系统明确标记为可信的 `NotRequired` 资源可以签发访问地址。
身份、短信、支付、通知和 AI 的租户配置由业务后台写入 `TenantExternalProvider`;敏感值写入加密的 `TenantSecret`。全局默认配置不能绕过租户 Provider 状态和 Secret 边界。
## 健康检查与观测
- `GET /api/health`:轻量 liveness只说明 API 进程可响应。
- `GET /api/health/ready`:检查 PostgreSQL 和已配置 Redis依赖未就绪时返回 503。
- `GET /api/health/ready`:检查 PostgreSQL 和已配置 Redis依赖未就绪时返回 503,匿名响应只包含总体状态和检查时间
- `GET /api/platform-admin/operations/health`:需要 `platform:operations:view`,返回 PostgreSQL、Redis、Worker heartbeat、ClamAV 和对象存储配置状态。
- `GET /api/platform-admin/operations/workers`:查询 Worker 心跳、周期循环和 stale 状态。
- `GET /api/platform-admin/operations/job-metrics`:查询队列状态、最老 Pending 任务与过期租约。
- 设置 `OpenTelemetry:OtlpEndpoint` 后导出 ASP.NET Core、HTTP client 和数据库观测数据。
- Serilog 输出结构化请求日志;数据库性能拦截器记录慢查询指标。
Readiness 为绿色不等于认证授权、跨租户隔离或后台任务恢复演练已通过,发布仍需执行对应集成测试。
## 租户归档与导出发布门禁
- 租户归档是逻辑归档,禁止硬删除。
- 归档前必须存在 24 小时内成功完成的租户导出,且不能有 Processing 后台任务。
- 导出包是 `tar.gz`,包含 manifest、租户、成员、域名和资源元数据明确排除密码哈希、令牌、密钥明文、Data Protection keys 和全局平台数据。
- 归档会撤销该租户授权域 Session、禁用域名并失效运行时缓存恢复后租户为 `Suspended`,域名为 `Pending`,必须重新审核后再激活。
- Owner 转移目标必须是已有 Active 成员,并在同一事务内同步成员角色和后台角色绑定。
- 发布前至少演练一次导出可下载、归档阻断条件、归档、恢复和 Owner 转移。
## 从 RabbitMQ 版本切换
移除消息表的 Migration 与旧 API/Worker 不兼容。发布时使用维护窗口:停止旧 API 和 Worker确认 RabbitMQ Consumer 已退出并备份 PostgreSQL运行 `Tiku.DbMigrator`,再部署新 API。未消费的后台任务消息可丢弃因为对应任务记录已经写入 `background_jobs`;安全消息不保存授权真相。
移除消息表的 Migration 与旧 API/Worker 不兼容。发布时使用维护窗口:停止旧 API 和 Worker确认 RabbitMQ Consumer 已退出并备份 PostgreSQL运行 `Tiku.DbMigrator`,再部署新 API 与新 Worker。未消费的后台任务消息可丢弃,因为对应任务记录已经写入 `background_jobs`;安全消息不保存授权真相。
切换时不强制重置 `processing` 任务。旧租约最多五分钟后由新 API 接管。回滚需要先停止新 API执行 Migration Down 重建空 inbox/outbox 表,再恢复 RabbitMQ 配置和旧 API/Worker历史消息不会恢复。
切换时不强制重置 `processing` 任务。旧租约最多五分钟后由新 Worker 接管。回滚需要先停止新 API 与 Worker,执行 Migration Down 重建空 inbox/outbox 表,再恢复 RabbitMQ 配置和旧 API/Worker历史消息不会恢复。

View File

@@ -13,6 +13,7 @@
可选:
- Redis 7
- ClamAV验证资源安全扫描时需要
```bash
dotnet --version
@@ -98,15 +99,17 @@ export ConnectionStrings__Redis='localhost:6379,abortConnect=false'
Production 必须配置 RedisPostgreSQL 仍是用户、Session、权限、套餐和用量的权威数据源。
## 7. 后台处理
## 7. 启动 Worker 与 ClamAV
API 默认在同一进程启动域名、订阅、用量和后台任务 Hosted Service。需要临时关闭时配置
API 不处理后台循环。另开终端启动 Worker
```bash
export BackgroundProcessing__Enabled=false
dotnet run --project Tiku.Worker
```
后台任务状态、租约、重试和 `RunAfter` 存在 PostgreSQL。域名 DNS/TLS 流程只有在 `TenantDomains` 的 CNAME target 和 Gateway 配置完整后才能激活自定义域名。
`Worker__Enabled=false` 仅用于测试或维护。后台任务状态、租约、重试和 `RunAfter` 存在 PostgreSQL;多个 Worker 通过 advisory lock 和任务租约协调。域名 DNS/TLS 流程只有在 `TenantDomains` 的 CNAME target 和 Gateway 配置完整后才能激活自定义域名。
上传确认会创建 `asset_security_scan` 任务。Worker 通过 TCP 3310 连接 ClamAV且 ClamAV `StreamMaxLength` 必须不小于 `Storage:MaxUploadBytes`(当前默认均为 500 MiB。本地可以使用容器启动 ClamAV并确保该限制已配置ClamAV 不可用时任务会重试,资源保持不可访问。
## 8. 开发验证
@@ -146,7 +149,7 @@ psql -h 127.0.0.1 -U <数据库用户> -d postgres -c 'select current_user;'
### Readiness 返回 503
检查响应中的 `database` `redis.ready`。配置了 Redis 连接串但服务未启动时readiness 会返回 503。
匿名 readiness 只返回总体 `status` `checkedAt`。配置了 Redis 连接串但服务未启动时会返回 503;依赖细节需要使用具有 `platform:operations:view` 权限的平台账号访问 `/api/platform-admin/operations/health`
### API 出现 HTTPS 重定向警告