fix: 安全加固(VUL-001~009)+ 构建版本注入 + 死代码清理 #2

Merged
sun merged 12 commits from develop into main 2026-08-27 11:01:48 +08:00
Owner

变更概览

develop → main,共 12 个提交,涉及 22 个文件(+803 / -86 行)。

本次合并以安全加固为核心,同时引入构建时版本注入和 CI/CD 自动化,并完成一轮死代码清理。


一、安全修复(6 项)

VUL-001(中):AAC/FLAC 响应 Content-Type 与真实数据不一致

  • 问题:上游将 aac/flac 降级为 mp3 时,finalFormat 仍保留客户端请求格式,导致响应头与字节流不符,客户端解码失败。
  • 修复:adapter/volcano/synthesis.go 中 finalFormat 改为反映真实输出格式(非 wav 时取 opts.Format)。

VUL-002(低):Transport 层错误不进入 UpstreamErrors 指标

  • 问题:原代码用 if errCode != 0 判断是否计错误,但合成链路中多种错误场景(transport_error / request_error / wrap_error / stream_error)的 errCode 为 0,导致这些场景在 /metrics 上完全不可见。
  • 修复:metrics/metrics.go 改为 if status != "ok",任何非 ok 状态均计为错误。火山接口不可达等网络故障首次在监控上可见。

VUL-003(低):XFF 解析可被伪造绕过 IP 限流

  • 问题:原代码直接取 X-Forwarded-For 第一个值,该值由客户端控制,攻击者可伪造任意 IP 绕过限流。
  • 修复:middleware/ratelimit.go 重写 GetClientIP,从 XFF 链尾部解析,支持两种模式:
    • 启发式模式(默认):从链尾取第一个公网 IP,自动跳过内网/回环地址。
    • 精确模式:通过环境变量 TRUSTED_PROXY_HOPS=N 指定反代跳数,适用于 Cloudflare + nginx 等多跳场景。

VUL-005(低):日志注入(RequestURI 与上游错误体)

  • 问题:r.RequestURI 和上游响应体未经转义直接写入日志,攻击者可在 URL 或错误体中注入 \n/\r 伪造日志行。
  • 修复:middleware/logger.go 和 adapter/volcano/synthesis.go 对换行符做转义处理。

VUL-006(低):监控端点(/metrics /health /dashboard)无鉴权

  • 判定:不引入新鉴权机制(会破坏 Prometheus 抓取),采用文档引导。
  • 修复:README 新增「公网部署:监控端点无鉴权」段落,含三端点风险表 + nginx 反代 basic auth 配置示例。

VUL-008 / VUL-009(信息):speed 与 sample_rate 文档描述修正

  • 修复:README 中 speed 描述从 "0.25 ~ 4.0" 修正为"客户端接受范围 0.25 ~ 4.0;火山实际生效范围 0.5 ~ 2.0,超出范围会被静默截断"。

二、新功能

构建时版本信息注入

  • 新建 version/version.go,声明 Version 和 Commit 两个包级变量(默认 "dev")。
  • Dockerfile 新增 ARG VERSION / ARG COMMIT,通过 -ldflags 在构建时注入。
  • controller/tts.go 的 /health 端点改用 version.Version / version.Commit,替代硬编码的 "2.0.0 (v3 API)"。
  • dto/health.go 的 HealthResponse 新增 Commit 字段。
  • 效果:开发环境显示 git describe 输出(如 v0.2.0-5-g4abcd5),生产环境(tag 构建)显示干净 semver,运维可通过 /health 确认"跑的到底是哪个 commit"。

GitHub Actions Docker 发布 Workflow

  • 新增 .github/workflows/docker.yml,自动化 Docker 镜像构建与发布。

三、工程改进

死代码清理(DEBT-2,7 文件,约 30 行)

  • 删除 middleware/auth.go 中 no-op 的 InitAPIKeys()
  • 删除 middleware/cors.go 中 no-op 的 InitCORSConfig()
  • 删除 dto/tts.go 中未使用的 ByteDanceTTSConfig 结构体
  • 删除 common/constants.go 中未使用的 MaxResponseTimes / MaxErrors 常量
  • 删除整个 middleware/ratelimit_middleware.go(功能已由 ratelimit_instrumented.go 替代)
  • 删除 controller/tts.go 中 resolveClientFormat 的冗余空字符串分支

限流中间件增强

  • middleware/ratelimit.go:新增 XFF 双模式解析(见 VUL-003),初始化时同步配置到 setting 包。
  • 新增 middleware/ratelimit_test.go:229 行单元测试,覆盖限流窗口、并发控制、XFF 解析等场景。

配置与忽略文件

  • .gitignore:新增忽略 TODO.md(本地待办清单不入仓库)
  • .env.example:新增环境变量示例
  • .dockerignore:更新构建上下文排除规则
  • setting/config.go:新增 TrustedProxyHops 等配置项的启动日志展示

四、文档

  • README.md(+147 行):speed/sample_rate 参数修正、监控端点安全说明、公网部署 best practice。
  • VULNERABILITY_REPORT.md(新增,197 行):完整代码审查交付物,含 9 项漏洞(高 1 / 中 2 / 低 4 / 信息 2)、8 条无风险核查项、工程债务记录、安全加固建议。

兼容性说明

  • 向后兼容:所有接口路径、请求/响应格式(除 /health 新增 commit 字段外)均保持不变。
  • 行为变更:
    • /metrics 中 UpstreamErrors 现在会统计 transport/request/wrap/stream 类错误(此前遗漏),监控告警阈值可能需要微调。
    • IP 限流的客户端 IP 解析策略变更(从 XFF 头部取第一个值 → 从尾部取第一个公网 IP),多跳反代部署建议配置 TRUSTED_PROXY_HOPS。
  • 配置新增:TRUSTED_PROXY_HOPS 环境变量(可选,默认启发式模式)。

## 变更概览 `develop` → `main`,共 **12 个提交**,涉及 **22 个文件**(+803 / -86 行)。 本次合并以**安全加固**为核心,同时引入构建时版本注入和 CI/CD 自动化,并完成一轮死代码清理。 --- ## 一、安全修复(6 项) ### VUL-001(中):AAC/FLAC 响应 Content-Type 与真实数据不一致 - **问题**:上游将 aac/flac 降级为 mp3 时,`finalFormat` 仍保留客户端请求格式,导致响应头与字节流不符,客户端解码失败。 - **修复**:`adapter/volcano/synthesis.go` 中 `finalFormat` 改为反映真实输出格式(非 wav 时取 `opts.Format`)。 ### VUL-002(低):Transport 层错误不进入 UpstreamErrors 指标 - **问题**:原代码用 `if errCode != 0` 判断是否计错误,但合成链路中多种错误场景(transport_error / request_error / wrap_error / stream_error)的 errCode 为 0,导致这些场景在 `/metrics` 上完全不可见。 - **修复**:`metrics/metrics.go` 改为 `if status != "ok"`,任何非 ok 状态均计为错误。火山接口不可达等网络故障首次在监控上可见。 ### VUL-003(低):XFF 解析可被伪造绕过 IP 限流 - **问题**:原代码直接取 `X-Forwarded-For` 第一个值,该值由客户端控制,攻击者可伪造任意 IP 绕过限流。 - **修复**:`middleware/ratelimit.go` 重写 `GetClientIP`,从 XFF 链**尾部**解析,支持两种模式: - **启发式模式(默认)**:从链尾取第一个公网 IP,自动跳过内网/回环地址。 - **精确模式**:通过环境变量 `TRUSTED_PROXY_HOPS=N` 指定反代跳数,适用于 Cloudflare + nginx 等多跳场景。 ### VUL-005(低):日志注入(RequestURI 与上游错误体) - **问题**:`r.RequestURI` 和上游响应体未经转义直接写入日志,攻击者可在 URL 或错误体中注入 `\n`/`\r` 伪造日志行。 - **修复**:`middleware/logger.go` 和 `adapter/volcano/synthesis.go` 对换行符做转义处理。 ### VUL-006(低):监控端点(/metrics /health /dashboard)无鉴权 - **判定**:不引入新鉴权机制(会破坏 Prometheus 抓取),采用文档引导。 - **修复**:README 新增「公网部署:监控端点无鉴权」段落,含三端点风险表 + nginx 反代 basic auth 配置示例。 ### VUL-008 / VUL-009(信息):speed 与 sample_rate 文档描述修正 - **修复**:README 中 speed 描述从 "0.25 ~ 4.0" 修正为"客户端接受范围 0.25 ~ 4.0;火山实际生效范围 0.5 ~ 2.0,超出范围会被静默截断"。 --- ## 二、新功能 ### 构建时版本信息注入 - 新建 `version/version.go`,声明 `Version` 和 `Commit` 两个包级变量(默认 `"dev"`)。 - `Dockerfile` 新增 `ARG VERSION` / `ARG COMMIT`,通过 `-ldflags` 在构建时注入。 - `controller/tts.go` 的 `/health` 端点改用 `version.Version` / `version.Commit`,替代硬编码的 `"2.0.0 (v3 API)"`。 - `dto/health.go` 的 `HealthResponse` 新增 `Commit` 字段。 - **效果**:开发环境显示 `git describe` 输出(如 `v0.2.0-5-g4abcd5`),生产环境(tag 构建)显示干净 semver,运维可通过 `/health` 确认"跑的到底是哪个 commit"。 ### GitHub Actions Docker 发布 Workflow - 新增 `.github/workflows/docker.yml`,自动化 Docker 镜像构建与发布。 --- ## 三、工程改进 ### 死代码清理(DEBT-2,7 文件,约 30 行) - 删除 `middleware/auth.go` 中 no-op 的 `InitAPIKeys()` - 删除 `middleware/cors.go` 中 no-op 的 `InitCORSConfig()` - 删除 `dto/tts.go` 中未使用的 `ByteDanceTTSConfig` 结构体 - 删除 `common/constants.go` 中未使用的 `MaxResponseTimes` / `MaxErrors` 常量 - 删除整个 `middleware/ratelimit_middleware.go`(功能已由 `ratelimit_instrumented.go` 替代) - 删除 `controller/tts.go` 中 `resolveClientFormat` 的冗余空字符串分支 ### 限流中间件增强 - `middleware/ratelimit.go`:新增 XFF 双模式解析(见 VUL-003),初始化时同步配置到 setting 包。 - 新增 `middleware/ratelimit_test.go`:229 行单元测试,覆盖限流窗口、并发控制、XFF 解析等场景。 ### 配置与忽略文件 - `.gitignore`:新增忽略 `TODO.md`(本地待办清单不入仓库) - `.env.example`:新增环境变量示例 - `.dockerignore`:更新构建上下文排除规则 - `setting/config.go`:新增 `TrustedProxyHops` 等配置项的启动日志展示 --- ## 四、文档 - **README.md**(+147 行):speed/sample_rate 参数修正、监控端点安全说明、公网部署 best practice。 - **VULNERABILITY_REPORT.md**(新增,197 行):完整代码审查交付物,含 9 项漏洞(高 1 / 中 2 / 低 4 / 信息 2)、8 条无风险核查项、工程债务记录、安全加固建议。 --- ## 兼容性说明 - **向后兼容**:所有接口路径、请求/响应格式(除 `/health` 新增 `commit` 字段外)均保持不变。 - **行为变更**: - `/metrics` 中 `UpstreamErrors` 现在会统计 transport/request/wrap/stream 类错误(此前遗漏),监控告警阈值可能需要微调。 - IP 限流的客户端 IP 解析策略变更(从 XFF 头部取第一个值 → 从尾部取第一个公网 IP),多跳反代部署建议配置 `TRUSTED_PROXY_HOPS`。 - **配置新增**:`TRUSTED_PROXY_HOPS` 环境变量(可选,默认启发式模式)。 ---
sun added 12 commits 2026-08-27 10:58:32 +08:00
与 ratelimit_middleware.go 内容完全重复(SHA256 一致,1116 字节),
且无任何代码引用 .tmp 路径,属于误提交的开发期残留文件。
controller.tts.go:contentTypeFor 对 aac/flac 返回 audio/aac/flac,
  但 adapter/volcano/synthesis.go 在上游降级时仅修改 opts.Format,
  finalFormat 仍保留 clientFormat,导致响应头与字节流不符。
  修复:finalFormat 改为反映真实输出格式(非 wav 时取 opts.Format),
  客户端按 AAC/FLAC 解码 MP3 流的失败场景消除。

VUL-004 (高): .env 凭据泄露
  README 引导用户 cp .env.example .env 填密钥,但 .gitignore
  未忽略 .env,任何 git add . 都会把含 BYTEDANCE_TTS_API_KEY
  的文件提交进 git 历史,不可逆。
  修复:
  - .gitignore 新增 Secrets section,拦截 .env 与 .env.* 变体,
    保留 .env.example 作为模板追踪
  - .dockerignore 升级为同名规则模式,覆盖未来 .env.local /
    .env.production 等变体,保证 git 与 docker 两通道一致

详见 VULNERABILITY_REPORT.md
"
- 9 项漏洞(高 1 / 中 2 / 低 4 / 信息 2)
  - 已核查无风险项 8 条
  - 工程债务记录(零测试、死代码、云盘占用)
  - 安全加固建议(Docker 密钥传递、TLS、依赖固定)

供后续按优先级处理备查。
"
VUL-003 (中): X-Forwarded-For 信任链可被伪造 IP 绕过限流

本服务定位为公网入口,即使单人使用,公网暴露意味着攻击面
与公开服务等同,不能"够用就行"。

采用渐进式披露设计,平衡易用性与功能性:

  1. 启发式模式(默认, 不设环境变量 或 TRUSTED_PROXY_HOPS=0)
     - 从 XFF 链尾扫描,跳过私有 IP,返回第一个公网 IP
     - 适合 90% 部署(单跳/多跳/直出),无需了解精确跳数
     - 限制:多跳 CDN 场景下,限流粒度为"按 CDN 边缘 IP"
     - 直出部署:整个 XFF 分支不会执行

  2. 精确模式(TRUSTED_PROXY_HOPS=N, N>0)
     - 从 XFF 链尾倒数第 N+1 个位置取值
     - 精准到真实 client,需按实际反代跳数正确配置
     - N=1:单跳反代;N=2:CDN+反代;以此类推

  3. 两种模式都从链尾扫描
     - XFF 首值是客户端可控的,信任首值等于信任攻击者
     - 链尾由受控的反代添加,天然免疫伪造绕过

  4. 默认值从 1 改为 0(行为变化)
     - 旧默认:精确模式 N=1,取 XFF 末值
     - 新默认:启发式模式,跳过链尾私有 IP
     - 对单跳场景行为相同
     - 对多跳/链尾含私有 IP 场景新版更准确(返回真实公网 IP)

  5. 配套
     - middleware/ratelimit_test.go:24 个表驱动测试用例,
       覆盖直出/单跳/多跳/伪造/畸形/精确 N 边界,全部通过
     - setting/config.go:LogStartupSummary 显示当前 XFF 模式
     - .env.example:重写说明,标注默认行为 + 何时需配
     - README.md:新增"反代拓扑与 X-Forwarded-For 解析"章节
       (何时需要/两种模式/行为对比/为什么从链尾/启动日志验证)

  6. 已知边界:TRUSTED_PROXY_HOPS=00 等被 Atoi 解析为 0 的
     输入归入启发式模式,日志不会出现"精确模式 0 跳"矛盾输出。
TODO.md 是本地待办清单,通过任务看板追踪更合适,不入 git 仓库。
VUL-005 (低): 攻击者可在 HTTP 请求 URL 或上游错误响应中
注入 \n / \r 字符,伪造日志行干扰排障。无代码执行风险。

修复位置:
  - middleware/logger.go: 访问日志中的 r.RequestURI 是未经
    解析的原始请求行,客户端可控。转义 \n / \r 为字面字符
  - adapter/volcano/synthesis.go: 上游非 200 响应体 (rawBody)
    可能是攻击者控制的恶意内容,转义后再嵌入错误消息
VUL-002 (低): transport 层错误不进入 UpstreamErrors 指标
原代码用 if errCode != 0 判断是否记录错误,但合成链路中
多种错误场景的 errCode 本身是 0(火山 v3 业务码非 0 时才
会传入),导致这些场景在 /metrics 上完全不可见:
  - transport_error: client.PostStream 失败(DNS / 连接 / TLS)
  - request_error  : buildRequest 序列化失败
  - wrap_error     : WAV 头拼装失败
  - stream_error (code=0): 读流错误
修复:把判断改为 if status != "ok",status 是上游调用全链
路的权威错误指示器,任何非 ok 状态都计为错误。
codeLabel(0) → "transport" 已有定义,修复后该分类真正生效,
火山接口不可达等网络故障首次在监控上可见。
向后兼容:已统计的 http_XXX 错误(status="http_xxx" != "ok")
行为不变,新增 transport / request / wrap / stream 错误进入指标。
判定:不引入新鉴权机制(会破坏 Prometheus 抓取),文档引导。
  修复:「观测 / Metrics」section 末尾新增「公网部署:监控端点
  无鉴权」段,含三端点风险表 + nginx 反代 basic auth 配置示例。

VUL-007 (低): 未设置 OPENAI_TTS_API_KEY 时鉴权完全关闭
  判定:不改代码(无 API Key 即不鉴权是 README 明示的设计),
  文档强化。
  修复:三处加强:
    1. 环境变量表 OPENAI_TTS_API_KEY 行加 🔴 公网必设 标记
    2. 「API 使用说明」section 顶部 callout 警示
    3. 「部署」section 末尾新增「公网部署安全清单」段,统一
       覆盖鉴权关闭与监控端点保护,形成完整安全姿态
VUL-008 (信息): speed 超范围静默截断无提示
  修复:「API 使用说明」speed 描述从"0.25 ~ 4.0"改为
  "客户端接受范围 0.25 ~ 4.0;火山实际生效范围 0.5 ~ 2.0
  (speech_rate [-50, 100]),超出范围会被静默截断,客户端无感反馈"。
  选 TODO 方案 1(诚实修正文档),不改代码。

VUL-009 (信息): WAV 采样率依赖配置而非上游实际值
  修复:BYTEDANCE_TTS_SAMPLE_RATE 描述加风险提示
  "此值直接写入 WAV 头,需与上游实际 PCM 采样率一致,
  否则音频变速变调"。
  上游 v3 协议不返回 sample_rate 字段,代码侧无法自动取真实值,
  仅文档强化。
VUL-003 修复期间意外发现项目遗留一批死代码,本次一并清掉:

  - middleware/ratelimit_middleware.go(37 行,物理删除)
    文件内 RateLimit / ConcurrencyLimit 函数从 977e9cc 创建后
    从未被引用,370a217 commit 用 ratelimit_instrumented.go
    (带 metrics 埋点 + 路径过滤)取代了它。占用包体,清。

  - middleware/auth.go:InitAPIKeys(6 行)
    注释说"已在 setting.InitAuthConfig 中完成",无 op。

  - middleware/cors.go:InitCORSConfig(6 行)
    同上,setting.InitCORSConfig 已做实际工作。

  - dto/tts.go:ByteDanceTTSConfig 类型(7 行)
    完整的配置走 setting.TTSOptions + adapter/volcano.Options,
    此类型从未被任何代码实例化。

  - setting/config.go: var _ = dto.ByteDanceTTSConfig{} 占位(3 行)
    配合上方类型删除,移除 dto import。

  - controller/tts.go:resolveClientFormat
    合并 if reqFmt == "" 与 default 分支(都返回
    setting.TTSOptions.Format),2 行简化。

  - common/constants.go: MaxResponseTimes / MaxErrors
    定义后从未被任何文件引用。

  - middleware/ratelimit_instrumented.go 顶部注释
    移除对"原 ratelimit_middleware.go"的悬空引用,
    改为描述本文件相对路由使用实现的两个增强点。

影响:
  - 包体减少约 30 行
  - 降低新人接手时的代码理解成本
  - 零功能变更,24 个现有测试用例全过
现状:controller/tts.go:232 硬编码 Version: "2.0.0 (v3 API)",
不会随代码变化而更新,/health 无法反映实际跑的代码。
修复:四文件改动,实现构建时 ldflags 注入。
  1. 新建 version/version.go,声明两个包级变量:
       Version (默认 "dev")
       Commit  (默认 "dev")
  2. dto/health.go:HealthResponse 加 Commit 字段(JSON 输出多一字段)
  3. controller/tts.go:HealthResponse 用 version.Version / version.Commit
     替代硬编码字面量
  4. Dockerfile:加 ARG VERSION=dev ARG COMMIT=dev,
     go build 时通过 -ldflags 注入到 version 包的两个变量
效果:
  - 本地 go build (不传 ldflags) → version=dev commit=dev
  - 开发 build (git describe) → version=v0.2.0-5-g4abcd5 commit=g4abcd5
  - release build (打 tag 后) → version=v0.2.1 commit=<对应 hash>
测试环境(develop 分支,无 tag)显示距离上次 release 几个 commit +
具体 hash;生产环境(main + tag)显示干净 semver。
为 Gitea→GitHub 镜像 + GitHub 镜像仓库场景准备 Docker 镜像
自动构建和发布流程。

触发条件:
  - push tag v*  (release 时打 tag 自动触发)
  - workflow_dispatch  (手动触发,可在 GitHub UI 测试)

构建:
  - 多平台:linux/amd64 + linux/arm64
  - 通过 QEMU + Buildx 跨架构构建
  - 推送到 ghcr.io/<github-user>/volcano-engine-tts-ui
  - 双标签:语义版本 + 短 commit hash
  - OCI labels 包含 version 和 revision

与之前 version 包配合:
  workflow 用 git describe --tags --always --dirty 算 VERSION
  (commit hash 类似),通过 --build-arg 传给 Dockerfile,
  Dockerfile 用 -ldflags 注入到 version.Version / version.Commit
  两个变量。最终 /health 端点返回构建时注入的真实版本信息。

前置条件(用户需自行完成):
  1. GitHub 建镜像仓库
  2. Gitea 配置 Push Mirror 到 GitHub(开启 Sync Tags)
  3. 仓库设为 Public(否则 GHCR 镜像默认 private)
sun marked the pull request as ready for review 2026-08-27 11:00:40 +08:00
sun merged commit 0d3517eb5c into main 2026-08-27 11:01:48 +08:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sun/Volcano-Engine-TTS-UI#2