OSS + ESA + 服务端上线前检查单
架构目标:
reader.fcana.com -> ESA -> 书店 UI
reader-data.fcana.com -> ESA -> JSON / OPDS / 下载授权
reader-file.fcana.com -> ESA -> 私有 OSS
说明:P0 为上线硬门槛;P1 为强烈建议;P2 为上线后优化项。
0. 测试样本
准备一个带空格和中文的测试对象,用来发现路径编码问题:
bookshelf-prod/books/preflight/a b-中文.epub
记录:
- Bucket:
________________ - Region:
cn-beijing - Prefix:
bookshelf-prod - 文件域名:
reader-file.fcana.com - 数据域名:
reader-data.fcana.com
A. OSS
| 编号 | 级别 | 检查项 | 检测方法 | 预期结果 | 结果 |
|---|---|---|---|---|---|
| A01 | P0 | Bucket 地域 | OSS 控制台 | 华北2(北京) | ☐ |
| A02 | P0 | Bucket 权限 | OSS 控制台 | 私有,未开启公共读 | ☐ |
| A03 | P0 | 公共访问阻断 | OSS ACL/Policy | 无匿名读取权限 | ☐ |
| A04 | P0 | Object Key | 控制台搜索测试对象 | 完整 Key 含 bookshelf-prod/ | ☐ |
| A05 | P0 | 裸 OSS 直链 | curl.exe -I | 403,不能是 200 | ☐ |
| A06 | P0 | RAM 写入权限 | RAM Policy | 仅目标 Bucket/Prefix;无管理权限 | ☐ |
| A07 | P0 | 服务端上传 | 上传测试文件 | OSS 中存在且大小一致 | ☐ |
| A08 | P1 | Content-Type | 查看对象 Metadata | EPUB 为 application/epub+zip | ☐ |
| A09 | P1 | 对象不可变 | 查看 Key 规则 | 更新后使用新 hash/版本路径 | ☐ |
| A10 | P1 | 删除保护 | 查看清理逻辑 | 不使用全桶 sync --delete | ☐ |
Windows CMD:
curl.exe -I "https://BUCKET.oss-cn-beijing.aliyuncs.com/bookshelf-prod/books/preflight/a%20b-%E4%B8%AD%E6%96%87.epub"
通过:403 Forbidden。返回 200 时立即停止上线。
B. 服务端配置
| 编号 | 级别 | 检查项 | 检测方法 | 预期结果 | 结果 |
|---|---|---|---|---|---|
| B01 | P0 | Endpoint | 查看 Storage Profile | 北京公网或可用内网 Endpoint | ☐ |
| B02 | P0 | Bucket | 查看配置 | 只填 Bucket 名 | ☐ |
| B03 | P0 | Region | 查看配置 | cn-beijing | ☐ |
| B04 | P0 | Prefix | 查看配置 | bookshelf-prod,无首尾 / | ☐ |
| B05 | P0 | CDN Base URL | 查看配置 | 仅 https://reader-file.fcana.com | ☐ |
| B06 | P0 | 下载模式 | 查看配置 | 阿里云 ESA HMAC CDN | ☐ |
| B07 | P0 | HMAC Secret | 查看状态,不打印明文 | 与 ESA 使用同一密钥 | ☐ |
| B08 | P0 | Secret 安全 | 搜 Git/日志/前端 | 无明文 Secret、AK、SK | ☐ |
| B09 | P0 | 签名协议 | 检查代码 | exp + sig,HMAC-SHA256,小写 hex | ☐ |
| B10 | P0 | 时间戳 | 查看真实 URL | Unix 秒,不是毫秒 | ☐ |
| B11 | P0 | 路径一致 | 比较三处路径 | OSS Key、CDN path、签名 path 完全一致 | ☐ |
| B12 | P0 | 特殊字符 | 生成测试 URL | 无 %2520 或双重编码 | ☐ |
| B13 | P0 | 无 OSS 原生签名 | 检查 Location | 无 OSSAccessKeyId/x-oss-signature | ☐ |
| B14 | P0 | 下载授权 | 带账号请求 | 302/307 到 reader-file | ☐ |
| B15 | P0 | 授权端点不缓存 | 查看响应头 | Cache-Control: private, no-store | ☐ |
| B16 | P0 | 未登录访问 | 不带凭据请求 | 401/403,无 Location | ☐ |
| B17 | P0 | 越权访问 | 无权限账号请求 | 403/404,无 Location | ☐ |
| B18 | P1 | TTL | 比较 exp-now | 建议约 300 秒 | ☐ |
| B19 | P1 | 幂等入库 | 同 key 重复提交 | 只产生一个正式书目 | ☐ |
| B20 | P1 | 健康检查 | 请求 /readyz | 200 | ☐ |
curl.exe -u "USERNAME:PASSWORD" -D - -o NUL "https://reader-data.fcana.com/download/BOOK_ID"
通过响应:
302
Location: https://reader-file.fcana.com/bookshelf-prod/...?exp=...&sig=...
Cache-Control: private, no-store
C. ESA DNS 与私有 OSS 回源
| 编号 | 级别 | 检查项 | 检测方法 | 预期结果 | 结果 |
|---|---|---|---|---|---|
| C01 | P0 | DNS 记录 | ESA 控制台 | reader-file 已开启代理 | ☐ |
| C02 | P0 | CNAME | nslookup | 指向 ESA,不是 VPS IP | ☐ |
| C03 | P0 | HTTPS | curl.exe -I | 证书有效且域名匹配 | ☐ |
| C04 | P0 | 源站类型 | ESA 控制台 | OSS,私有访问-同账号 | ☐ |
| C05 | P0 | Bucket | ESA 控制台 | 与服务端上传 Bucket 相同 | ☐ |
| C06 | P0 | 回源授权 | ESA 控制台 | 自动授权成功,只读回源 | ☐ |
| C07 | P0 | 不经 Nginx | 查看 Nginx/流量 | 文件请求不进入 VPS | ☐ |
| C08 | P1 | 静态网站冲突 | OSS 控制台 | 未启用静态网站托管 | ☐ |
nslookup reader-file.fcana.com
D. ESA HMAC 旁路函数
| 编号 | 级别 | 检查项 | 检测方法 | 预期结果 | 结果 |
|---|---|---|---|---|---|
| D01 | P0 | 函数已发布 | 函数和 Pages | 有生产版本 | ☐ |
| D02 | P0 | 旁路模式 | 路由配置 | Bypass 已开启 | ☐ |
| D03 | P0 | 路由范围 | 查看路由 | 只匹配 bookshelf-prod/books/*、covers/* | ☐ |
| D04 | P0 | 算法一致 | 比对代码 | HMAC-SHA256、UTF-8 Secret、64 位 hex | ☐ |
| D05 | P0 | 签名原文 | 固定向量验证 | encoded_path + exp | ☐ |
| D06 | P0 | 无签名 | 直接请求 | 403 | ☐ |
| D07 | P0 | 错误签名 | 改 sig 一位 | 403 | ☐ |
| D08 | P0 | 过期签名 | 使用旧 URL | 403 | ☐ |
| D09 | P0 | 过远未来 exp | 手工构造 | 403 | ☐ |
| D10 | P0 | 合法签名 | 服务端生成 URL | 200 或 206 | ☐ |
| D11 | P0 | 中文/空格路径 | 测试对象 | 合法 URL 成功,篡改编码失败 | ☐ |
| D12 | P1 | 方法限制 | POST/PUT/DELETE | 403/405 | ☐ |
| D13 | P1 | 函数指标 | 指标页面 | 无持续脚本错误或 CPU 超限 | ☐ |
curl.exe -I "https://reader-file.fcana.com/bookshelf-prod/books/preflight/a%20b-%E4%B8%AD%E6%96%87.epub"
必须返回 403。
E. ESA 缓存
| 编号 | 级别 | 检查项 | 检测方法 | 预期结果 | 结果 |
|---|---|---|---|---|---|
| E01 | P0 | 文件缓存规则 | 查看规则 | 只匹配文件域名的 books/covers | ☐ |
| E02 | P0 | Cache Key 忽略 exp | 查看规则 | exp 不参与 Cache Key | ☐ |
| E03 | P0 | Cache Key 忽略 sig | 查看规则 | sig 不参与 Cache Key | ☐ |
| E04 | P0 | 无全局参数冲突 | 查看全局设置 | 未开启冲突的全站忽略 query | ☐ |
| E05 | P0 | 首次请求 | 合法 URL A | MISS 或首次回源 | ☐ |
| E06 | P0 | 第二次请求 | 同路径合法 URL B | HIT 或 Age 增加 | ☐ |
| E07 | P0 | HIT 后仍验签 | 错误签名访问已缓存对象 | 403 | ☐ |
| E08 | P0 | 错误响应不长缓存 | 先错后对 | 合法请求不持续命中旧 403/404 | ☐ |
| E09 | P1 | TTL | 查看规则 | 不可变 hash 路径可长缓存 | ☐ |
| E10 | P1 | 刷新流程 | 刷新测试对象 | 能正确清除对应缓存 | ☐ |
最关键的三步:
合法 URL A -> MISS
合法 URL B(同路径、不同 exp/sig)-> HIT
错误签名 URL -> 403
错误签名得到 200 时立即停止上线。
F. Range 与大文件
| 编号 | 级别 | 检查项 | 检测方法 | 预期结果 | 结果 |
|---|---|---|---|---|---|
| F01 | P0 | Range | 请求 0-1023 | 206 Partial Content | ☐ |
| F02 | P0 | Content-Range | 看响应头 | bytes 0-1023/总大小 | ☐ |
| F03 | P0 | Accept-Ranges | 看响应头 | bytes | ☐ |
| F04 | P0 | 文件大小 | HEAD/完整下载 | 与 OSS 对象一致 | ☐ |
| F05 | P1 | Content-Type | 看响应头 | 类型正确 | ☐ |
| F06 | P1 | 文件名 | 实际下载 | 中文名可用,至少有 ASCII fallback | ☐ |
| F07 | P1 | 断点续传 | 中断后续传 | 不从头重新下载 | ☐ |
| F08 | P0 | 不经 VPS | 观察网卡 | VPS 无文件主体流量 | ☐ |
curl.exe -H "Range: bytes=0-1023" -D - -o range-test.bin "完整合法签名URL"
G. CORS(前端使用 fetch/Blob 时升为 P0)
| 编号 | 级别 | 检查项 | 检测方法 | 预期结果 | 结果 |
|---|---|---|---|---|---|
| G01 | P1 | Allow-Origin | 带 Origin 请求 | https://reader.fcana.com | ☐ |
| G02 | P1 | Expose-Headers | 看响应 | 暴露 Length、Range、Disposition 等 | ☐ |
| G03 | P1 | Credentials | 看响应 | 不依赖 Cookie 时不启用 | ☐ |
| G04 | P1 | OPTIONS | 预检请求 | 200/204 或确认业务不触发预检 | ☐ |
| G05 | P1 | 浏览器实测 | UI 点击下载 | 无 CORS 报错 | ☐ |
curl.exe -I -H "Origin: https://reader.fcana.com" "完整合法签名URL"
H. 旧 ESA 规则迁移
| 编号 | 级别 | 检查项 | 检测方法 | 预期结果 | 结果 |
|---|---|---|---|---|---|
| H01 | P0 | 新下载端点绕过缓存 | 请求两次 | 每次重新鉴权/签发 | ☐ |
| H02 | P0 | JSON/API 动态 | 查看规则 | /api/* 不共享缓存 | ☐ |
| H03 | P0 | OPDS 权限 | 两账号请求 | 内容按权限隔离 | ☐ |
| H04 | P0 | 旧规则不抢先 | 检查顺序和 Host | 新旧规则互不覆盖 | ☐ |
| H05 | P0 | 旧 Authorization Cache Key | 查看匹配范围 | 不作用于 reader-file | ☐ |
| H06 | P1 | 旧链路可回滚 | 保留配置 | 观察期内可切回 | ☐ |
| H07 | P1 | 协议接口无 JS Challenge | Koodo/KOReader 实测 | 返回 XML/302,不返回 HTML | ☐ |
| H08 | P1 | 管理 API 未全放行 | 查看 WAF | 仍有正常安全防护 | ☐ |
| H09 | P2 | 清理旧规则 | 稳定后执行 | 删除旧下载缓存和 Koodo 白名单 | ☐ |
I. 端到端
| 编号 | 级别 | 检查项 | 检测方法 | 预期结果 | 结果 |
|---|---|---|---|---|---|
| I01 | P0 | 上书 | 上传测试 EPUB | 任务成功、对象进入 OSS | ☐ |
| I02 | P0 | JSON | 请求 catalog | 书目和下载入口正确 | ☐ |
| I03 | P0 | OPDS | 客户端/curl | acquisition 指向新授权端点 | ☐ |
| I04 | P0 | UI | 浏览器 | 封面、分类、下载正常 | ☐ |
| I05 | P0 | 跳转链 | 点击下载 | reader-data -> 302 -> reader-file | ☐ |
| I06 | P0 | 完整性 | 比较 hash/打开 EPUB | 文件完整可读 | ☐ |
| I07 | P0 | 权限隔离 | 无权限账号 | UI/JSON/OPDS/下载均不可越权 | ☐ |
| I08 | P0 | 隐藏/删除 | 隐藏书目后重试 | 新请求立即不可见;旧 URL 到期失效 | ☐ |
| I09 | P1 | 客户端兼容 | Koodo/KOReader | 能下载、识别文件名和格式 | ☐ |
| I10 | P1 | 移动端 | 手机浏览器 | 能下载或打开系统分享 | ☐ |
| I11 | P1 | 监控 | 下载大文件 | ESA/OSS 有流量,VPS 无主体流量 | ☐ |
J. Go / No-Go
以下任意一项出现,立即 No-Go:
- ☐ OSS 裸直链返回
200 - ☐
reader-file无签名返回200 - ☐ 错误签名在缓存 HIT 后返回
200 - ☐ OSS Key、CDN path、签名 path 不一致
- ☐ 空格或中文路径发生双重编码
- ☐ 服务端生成 OSS 原生预签名参数
- ☐ 下载授权 302 被共享缓存
- ☐ 无权限用户取得
Location - ☐ Range 不返回
206 - ☐ 文件主体流量仍经过 VPS
- ☐ Secret、AK、SK 出现在 Git、前端或日志
- ☐ 新 ESA 规则被旧规则覆盖
允许上线的条件:
- ☐ 所有 P0 通过
- ☐ P1 失败项均有风险说明和处理日期
- ☐ 已备份数据库、服务端配置和 ESA 规则
- ☐ 已完成一份真实 EPUB 的完整端到端下载
- ☐ 已完成“缓存 HIT 后错误签名仍 403”测试
- ☐ 已记录回滚操作
K. 推荐上线顺序
1. 备份旧配置
2. 上传测试对象
3. 验证 OSS 裸直链 403
4. 接入 reader-file 私有 OSS
5. 发布 HMAC 旁路函数
6. 验证无签名/错误/过期均 403
7. Cache Key 忽略 exp 和 sig
8. 验证 MISS -> HIT -> 错误签名仍 403
9. 验证 Range 206
10. 服务端启用新 Storage Profile
11. 上传真实测试刊物
12. 验证 JSON、OPDS、UI、302 和完整下载
13. 小范围切换
14. 观察至少 24 小时
15. 再清理旧 Koodo ESA 规则
L. 回滚
1. 暂停新上书
2. 下载入口切回旧端点
3. 恢复旧 Storage Profile
4. 保留 OSS 新对象,不批量删除
5. 禁用 reader-file 函数路由
6. 恢复旧 ESA 规则
7. 排查 prefix、编码、HMAC、Cache Key 和规则优先级
回滚时不要公开 Bucket,也不要为了临时可用而关闭 HMAC 后让文件域名裸奔。