跳转到主要内容

副本元数据归一化:可信复制不得重新注入什么

本文记录可信复制接收端如何为副本恢复元数据的修复,合入 Server main 为 PR #194(修复 4fcdf37ce,合并为 9f3037e941)。

截至 2026-09-16: 修复在核验过的 main 40220bd836cb 上;不在已发布的 Server 20260903 中。
来源: 生产逻辑采纳 Mikhail Khadarenka 的 PR #187;合并后的改动保留其作者身份,并把范围收敛到经评审验证的边界。
证据类别: 针对真实单盘与 16 盘纠删后端的 64 叶 HTTP 级基线(修复前 44 对照通过 / 20 缺陷失败),以及同一套测试对基线 helper 的反事实重放。没有客户事故被归因。

哪里错了

普通 PUT 路径会对元数据归一化:从 Content-Encoding 中剥掉仅传输用的 aws-chunked token,并删除某项 GHSA 缓解刻意移除的 X-Amz-Meta-X-Amz-Unencrypted-Content-Length/-Md5 用户元数据键。而可信复制 接收端恢复副本元数据时,却以"允许复制"的开关重新运行同一个宽松提取器——把 原始请求的全部受支持头与用户元数据重放一遍。具体表现为,可信副本写入时服务器可能存储、并在之后的 GET/HEAD 返回:

  • Content-Encoding: aws-chunked(纯传输编码,按 AWS SigV4 streaming 规则绝不能存储),或未拆分的 aws-chunked,gzip 整串(应为 gzip);
  • 两个 GHSA 脱敏用户元数据键——对该缓解的部分回退,仅限可信副本写入;
  • 无自身 PAX 头的 Snowball 条目:外层归档的 content-type、cache-control 与用户元数据。

对象字节本身不一定受损;是存储的元数据错了。该回归由 56fa63bfd (2026-04-15,复制头信任边界加固,CVE-2026-34204)引入——其信任保护本身是对的,予以保留。

修复

只改一个文件(cmd/handler-utils.go)。删除布尔双模式 helper:

  • 普通提取器无条件跳过复制专用键;
  • 新的副本提取器只遍历复制到内部的头映射,恢复六个复制域字段:SSE-C 密封密钥材料、密封算法、IV、加密 multipart 标记(空标记按键存在性生效)、实际对象大小,以及 SSE-C checksum 恒等映射;
  • 绝不重新读取普通受支持头或用户元数据。

修复后期望的存储编码:

请求编码 存储的 Content-Encoding
aws-chunked
aws-chunked,gzip gzip
gzip gzip

aws-chunked, gzip(注意空格)仍存储带前导空格的 gzipgzip, aws-chunked 仍存储整串。这些是记录在案的现状,由测试按现状断言——不是修复声明。

运维可见变化

  • 无 PAX 头的 Snowball 条目的可信副本不再继承外层归档的普通元数据。六个复制域字段仍作用于已授权条目。这与普通(非信任)Snowball 行为一致,且仓库内没有生产代码发送 auto-extract 标记,没有内部依赖旧继承行为。
  • GHSA 脱敏键不再在副本恢复时被写回——与每次普通 PUT 的行为一致。
  • 认证、权限门控与复制信任语义不变;普通提取路径逐字节等价。
  • 回滚代码会重新打开注入路径,但不会修复已存储的元数据。

升级不会修复存量对象

升级阻止新的污染;不扫描、不改写既有对象。两个后果值得注意:

  • 权威来源仍被污染的对象在升级后会被判为不一致,并在 heal/resync 中被反复选中做元数据复制。先修复权威来源,再让副本收敛。
  • 普通 S3 自 COPY 不是通用的修复 API:它会创建新版本或移动时间戳,而不是原位改写单个版本的元数据。

存量元数据修复提案——状态

一个未来操作的设计已经存在:构建清单(包含非当前版本,不能只查最新);通过与可信来源版本或独立校验值比对来核验——绝不凭错误的响应头猜测,也绝不因为标签写着 gzip 就重新解压;先处理权威来源的精确版本,再收敛副本;保留不可变清单与元数据备份;小批量验证并演练过回滚。对没有受支持路径的对象,停下来不动它——直接编辑 xl.meta 不是受支持操作。

所选操作必须保护需要保留的版本身份与当前版本关系、Object Lock 保留期与 legal hold、标签、复制状态和加密上下文;写入前检查并发变更,受阻或无法核验的版本保持不动。先在本地克隆中证明具体操作与回滚可行,才能把提案变成可执行 runbook。

这是一个等待单独批准的设计提案,不是已执行的程序。 作为其一部分,没有进行任何生产清单扫描、对象写入、版本调整或部署。把它当作未来 runbook 的形状,而不是已验证的 runbook。

已知限制

  • POST 表单上传路径(bucket-handlers.go)直接调用低层提取器,从不归一化编码;该行为不变,已另立 issue。
  • 本地验证在测试专用的容量适配(宿主盘满)下运行;R4–R8 集成记录中的合并树复跑覆盖了未改动树的情形。
  • 不声明双站点调度、重启或网络故障验收。

验证

回归测试(TestExtractReplicationMetadata*TestAPIReplicaContentEncodingTestAPISnowballReplicaContentEncoding,外加含 race 的信任/SSE-C 回环)覆盖映射表、六个恢复字段与普通路径等价性;反事实运行(同一套测试对基线 helper)复现 20 个缺陷失败,证明测试确有咬合力。升级摘要见组件版本矩阵;姊妹修复见复制标签排序