diff --git a/.env.docker.example b/.env.docker.example index 8d6fc1b..c86d0c0 100644 --- a/.env.docker.example +++ b/.env.docker.example @@ -38,4 +38,13 @@ GAOKAO_SMTP_USE_TLS=false GAOKAO_SMTP_USE_SSL=false GAOKAO_ALERT_RECIPIENTS= GAOKAO_ALERT_WEBHOOK_URLS= -GAOKAO_OPS_ALERT_LOG=/var/lib/gaokao/ops-alerts.jsonl \ No newline at end of file +GAOKAO_OPS_ALERT_LOG=/var/lib/gaokao/ops-alerts.jsonl +# === Review Remediation 2026-07-05 新增安全要求 === +# 1. Admin JWT 不再允许 URL query 参数传递(T1-01) +# 2. payment-return 需要 return_nonce(T2-01,由 checkout_token 自动生成) +# 3. Portal token v2 带 jti,支持撤销(T2-04) +# 4. 附件上传校验 magic bytes + MIME(T3-01) +# 5. Alipay notify body 限制 64KB(T3-02) +# 6. 物理删除订单必须带 actor/reason,自动写 deletion audit(T3-03) +# 7. schema_migrations 版本化迁移(T3-04,旧库自动升级) +# 8. LLM provider 生产环境必须配置(GAOKAO_LLM_PROVIDER=none 在 prod 会 fail-closed) diff --git a/docs/PRODUCTION_DEPLOYMENT_CHECKLIST_2026-06-15.md b/docs/PRODUCTION_DEPLOYMENT_CHECKLIST_2026-06-15.md index 0f08ff6..e12c9fe 100644 --- a/docs/PRODUCTION_DEPLOYMENT_CHECKLIST_2026-06-15.md +++ b/docs/PRODUCTION_DEPLOYMENT_CHECKLIST_2026-06-15.md @@ -1,6 +1,7 @@ # PRODUCTION_DEPLOYMENT_CHECKLIST -最后更新: 2026-06-15 +最后更新: 2026-07-06T16:17:01+08:00 +状态: Review Remediation Phase 0~5 已全部通过;本清单仅剩外部凭证依赖项 适用范围: `gaokao-volunteer-system` 在在线服务器上的首次部署 / 上线前联调 真相源: `docs/CURRENT_STATE.md` @@ -265,14 +266,40 @@ PY ### A. 必须通过 -- [ ] prod 配置可成功启动 -- [ ] 真实支付回调成功落库 -- [ ] portal 支付后状态自动推进 +#### 已通过(Phase 0~5 本地验证) + +- [x] prod 配置 fail-closed 机制(JWT/portal token/admin password/payment provider) +- [x] `/health` 端点返回 readiness checks(db_writable / disk_writable / settings_valid) +- [x] 公共下单限流 + 幂等 + 错误脱敏 +- [x] payment-return 需 return_nonce(不再裸 payment_id 换 token) +- [x] Portal token v2/jti/revocation +- [x] 附件 magic bytes + MIME 校验 +- [x] Alipay notify body size limit +- [x] 删除审计强制化(DAO 层 actor/reason 必填) +- [x] schema_migrations 版本化迁移 +- [x] Admin JWT 不允许 query token +- [x] Admin 真实 /api/auth/login + Bearer 注入 + RequireAuth 校验 +- [x] Admin 全导航 e2e(含 /admin/review) +- [x] 前端 typecheck/lint/test/build/e2e 全通过 +- [x] mypy 0 errors / ruff clean / coverage 90%+ +- [x] Docker poster build 可复现 +- [x] compose healthcheck 端口一致 +- [x] 100-case smoke PASS +- [x] 浏览器视觉/用户流验收 PASS(T5-02) +- [x] systemd/cron/backup 脚本语法/编译通过 + +#### 待外部凭证(部署后执行) + +- [ ] prod 配置在目标服务器可成功启动(需 GAOKAO_ENV=prod + 全部密钥) +- [ ] 真实支付回调成功落库(需 GAOKAO_PAYMENT_PROVIDER=alipay + 商户密钥) +- [ ] portal 支付后状态自动推进(需真实回调 URL) - [ ] 资料提交后状态推进到 processing -- [ ] watchdog 失败时产生真实告警 +- [ ] watchdog 失败时产生真实告警(需 SMTP/IM 配置) - [ ] `/admin/notifications` 可查看通知审计 - [ ] `/admin/ops-alerts` 可查看运维告警 - [ ] `bash scripts/backup_verify.sh --from-backup ...` 在目标机可跑通 +- [ ] LLM provider 生产配置可用(需 GAOKAO_LLM_API_KEY) + ### B. 不可伪完成 @@ -290,6 +317,6 @@ PY - “支付已上线” - “告警已联通” -只有在本清单第 7 节 A 全部打勾后,才能说: +只有在本清单第 7 节 A 的「待外部凭证」部分全部打勾后,才能说: > 当前版本已达到生产上线前的最小可验证标准。 diff --git a/scripts/gaokao-production-doctor.sh b/scripts/gaokao-production-doctor.sh new file mode 100755 index 0000000..50e5c75 --- /dev/null +++ b/scripts/gaokao-production-doctor.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +set -euo pipefail + +# gaokao-production-doctor.sh +# 检查生产部署前置条件,返回 ready / not-ready + 缺失项清单。 +# 用法: bash scripts/gaokao-production-doctor.sh [--env-file .env] + +ENV_FILE="${1:-}" +if [ -n "$ENV_FILE" ] && [ -f "$ENV_FILE" ]; then + set -a; source "$ENV_FILE"; set +a +fi + +PASS=0 +FAIL=0 +MISSING=() + +check() { + local name="$1" + local value="${!2:-}" + if [ -n "$value" ] && [ "$value" != "mock" ] && [ "$value" != "dev" ]; then + echo " [PASS] $name" + PASS=$((PASS + 1)) + else + echo " [FAIL] $name (empty or placeholder)" + FAIL=$((FAIL + 1)) + MISSING+=("$name") + fi +} + +echo "=== Gaokao Production Readiness Doctor ===" +echo "" + +echo "--- Environment ---" +check "GAOKAO_ENV" GAOKAO_ENV + +echo "" +echo "--- Security Keys ---" +check "GAOKAO_JWT_SECRET" GAOKAO_JWT_SECRET +check "GAOKAO_PORTAL_TOKEN_SECRET" GAOKAO_PORTAL_TOKEN_SECRET +check "GAOKAO_ORDERS_FERNET_KEY" GAOKAO_ORDERS_FERNET_KEY +check "GAOKAO_ADMIN_PASS" GAOKAO_ADMIN_PASS + +echo "" +echo "--- Payment (alipay) ---" +check "GAOKAO_PAYMENT_PROVIDER" GAOKAO_PAYMENT_PROVIDER +check "GAOKAO_PAYMENT_APP_ID" GAOKAO_PAYMENT_APP_ID +check "GAOKAO_PAYMENT_PRIVATE_KEY_PATH" GAOKAO_PAYMENT_PRIVATE_KEY_PATH +check "GAOKAO_PAYMENT_ALIPAY_PUBLIC_KEY_PATH" GAOKAO_PAYMENT_ALIPAY_PUBLIC_KEY_PATH +check "GAOKAO_PAYMENT_NOTIFY_URL" GAOKAO_PAYMENT_NOTIFY_URL +check "GAOKAO_PAYMENT_RETURN_URL" GAOKAO_PAYMENT_RETURN_URL +check "GAOKAO_PAYMENT_WEBHOOK_SECRET" GAOKAO_PAYMENT_WEBHOOK_SECRET + +echo "" +echo "--- LLM ---" +check "GAOKAO_LLM_PROVIDER" GAOKAO_LLM_PROVIDER +check "GAOKAO_LLM_API_KEY" GAOKAO_LLM_API_KEY + +echo "" +echo "--- SMTP / Alert ---" +check "GAOKAO_SMTP_HOST" GAOKAO_SMTP_HOST +check "GAOKAO_SMTP_SENDER" GAOKAO_SMTP_SENDER +check "GAOKAO_ALERT_RECIPIENTS" GAOKAO_ALERT_RECIPIENTS + +echo "" +echo "--- Summary ---" +echo "PASS: $PASS" +echo "FAIL: $FAIL" +if [ "$FAIL" -gt 0 ]; then + echo "MISSING: ${MISSING[*]}" + echo "" + echo "RESULT: NOT READY — 请补全上述缺失的外部凭证后重新运行。" + exit 1 +else + echo "RESULT: READY — 所有前置条件已满足,可以执行生产部署。" + exit 0 +fi