【免费下载链接】VaultS3
Lightweight, S3-compatible object storage server with built-in web dashboard. Single binary, low memory, encryption at rest.
VaultS3 是一款轻量级、S3 兼容的开源对象存储服务器,内置 Web 管理仪表盘。它的按桶加密(Per-Bucket Encryption)采用 KEK/DEK 信封加密:每个桶拥有独立的数据加密密钥(DEK),由主密钥(KEK)包装保护,支持按需开启、密钥轮换(Key Rotation)以及一键 Crypto-Shredding 彻底销毁密钥,非常适合"一桶一租户"的多租户部署场景。本文带你用 5 分钟吃透这套设计。
为什么需要按桶加密密钥?
传统做法下,整个服务器共享同一个静态加密密钥,这带来三个痛点:
- 🔒无法隔离:任何租户的密钥泄露,可能波及所有桶的数据;
- 🚫全有或全无:加密要么对整台服务器开启,要么完全不开,无法按桶选择;
- 🧹退租成本高:删除一个租户的数据,只能逐个对象擦除,耗时且昂贵。
VaultS3 的解法:给每个桶发一把专属钥匙。这样:
- 租户 A 加密自己的桶,租户 B 可以保持明文,互不干扰;
- 一把桶密钥泄露,只影响该桶;
- 租户离场 = 删除一把密钥(Crypto-Shredding),数据即刻永久不可恢复,无需批量擦除对象。
密钥层级:KEK 如何保护 DEK?
信封加密(Envelope Encryption)的核心是"钥匙装进信封,信封再上锁",VaultS3 采用两层密钥结构:
主密钥 KEK(来自配置或 KMS,绝不落盘明文) │ 包装 ▼ 每桶 DEK(256 位随机数,按版本管理,只以密文形式存储) │ 加密 ▼ 对象数据(AES-256-GCM,每个对象独立随机 nonce)| 角色 | 职责 | 存储方式 |
|---|---|---|
| KEK(密钥加密密钥) | 只用来包装/解包 DEK,从不接触对象数据 | 配置文件或 KMS,应用从不写入磁盘 |
| DEK(数据加密密钥) | 实际加密对象内容 | 仅以 KEK 加密后的密文存于桶的加密配置中 |
| 对象数据 | 被 DEK 加密 | AES-256-GCM,每对象全新随机 nonce |
几个关键安全细节:
- 明文 DEK 只活在内存里:每个桶的 DEK 解包后放入内存缓存,每个进程生命周期内只解包一次,读取对象时零额外开销;
- 桶间隔离是密码学级别的:用桶 B 的密钥去解桶 A 的对象,GCM 认证会直接失败——隔离不靠访问控制,而靠数学;
- 错误 KEK = 全盘不可读:KEK 错了,DEK 解包失败,任何数据都拿不到。
核心实现位于 internal/bucketcrypto/bucketcrypto.go,完整的架构设计可见 docs/design/per-bucket-encryption.md。
对象在磁盘上长什么样?
加密对象是自描述的:文件头包含魔数、格式版本和 DEK 版本号,读取时据此选择正确的密钥版本;没有魔数的文件则按明文或旧版全局密钥对象处理,天然向后兼容。
从 4.4.53 版本起,对象采用1 MiB 分块(chunked)加密格式(魔数VS3S):每个块是独立的 AES-256-GCM 消息,块的 nonce 由"随机前缀 + 块序号 + 末块标志"派生,带来三个好处:
- ✂️防篡改防错位:块无法被重排或从别的对象移植,截断的对象读取会直接失败;
- 📦内存友好:读取大对象只需加载一块,而不是整个对象(解决了大对象并发读导致 OOM 的 issue #49);
- ⏩Range 读友好:按块认证解密,客户端永远拿不到未校验的明文。
旧格式(VS3X,整对象单条 GCM 消息)仍然可以正常读取,重写一次对象即完成格式迁移。
如何开启、轮换与销毁?
开启加密:按桶选择,明暗共存
在配置中开启per_bucket模式(详见 docs/CONFIGURATION.md):
encryption: enabled: true key: "" # 64 位十六进制主密钥,即 KEK per_bucket: true开启后每个桶默认仍以明文存储,直到显式"选入"加密。可以通过 S3 APIPUT /{bucket}?encryption(SSEAlgorithm: AES256)或 Web 仪表盘的桶配置面板开启——仪表盘上会显示当前密钥版本,并提供"启用加密 / 轮换密钥 / 销毁密钥"操作。
⚠️ 注意:
per_bucket模式只接受AES256算法;若请求aws:kms,服务器会直接拒绝而不是静默忽略。想走 KMS 路线请改用encryption.kms配置并关闭per_bucket。
密钥轮换:旧数据不重写也能读
轮换(Rotate)生成 DEK 版本 N+1 并设为当前版本,旧版本密钥全部保留:
- 新写入使用最新版本 DEK;
- 历史对象头部自带版本号,仍用旧密钥正常解密;
- 历史数据按需重加密:下次覆写时自然迁移,或由后台重加密任务批量处理(支持 dry-run 预览,见 internal/api/reencrypt.go)。
这意味着轮换不需要停机、不需要全量重写,是平滑的安全运维操作。
Crypto-Shredding:一次删除,永久擦除
Crypto-Shredding(加密销毁)是这套设计最有价值的"杀手锏":删除桶的所有 DEK 密文并驱逐内存缓存后,密文永久不可恢复——KEK 也救不了,因为被毁的是 DEK 本身。
它是租户退场、数据"被遗忘权"(GDPR 场景)的理想原语:毫秒级完成、结果确定、无残留。在 Web 控制台中该操作需要二次确认("不可逆,所有对象都将无法恢复"),实现见 web/src/components/EncryptionPanel.tsx。
集群与向后兼容:几个容易忽略的细节
这套设计在分布式和兼容性上做了不少"兜底"处理,值得了解:
- 加密配置随元数据复制:包装后的 DEK 存入桶的加密配置,通过 Raft 命令(
cmdPutEncryptionConfig)在集群内同步; - EncryptionPending 防"静默明文":开启加密是两条 Raft 日志(先算法、后密钥),某节点可能只同步到前半截。
EncryptionPending机制区分"该桶本就选择明文"和"本节点密钥还没同步到",后者会拒绝写入而不是偷偷落明文——详见 internal/bucketkeys/bucketkeys.go; - 向后兼容零迁移:旧的全局加密对象(legacy key)和明文对象保持可读;选入加密只影响之后的新写入,无强制迁移;
- KEK 来源可插拔:配置主密钥(最简单)、HashiCorp Vault KMS(主密钥不落配置文件)、未来可扩展云 KMS。
小结:这套设计解决了什么?
| 能力 | VaultS3 方案 |
|---|---|
| 租户隔离 | 每桶独立 DEK,桶间密码学隔离 |
| 灵活选入/退出 | 按桶 opt-in,明文与加密桶共存 |
| 密钥轮换 | 版本化 DEK + 惰性重加密,不重写历史 |
| 快速擦除 | Crypto-Shredding 一次删除永久失效 |
| 性能 | 分块 AES-256-GCM 流式读写,DEK 内存缓存摊销 |
| 兼容性 | 旧格式/明文/legacy 全局密钥均可读 |
如果你想深入实现细节,推荐按以下路径阅读:设计文档 docs/design/per-bucket-encryption.md → 加密核心 internal/bucketcrypto/ → 元数据接入 internal/bucketkeys/bucketkeys.go → 磁盘格式与配置 docs/CONFIGURATION.md。
【免费下载链接】VaultS3
Lightweight, S3-compatible object storage server with built-in web dashboard. Single binary, low memory, encryption at rest.
相关推荐
Rook OSD 密钥加密密钥(KEK)轮换机制:设计与实现深度解析
Rook OSD 密钥加密密钥(KEK)轮换机制:设计与实现深度解析 本指南以 Rook 设计文档 design/ceph/key encryption key
云原生存储容器编排运维SiYuan 加密笔记本全解析:从 KEK/DEK 密钥架构到孤岛式隔离的完整实现指南
SiYuan 加密笔记本全解析:从 KEK/DEK 密钥架构到孤岛式隔离的完整实现指南 导读 本文以 SiYuan 官方设计文档 docs/ENCRYPTED
知识管理知识库Tink Java 信封加密(Envelope Encryption)实战:基于 Cloud KMS 的 DEK/KEK 示例全解析
Tink Java 信封加密(Envelope Encryption)实战:基于 Cloud KMS 的 DEK/KEK 示例全解析 导读 本文围绕 Tink(
密码学
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考