From 1e9e12489d625bac7cddf76854dd5e4cf94aab0c Mon Sep 17 00:00:00 2001 From: zhangtao <9480807882@qq.com> Date: Sun, 4 Oct 2026 06:39:30 +0800 Subject: [PATCH] =?UTF-8?q?security(deploy):=20=E7=94=9F=E4=BA=A7=E5=87=AD?= =?UTF-8?q?=E6=8D=AE=E8=BD=AE=E6=8D=A2=20+=20=E9=95=9C=E5=83=8F=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E6=90=BA=E5=B8=A6=E5=8F=A3=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 轮换(MySQL 应用用户 / MySQL root / Redis,实测旧值被拒) - 旧 MySQL app 口令 → ERROR 1045 (28000) Access denied;旧 root 同被拒 - 旧 Redis 口令 → AUTH failed: WRONGPASS - 新口令:容器内直连成功;应用健康 db_status=1 / redis_status=1;四端 200;容器全 healthy - .env 保持 600,轮换前备份 .env.bak-rotate-* 镜像卫生(审计 B3 根治) - deploy-artifacts.sh 构建时剔除 DATABASE_PASSWORD / REDIS_PASSWORD / SECRET_KEY / DATA_ENCRYPTION_KEY / DATA_ENCRYPTION_OLD_KEYS / MYSQL_* 口令键, 仅保留主机、端口等非敏感项;口令全部由 compose 运行时注入 - 重建后镜像内 /home/env/.env.prod 口令类键命中 0 条;容器 env 仍持有 4 个密钥且应用正常 - 本地 backend/env/.env.prod 中泄露值清空并 chmod 600 文档 - DEPLOYMENT.md 新增「凭据轮换」标准步骤(含 mysql/redis 健康检查需一并重建、 docker exec -i 会吞脚本 stdin 的排查提示) - AUDIT-REPORT.md 记录公开仓库历史暴露面与待用户侧吊销项(OpenAI Key) 说明:仓库为 public,历史中真实出现过 OpenAI Key 与旧口令;旧口令本轮已失效, OpenAI Key 需到控制台吊销(第三方无法代办)。是否用 git-filter-repo 清洗历史见报告 §10。 --- AUDIT-REPORT.md | 7 ++++--- DEPLOYMENT.md | 35 ++++++++++++++++++++++++++++++++++- deploy-artifacts.sh | 13 +++++++++++-- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/AUDIT-REPORT.md b/AUDIT-REPORT.md index 5cc9137e..38fa2dc0 100644 --- a/AUDIT-REPORT.md +++ b/AUDIT-REPORT.md @@ -190,7 +190,8 @@ | Web i18n 与通知枚举 | 85/101 页面未接入 i18n;通知状态枚举与后端语义冲突 | | 测试覆盖与夹具 | 夹具掩盖缺陷、迁移链路 0 覆盖(CI 已建立,但用例仍需补) | | 移动端其它缺陷 | `alova.ts:115-119` 请求头写成 `ContentType`(缺连字符),修正需回归上传/表单/JSON 三类请求 | -| 密钥残留 | 旧数据库口令与 OPENAI_API_KEY 仍存在于 git 历史,建议按已泄露处理并重新签发 | +| 凭据轮换与镜像卫生 | ① 生产口令整体轮换:MySQL 应用用户 / MySQL root / Redis(旧值实测被拒:`ERROR 1045`、`WRONGPASS`);② 镜像不再携带任何口令:`deploy-artifacts.sh` 构建时剔除 `DATABASE_PASSWORD / REDIS_PASSWORD / SECRET_KEY / DATA_ENCRYPTION_KEY` 等键(审计 B3 根治),重建后镜像内口令行命中 0 条,应用改由 compose 注入口令且 `db_status/redis_status=1`;③ 本地 `backend/env/.env.prod` 中泄露值清空并改 600 权限 | 旧口令拒绝测试、新口令容器内连接测试、镜像内容扫描、四端 200、容器全 healthy | +| 公开仓库历史暴露(需用户侧吊销) | 仓库为 **public**,历史中真实出现过:**OpenAI API Key(35 字符 `sk-…`)**、旧 MySQL/Redis/root 口令、旧 `SECRET_KEY`、旧自签名 TLS 私钥;其余 `PAYMENT_* / EMAIL_PASSWORD / DINGDING_SECRET / MONGO_DB_PASSWORD / POSTGRESQL_PASSWORD` 等仅出现在 `.example` 占位文件 | 已消除:DB/Redis/root 口令与 `SECRET_KEY`/`DATA_ENCRYPTION_KEY` 本轮轮换(历史值即失效);线上证书与历史私钥**不同**(指纹比对)。**待用户处理:到 OpenAI 控制台吊销该 Key**(第三方无法代办);是否用 `git-filter-repo` 清洗历史见 §9.2 | | 对外口径(异常统一后的取舍) | 存储「测试连接」密钥环损坏、上传/下载失败、OAuth 渠道未配置、Redis 同步失败等已由具体原因变为通用 5xx 文案(细节只在日志)。若希望管理员在界面看到可操作原因,正确做法是为这些场景定义**业务码**,而不是回到「包装意外异常」 | | 仍可能外泄的出口 | `ValueError` 全局处理器返回 `msg=str(exc)`;建议改为通用文案 + 日志,或让我们的校验统一抛 `CustomException` | | 静默吞异常(既有) | 48 处「记录日志但不 raise」的 best-effort 处理器(redis_crud、ap_scheduler、middlewares、discover 等),属审计「静默吞异常」主线,建议单独排一轮 | @@ -231,7 +232,7 @@ **收尾后仍需人工处理(不阻塞上线)** -1. 轮换 `OPENAI_API_KEY` 与旧数据库口令(已进历史/镜像层,按已泄露处理)。 -2. 审计报告内含旧密钥的少量特征(前 4 字符与长度)用于佐证;若仓库将转为公开,建议先按上述轮换再考虑是否脱敏。 +1. **吊销旧 OpenAI API Key**:仓库是 public,`sk-…`(35 字符)确实出现在 `backend/env/.env.prod` 的历史提交中。线上环境从未使用它(容器 env 与该键均为空、新镜像已不含任何密钥),所以风险只在于该 Key 在 OpenAI 侧可能仍有效。请到 吊销;如需 AI 功能再签发新 Key 并只写入服务器 `.env`。 +2. **是否清洗 git 历史**:`git-filter-repo` 可把历史中的 `.env*` 与旧证书整体抹除,但会重写所有 SHA(需 force-push `dev` 与 `master`,影响既有克隆与 PR 引用,且 gitee 镜像需同步)。由于相关口令已轮换、Key 将吊销,**建议以「吊销 + 轮换」为准,清洗作为可选项**;若仓库要转为对外开源,则建议执行清洗。 3. §9.2 中的产品/架构决策项(会话失效、数据权限注入、多租户规格、i18n、移动端 `ContentType`)。 diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index f55961b4..521bea77 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -123,7 +123,40 @@ ss -tlnp | grep docker-proxy # 确认对外只有 80/443 `pydantic_settings.exceptions.SettingsError: error parsing value for field "XXX" from source "EnvSettingsSource"`, 就是该字段的取值格式与类型不匹配。 -## 7. 已知遗留问题 +## 7. 凭据轮换(生产) + +轮换对象:MySQL 应用用户 / MySQL root / Redis。三者都通过 `docker/.env` 注入,且 +**镜像内不得包含任何口令**(`deploy-artifacts.sh` 在构建上下文里剔除 +`DATABASE_PASSWORD / REDIS_PASSWORD / SECRET_KEY / DATA_ENCRYPTION_KEY` 等键, +仅保留主机名、端口等非敏感项——审计 B3 的根治)。 + +标准步骤(约 1 分钟中断窗口,顺序不可颠倒): + +```bash +cd /home/FastapiAdmin/docker +cp -a .env ".env.bak-rotate-$(date +%Y%m%d-%H%M%S)" # 先备份,便于回滚 + +# 1) 改数据库口令(用 stdin 传 SQL,避免口令出现在命令行历史/ps) +NEW=$(openssl rand -base64 36 | tr -d '/+=' | cut -c1-30) +docker exec -i -e MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mysql mysql -uroot <