Files
crearte-monorepo/docs/plans/2026-10-02-p11-server-hardening.md
T
XingfenD 708f95eb3d docs: P11 server-hardening delivered (server 0.17.0 / deploy 0.7.0 / crearte 0.24.1)
ROADMAP P11 row -> 完成 with the D-A->D-A' re-pin narrative; doc index updated;
CHANGELOG 0.3.4 (Done section, bilingual) recording the endpoint-verification
catch: the original subnet-trust design was a rate-limit bypass under compose's
docker SNAT, corrected to an empty TRUSTED_PROXIES default. Register the P11
spec + plan in the doc index.
2026-10-02 03:54:41 +08:00

7.5 KiB
Raw Blame History

P11 服务端安全硬化批 — 实现计划

日期:2026-10-02 | spec:docs/specs/2026-10-02-p11-server-hardening-design.md(权威,冲突时 spec 赢)

全局约束(逐字进每个任务简报)

  1. 零新依赖:crearte-server/src/go.mod 与 go.sum 不得出现新 require;前端 package.json 不动。

  2. Go 工具链(硬性):宿主 Go 是 1.18,不可用。所有 go build/vet/test 必须在容器里跑:

    cd <repo>/crearte-server/src && docker run --rm \
      -v "$PWD":/src -w /src \
      -v crearte_gomod:/go/pkg/mod \
      -e GOCACHE=/gocache -v crearte_gocache:/gocache \
      -e GOPROXY=https://goproxy.cn,direct -e GOSUMDB=sum.golang.google.cn \
      golang:1.24-alpine go test ./internal/api/ -run 'X' -count=1
    

    冷构建 1–3 分钟属正常,用 exec 后台 + 耐心轮询,不要因为没立刻出结果就重跑。proxy.golang.org 在本机不可达,漏掉 GOPROXY 会挂死。

  3. 禁触文件:crearte-server 的 internal/bundle/**、internal/repository/migrations/**、internal/storage/**、cmd/**(除 spec 明列);crearte 的 src/app/**、src/e2e/**、vite.config.ts、package.json、各 config;crearte-deploy 的 scripts/**、.github/**、backups/**。

  4. compose 数据身份红线:不得重命名或增删任何卷(pgdata-*、bundle-keys-*、minio-data-*、dev_node_modules、mock_node_modules)。不得改 depends_on 健康门控。不得移除 ${VAR:?…} 守卫。

  5. 绝不 docker compose … down -v(会毁 postgres 数据、bundle keys、MinIO 对象,且 MinIO bucket 初始化须手工重做)。收尾用裸 down。

  6. TEST_DATABASE_URL 绝不指向 db-debug/pgdata-dev——db-debug 与 db-dev 共享 pgdata-dev 卷,误指会清空开发数据。集成测试只用 db-test(pgdata-test,127.0.0.1:5432)。

  7. TDD:先写/改测试跑红,再实现跑绿;报告里贴 RED/GREEN 的命令与输出摘要。

  8. 每任务独立 commit,前缀 fix(security): / fix(config): / feat(observability): / chore(deploy): + 任务号;CHANGELOG 不由任务写,控制者波次末统一补。

  9. 分支纪律:commit 前 git branch --show-current 必须是本任务指定分支;不 merge、不 push、不碰 wrapper 仓(ROADMAP/spec/plan 记账归控制者)。

    • crearte-server → fix/p11-server-hardening
    • crearte-deploy → feat/p11-trusted-proxies
    • crearte → feat/p11-trusted-proxies
  10. 精确使用 spec 给定值:subnet 与 TRUSTED_PROXIES 默认值均为 172.28.0.0/24(已核实宿主 172.17.0.0/16 docker0、172.18.0.0/16 baota_net 之外空闲);X-Real-IP 用 $remote_addr;Referrer-Policy 用 strict-origin-when-cross-origin;X-Content-Type-Options 用 nosniff;两个 add_header 都带 always。

  11. 数量/数值必须实测核实:简报或 spec 里引用的计数(几处 location、几个 add_header、几个限流器)动手前自己 grep 核准,发现不符以实测为准并在报告里披露。本仓上一波(P9-B)控制者连错两次数字(对比度 3.16 应为 2.83、「19 个 th」是行计数应为 24 元素),均由实现者纠正——这条纪律有效,继续保持。

  12. 验收命令(每任务收尾自跑并贴摘要):server 任务 = 本包 go test + go vet ./... + gofmt -l(应无输出);deploy/crearte 任务见各自简报。全量腿由控制者波次末跑。

任务分解与并行度

文件面两两不相交才可并行。同一 Go 包内并发编辑会让兄弟任务的 go test 看到半成品而假红,故 T1 与 T2 同属 internal/api,必须串行。

任务 仓 / 包 改动文件 波次
T1 限流表硬上界(缺陷 B) server / internal/api ratelimit.go、ratelimit_test.go 1
T2 信任代理链 + 启动告警(缺陷 A、D-C) server / internal/api router.go、新 clientip_test.go、router_test.go 2(T1 后)
T3 LOG 归一 + parseTrustedProxies 覆盖(缺陷 C) server / internal/config config.go、config_test.go 1
T4 compose 固定 subnet + TRUSTED_PROXIES(D-A) deploy docker-compose.yml、.env.example、README.md 1
T5 nginx 反代头加固(D-E) crearte deploy/nginx.conf.template 1
T6 验收(控制者本人,不派发) 三仓 — 3

波次 1 并行:T1 + T3 + T4 + T5(四个不同仓/包,零文件重叠)。 波次 2:T2(等 T1 落地,同包串行)。 波次 3:T6 控制者验收 + 终审。

T6 验收腿(控制者本人)

  1. server:gofmt -l(空)、go vet ./...、go test ./... -count=1、go test ./... -race -count=1、go build ./...;集成腿用 db-test(docker compose --profile debug up -d db-test 后 TEST_DATABASE_URL=postgres://crearte:<pw>@127.0.0.1:5432/crearte?sslmode=disable go test ./... -count=1),确认 skip 守卫数为 0。
  2. deploy:四 profile docker compose --profile dev|prod|debug|mock config -q 全过;config 输出断言 api-prod/api-dev 的 TRUSTED_PROXIES = 172.28.0.0/24、networks.default.ipam.config[0].subnet 同值、卷名集合与改动前逐字相同。
  3. crearte:五腿全量不回退(vitest 626 / vue-tsc 0 / build OK / 主 e2e 80+1skip / noauth 4)——nginx 模板属部署资产,前端测试不应受影响,但必须实跑确认。
  4. 端到端(缺陷 A 的回归钉桩,最关键一腿):docker compose --profile dev up -d --build → ps 健康 →
    • 启动日志无 D-C 告警(因为 T4 注入了 TRUSTED_PROXIES);
    • 用两个不同 X-Forwarded-For 各打 /api/auth/login 若干次,确认各自独立计数(改前是共享桶 → 第二个用户首次即 429;改后两个用户互不干扰);
    • 伪造抗性:客户端自带 X-Forwarded-For: 8.8.8.8 打一次,确认服务端日志/限流键取的是网关追加后的真实值而非 8.8.8.8;
    • nginx -t 校验模板渲染;确认新安全头出现在响应里(含 4xx 路径,验 always)。
    • 收尾 docker compose --profile dev down(绝不 -v)。
  5. prod profile 冒烟:--profile prod up -d --build → /healthz + /metrics → down。

记账(控制者,波次末)

  • crearte-server/docs/CHANGELOG.md → 0.17.0;crearte-deploy/docs/CHANGELOG.md → 0.7.0;crearte/docs/CHANGELOG.md → 0.24.1;wrapper docs/CHANGELOG.md → 0.3.4。四仓均双语(同条目英中相邻行、条目间空行、高版本在上)。
  • 三内仓各自 git merge --no-ff <branch> → push → 删分支(内仓不得在 master 直接 commit)。wrapper 可 master 直提(2026-09-29 已获批)。
  • wrapper docs/ROADMAP.md:新增 P11 行 + 文档索引表补 spec/plan 两行(AGENTS.md 硬性要求)。
  • memory/2026-10-02.md:记录本波缺陷链(A 掩盖 B)、spike 方法论、以及「修一个缺陷可能让另一个从不可达变可达」这条教训。

打磨批 / 挂账(不在本批)

  • CSP(iframe + Service Worker 加载用户作品,需独立设计批)。
  • 共享限流存储(Redis 等)——多实例化前置条件,见 spec D-G。
  • P9-B 打磨批四条(vue-router 升级复看弱断言、撤评语义进可访问名、手动关最新 toast 的播报重念、spec 口径已修)。
  • 备份恢复演练实证(backup.sh/restore-drill.sh 已交付但 prod 栈演练未跑,属 owner 手动尾巴)。