
桶一旦上了服务端加密,上传反而不是最让人心里没底的动作,服务端拷贝(CopyObject)和跨集群复制才是。上传时密钥是你亲手递的,你知道它在哪;可拷贝和复制是存储内部搬数据,密钥要在节点之间流动,漏一点就可能变成合规事故。RustFS 1.0.2 把这几条路径又收紧了一遍,搞清楚它们怎么走,比盲目"先备份再试"要稳妥得多。
SSE-C 拷贝:三个客户密钥头必须随 CopyObject 走
SSE-C 是客户自管密钥的加密方式:你每次请求都带上一个 AES-256 的密钥,RustFS 用它加解密,但明文密钥不落盘。根据 SSE-C 文档,RustFS 用 rc 的签名自定义头把三个 S3 头送进去:算法 AES256、Base64 编码的 32 字节密钥、以及它的 MD5。一次加密上传长这样:
SSE_C_KEY_HEX=$(openssl rand -hex 32)
SSE_C_KEY_B64=$(printf '%s' "$SSE_C_KEY_HEX" | xxd -r -p | openssl base64 -A)
SSE_C_KEY_MD5=$(printf '%s' "$SSE_C_KEY_HEX" | xxd -r -p | openssl dgst -md5 -binary | openssl base64 -A)
rc object copy /path/to/hello.txt rustfs/my-bucket/hello.txt \
-H "x-amz-server-side-encryption-customer-algorithm:AES256" \
-H "x-amz-server-side-encryption-customer-key:$SSE_C_KEY_B64" \
-H "x-amz-server-side-encryption-customer-key-md5:$SSE_C_KEY_MD5"
服务端拷贝时,这三个头同样要跟着走。源对象是用哪把密钥写的,拷贝请求就得带同一把,否则源端校验不过,拷贝直接失败。1.0.2 的 #8296 把这组 copy-source 密钥头纳入了 TLS 守卫范围:修复前 carries_ssec_headers 只认对象自身的客户密钥头,从 SSE-C 源读数据的 CopyObject / UploadPartCopy 恰好只带 copy-source 那组头,漏在守护之外;修复后开启 RUSTFS_SSE_C_REQUIRE_TLS 的部署里,这类明文请求会直接 400 拒绝,默认配置下也会被计数记录进 rustfs_ssec_plaintext_requests_total。至于拷贝内部是"先解密再按目标桶的加密设置重新加密写下去"还是别的实现,官方文档没有写明,属按 S3 语义做的推断;但无论内部怎么走,源密钥必带是确定的。

想换个密钥重加密,源和目标各管各的
如果目标要换成一把不同的 SSE-C 密钥,规则是源和目标分开指定:源对象的解密密钥走 x-amz-copy-source-server-side-encryption-customer-* 这组头,目标对象的加密密钥走 x-amz-server-side-encryption-customer-* 这组头。两者不能混,混了就会校验失败。这种"读旧密钥、写新密钥"的拷贝是合规里常见的再加密动作,比如密钥轮换后希望新写入的对象都用新主密钥派生出的客户密钥,历史对象则要逐对象重加密。做这类操作时,源和目标密钥都由你掌握,RustFS 不替你存任何一把明文密钥,丢了哪一把,对应对象就永久读不出来。
KMS 上下文:桶级密钥不能套到逐对象上
SSE-KMS 把每对象的数据密钥交给 KMS 包装,加密上下文里有一条记录对象位置的 {bucket: bucket/key} 条目。问题在于客户端如果传入一条 key 等于桶名的 context 条目,它会在写入和每次读取时顶掉这条位置绑定,数据密钥不再和对象位置挂钩。1.0.2 的 #8296 从写入入口把这种 context 直接 400 InvalidArgument 拒绝,错配进不来;修复前已存的对象不受影响,照常可读,读取侧没有改动。一个硬前提始终成立:KMS 后端必须真的可达,否则桶默认加密设置写进去了,之后加密对象写入会直接失败,上线前先验证一次加密读写最稳。
跨集群复制:S3 key 路径要保住,ETag 审计要跳过
站点复制(site replication)把对象从一个 RustFS 部署同步到另一个。加密对象在复制链路里最容易出现两种怪象:一是复制时 S3 key 路径错位,导致目标端拿到的密钥上下文不对;二是复制引擎对加密源副本做 ETag 一致性审计时,把源端存的 HMAC 指纹和目标端算的 content-MD5 当成可比的 MD5,比出了不匹配。#8300 保留了站点复制的 S3 key 路径,让复制出的对象密钥上下文保持正确;#8580 跳过对加密源副本的 ETag 审计:没有这个修复,SSE-S3 / SSE-KMS 的单段对象复制到 AWS S3 或任何返回 content-MD5 ETag 的目标时,每一个这样的对象都会卡在 FAILED 状态,MRF 和重同步无限重试。这两处改动对跑加密桶复制的集群尤其关键,撞上任何一个,后果都是整批对象卡在 FAILED 无限重试,真正的数据偏移也会被这类告警淹没。

几条保命的边界
SSE-C 的明文密钥 RustFS 不落盘,密钥丢了对象就永久读不出来,所以密钥必须进密钥管理器,不能落日志、不能进版本库。命令行 -H 会把展开后的密钥写进进程参数,主机上其他进程可能看到,生产自动化优先用能在受保护内存里接收密钥的 S3 SDK,别走命令行参数。操作完用 unset 清掉 shell 里的密钥变量。改桶的 SSE-S3 或 SSE-KMS 默认设置不会改变已有的 SSE-C 对象,SSE-C 由每次请求的客户密钥头决定,只作用于那一次请求。KMS 不可用的情况下,任何依赖它的加密写入都会失败,这是设计上的硬约束,不是偶发报错。
落地动作清单
- SSE-C 拷贝用
rc object copy带全三个客户密钥头,源与目标用同一把密钥;要换密钥重加密时,源和目标密钥头各写各的。 - 加密拷贝与复制一律走 HTTPS,确认 KMS 后端可达再上生产。
- 站点复制加密桶前,核对
#8300/#8580所在版本已包含,避免密钥路径错位与加密单段对象复制卡在 FAILED。 - 密钥材料进密钥管理器,命令行用完即
unset,生产改用 S3 SDK 传密钥。
一个直接的验证动作:升级后用 rc --version 确认二进制版本,挑一个非关键桶做一次 rc object copy 带 SSE-C 头,确认拷贝成功且用源密钥读回字节一致;再开一次站点复制,观察目标端对象密钥上下文与 ETag 告警是否如预期。把这几步放进加密桶的运维 runbook,比单纯看版本号更踏实。
官方入口:服务端加密文档 docs.rustfs.com/zh/security-compliance/encryption/sse-c,1.0.2 发布说明在 GitHub Releases,源码在 GitHub。
本文深入剖析RustFS中SSE-C与SSE-KMS在服务端拷贝(CopyObject)及跨集群复制场景下的密钥流转机制。重点说明SSE-C拷贝必须携带三组客户密钥头、源/目标密钥分离原则、KMS上下文绑定对象路径的强制校验、加密对象ETag审计跳过策略,以及1.0.2版本关键修复点。强调密钥不落盘、HTTPS传输、KMS可达性验证等运维边界,确保加密合规与数据一致性。
386

被折叠的 条评论
为什么被折叠?



