Files
ctms/docs/guides/branch-environment-installation.md
T
Cheng Zhou 74feca4467 feat: harden auth and study workflows
- replace plaintext login and unlock requests with RSA-OAEP/AES-GCM encrypted payloads

- add login challenge replay protection, production RSA key validation, and auth tests

- wire compose to environment-driven dev/prod settings without committing local secrets

- update setup-config smoke scripts and Postman docs for encrypted login

- add visit schedule migrations/tests and update study/subject setup workflows
2026-05-08 22:16:43 +08:00

8.5 KiB

CTMS 分支环境安装配置指南

本文定义 devmainrelease 三类分支对应的推荐部署环境、配置文件、初始化步骤与验证命令。分支本身不会自动切换运行模式,实际模式由后端环境变量 ENV 决定。

分支与环境映射

分支 分支定位 推荐环境 后端 ENV 用途
dev 日常开发与集成 开发环境 development 功能开发、联调、自测、内部集成
main 下一版本候选 预发布 / 验收环境 production 回归测试、验收、发布候选验证
release 当前稳定生产线 生产环境 production 正式生产部署、生产 hotfix

推广路径保持为:

feature/* -> dev -> main -> release

不要把 release 当作日常开发分支使用。main 应只接收已经准备进入候选版本范围的变更。

公共前置条件

所有环境都需要:

  • Docker 与 Docker Compose
  • 可写的 pg_data/ 数据目录
  • 端口 888880005432 未被占用
  • 后端镜像可安装 backend/requirements.txt 中的依赖
  • 前端镜像可执行 npm cinpm run build

当前 compose 服务拓扑:

nginx -> backend -> db

对外入口:

  • 前端:http://localhost:8888
  • 后端 API:同域 /api/v1/*
  • 健康检查:http://localhost:8888/health

dev 分支:开发环境

dev 默认用于本地开发和内部集成,推荐使用 ENV=development

1. 切换分支

git checkout dev
git pull --rebase origin dev

2. 配置 .env

根目录 .env 不提交到仓库。开发环境推荐:

COMPOSE_PROJECT_NAME=ctms_dev
ENV=development
JWT_SECRET_KEY=dev-secret
LOGIN_RSA_KEY_ID=default
LOGIN_RSA_PRIVATE_KEY=

说明:

  • ENV=development 会启用开发行为。
  • 未配置 LOGIN_RSA_PRIVATE_KEY 时,后端启动后会生成临时 RSA 私钥。
  • 后端重启后临时公钥会变化,已有登录页应刷新后重新登录。
  • JWT_SECRET_KEY=dev-secret 只允许本地开发使用。

3. 启动

docker compose up -d --build backend nginx

如果需要首次启动完整栈:

docker compose up -d --build

4. 开发模式行为

development 模式下:

  • FastAPI debug=True
  • 应用启动时会执行 Base.metadata.create_all
  • 应用启动时会确保默认管理员存在
  • 未配置 RSA 私钥时允许临时生成

这些行为只适合开发,不适合生产。

5. 验证

docker compose ps
docker compose exec backend python -c "from app.core.config import settings; print(settings.ENV)"
curl -i http://127.0.0.1:8888/health
curl -i http://127.0.0.1:8888/api/v1/auth/login-key

预期:

ENV=development
GET /health -> 200
GET /api/v1/auth/login-key -> 200, key_id=default

main 分支:预发布 / 验收环境

main 是下一正式版本候选分支,应按生产模式运行,但不直接承载正式生产流量。

1. 切换分支

git checkout main
git pull --rebase origin main

2. 配置 .env

预发布环境应使用 ENV=production,并配置独立于生产的密钥:

COMPOSE_PROJECT_NAME=ctms_staging
ENV=production
JWT_SECRET_KEY=<staging-strong-random-secret>
LOGIN_RSA_KEY_ID=staging-YYYYMMDD
LOGIN_RSA_PRIVATE_KEY=<staging-rsa-private-key-pem-with-\n-escaped-newlines>

生成密钥示例:

openssl rand -hex 32
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048

要求:

  • JWT_SECRET_KEY 不得使用 dev-secret
  • LOGIN_RSA_PRIVATE_KEY 必须固定保存,不能每次部署重新生成。
  • staging 私钥不得复用 production 私钥。

3. 初始化数据库

生产模式不会自动建表或自动补管理员。首次部署或迁移前执行:

docker compose run --rm backend-init

该步骤应运行 Alembic migration,并确保固定管理员账号存在。

4. 启动

docker compose up -d --build

5. 验证

docker compose config
docker compose ps
docker compose exec backend python -c "from app.core.config import settings; print(settings.ENV); print(settings.JWT_SECRET_KEY == 'dev-secret'); print(bool(settings.LOGIN_RSA_PRIVATE_KEY))"
curl -i http://127.0.0.1:8888/health
curl -i http://127.0.0.1:8888/api/v1/auth/login-key

预期:

ENV=production
JWT_SECRET_KEY == dev-secret -> False
LOGIN_RSA_PRIVATE_KEY_SET -> True
GET /health -> 200
GET /api/v1/auth/login-key -> 200

6. 验收门禁

在把 main 推进到 release 前,至少执行:

docker compose run --rm -v "$PWD/backend/tests:/code/tests:ro" backend python -m pytest
cd frontend && npm run test:unit
cd frontend && npm run type-check
cd frontend && npm run build

release 分支:生产环境

release 是正式生产稳定分支。只有正式发布和生产 hotfix 应进入该分支。

1. 切换分支

git checkout release
git pull --rebase origin release

2. 配置 .env

生产环境必须使用生产专用配置:

COMPOSE_PROJECT_NAME=ctms_prod
ENV=production
JWT_SECRET_KEY=<production-strong-random-secret>
LOGIN_RSA_KEY_ID=prod-YYYYMMDD
LOGIN_RSA_PRIVATE_KEY=<production-rsa-private-key-pem-with-\n-escaped-newlines>

要求:

  • .env 必须只保存在部署机器或密钥管理系统中。
  • 不得提交 .env、私钥、JWT 密钥。
  • JWT_SECRET_KEY 轮换会使既有 token 失效,应安排维护窗口。
  • LOGIN_RSA_PRIVATE_KEY 轮换会影响新登录密钥获取,应同步更新 LOGIN_RSA_KEY_ID

3. HTTPS 要求

登录加密依赖浏览器 WebCrypto。生产访问必须使用 HTTPS:

  • https://正式域名
  • 本地 localhost 是浏览器安全上下文例外,但不能代表生产可用性

如果生产仍通过 http://服务器IP:8888 访问,前端会拒绝执行登录加密。

4. 初始化与迁移

首次部署或每次包含 migration 的发布:

docker compose run --rm backend-init

确认 migration 成功后再启动或滚动重启服务。

5. 启动

docker compose up -d --build

6. 生产验证

docker compose ps
docker compose exec backend python -c "from app.core.config import settings; print(settings.ENV); print(settings.JWT_SECRET_KEY == 'dev-secret'); print(bool(settings.LOGIN_RSA_PRIVATE_KEY)); print(settings.LOGIN_RSA_KEY_ID)"
curl -i https://<production-domain>/health
curl -i https://<production-domain>/api/v1/auth/login-key

预期:

ENV=production
JWT_SECRET_KEY == dev-secret -> False
LOGIN_RSA_PRIVATE_KEY_SET -> True
GET /health -> 200
GET /api/v1/auth/login-key -> 200

7. 多实例约束

当前登录 challenge 默认保存在后端进程内:

  • 单实例、单 worker:可直接使用
  • 多实例或多 worker:必须满足以下至少一项
    • 使用共享缓存保存 challenge,例如 Redis
    • 启用粘性会话,确保 /login-key/login 命中同一后端进程
    • 将后端限制为单 worker 单副本

如果不满足,上线后可能出现偶发登录失败。

环境变量说明

变量 dev main/staging release/production 说明
COMPOSE_PROJECT_NAME ctms_dev ctms_staging ctms_prod 防止不同环境容器名、网络名冲突
ENV development production production 后端运行模式
JWT_SECRET_KEY dev-secret 强随机 强随机 JWT 签名密钥
LOGIN_RSA_KEY_ID default staging-YYYYMMDD prod-YYYYMMDD 登录 RSA 密钥版本
LOGIN_RSA_PRIVATE_KEY staging 私钥 production 私钥 RSA 私钥,生产模式必填

常见问题

登录页提示当前浏览器环境不支持安全登录加密

原因:非 HTTPS、非 localhost 的访问环境不满足 WebCrypto 安全上下文要求。

处理:

  • 本地使用 http://localhost:8888
  • staging/production 使用 HTTPS 域名

production 模式启动失败,提示必须配置 LOGIN_RSA_PRIVATE_KEY

原因:ENV=production 时必须提供固定 RSA 私钥。

处理:

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048

将输出 PEM 写入 .envLOGIN_RSA_PRIVATE_KEY,换行使用 \n 转义。

切换分支后环境不符合预期

分支不会自动修改 .env。切换分支后应重新确认:

docker compose config
docker compose exec backend python -c "from app.core.config import settings; print(settings.ENV)"

必要时修改 .env 并重启:

docker compose up -d --build backend nginx