桶开了 SSE-C 或 SSE-KMS,服务端拷贝时密钥到底走哪条路?

本文深入剖析RustFS中SSE-C与SSE-KMS在服务端拷贝(CopyObject)及跨集群复制场景下的密钥流转机制。重点说明SSE-C拷贝必须携带三组客户密钥头、源/目标密钥分离原则、KMS上下文绑定对象路径的强制校验、加密对象ETag审计跳过策略,以及1.0.2版本关键修复点。强调密钥不落盘、HTTPS传输、KMS可达性验证等运维边界,确保加密合规与数据一致性。

封面

桶一旦上了服务端加密,上传反而不是最让人心里没底的动作,服务端拷贝(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 拷贝执行链

想换个密钥重加密,源和目标各管各的

如果目标要换成一把不同的 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 不可用的情况下,任何依赖它的加密写入都会失败,这是设计上的硬约束,不是偶发报错。

落地动作清单

  1. SSE-C 拷贝用 rc object copy 带全三个客户密钥头,源与目标用同一把密钥;要换密钥重加密时,源和目标密钥头各写各的。
  2. 加密拷贝与复制一律走 HTTPS,确认 KMS 后端可达再上生产。
  3. 站点复制加密桶前,核对 #8300 / #8580 所在版本已包含,避免密钥路径错位与加密单段对象复制卡在 FAILED。
  4. 密钥材料进密钥管理器,命令行用完即 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。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值