ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Rook OSD 密钥加密密钥(KEK)轮换机制:设计与实现深度解析

2026/9/23 4:05:00 拓冰建站 浏览量
Rook OSD 密钥加密密钥(KEK)轮换机制:设计与实现深度解析 Rook OSD 密钥加密密钥KEK轮换机制设计与实现深度解析【免费下载链接】rookStorage Orchestration for Kubernetes项目地址: https://gitcode.com/gh_mirrors/roo/rook本指南以 Rook 设计文档 design/ceph/key-encryption-key-rotation.md 为核心骨架结合仓库内 Operator 与 Daemon 层的完整源码实现系统讲解 Rook 如何在无停机的前提下通过 Kubernetes CronJob 周期性轮换加密 OSD 底盘的密钥加密密钥Key Encryption KeyKEK并同步更新 KMS 中的密钥版本。读完本文你将掌握security.keyRotation配置的完整语义、五步安全轮换算法的工作原理以及底层luksAddKey/luksChangeKey/luksKillSlot命令的封装细节与失败边界。背景为什么需要 KEK 轮换Rook 使用 dm-crypt 并借助 cryptsetup 的 LUKS 扩展cryptsetup(8)来加密承载 OSD 的 PVC。加密设备本身使用数据加密密钥DEK而 DEK 则由一把密钥加密密钥KEK保护KEK 通常存放在 Rook 支持的各类密钥管理服务KMS中包括Kubernetes Secrets默认HashiCorp VaultIBM Key ProtectKMIPKey Management Interoperability ProtocolAzure Key Vault源码 pkg/daemon/ceph/osd/kms/kms.go 中同样支持用于 OSD 加密场景从安全角度看长期不更换 KEK 会扩大密钥泄露带来的影响面。Rook 需要能够周期性地轮换 KEK并同时在两个位置原子化地更新一是承载 OSD 的加密设备LUKS 密钥槽二是 KMS 中保存的密钥本身。该功能的目标版本为 release-1.11.1见设计文档 front-matter 中的target-version。目标GoalsRook 能够按计划周期性轮换 KEK同步更新承载 OSD 的加密设备与 KMS全程无停机。非目标Non-Goals暂不支持按需on-demand触发KEK 轮换。当前实现仅基于 CronJob 定时调度执行。整体设计概览整个 KEK 轮换特性由以下四个部分组成它们共同构成了一个从声明式配置到底层设备操作的完整闭环CRD 配置入口CephCluster.spec.security.keyRotation字段enabledschedule对应 API 类型KeyRotationSpec定义于 pkg/apis/ceph.rook.io/v1/types.go。Operator 侧调度器为每个加密的 PVC 后端 OSD 创建命名规律、带亲和性约束的 CronJob实现位于 pkg/operator/ceph/cluster/osd/key_rotation.go。CLI 命令入口CronJob 容器内执行rook key-management rotate-key命令注册于 cmd/rook/secret.go。Daemon 侧轮换逻辑实际执行取旧钥→写槽→生成新钥→更新 KMS→验证→清理旧槽的算法实现在 pkg/daemon/ceph/osd/key_rotation.go底层 LUKS 命令封装在 pkg/daemon/ceph/osd/encryption.go。下面逐一深入每个环节。KEK 轮换 CronJob调度器的设计与实现设计文档明确了 CronJob 的三条关键约束源码 pkg/operator/ceph/cluster/osd/key_rotation.go 对它们逐一落实1. 一个加密 OSD 对应一个 CronJob命名规则为rook-ceph-osd-key-rotation-osdID源码常量keyRotationCronJobAppNameFmt rook-ceph-osd-key-rotation-%d。makeKeyRotationCronJob在 key_rotation.go#L204-L234 中构造 CronJob其中调度表达式schedule取自spec.Security.KeyRotation.Schedule若未配置 schedule默认使用weekly源码注释说明默认值写在代码中而非 CRD 默认值是为了避免 CRD 默认值机制带来的兼容性问题ConcurrencyPolicy设置为ForbidConcurrent保证同一 OSD 的轮换任务不会并发执行避免密钥槽竞争。2. 使用 OSD Pod 亲和性调度到同一节点applyKeyRotationPlacement在 key_rotation.go#L48-L65 中清空TopologySpreadConstraints与PodAntiAffinity并设置RequiredDuringSchedulingIgnoredDuringExecution级别的PodAffinity以 OSD 的标签approok-ceph-osd、osd-idID等为LabelSelector以主机名kubernetes.io/hostname为TopologyKey。这样 CronJob 的 Pod 会被强制调度到目标 OSD 所在的宿主机——因为轮换必须操作宿主机上的加密设备。对应的单元测试 pkg/operator/ceph/cluster/osd/key_rotation_test.go 验证了该亲和性在 Affinity 为 nil 或已有反亲和性时的覆盖行为。3. 与 OSD 共享宿主机设备访问能力CronJob Pod 挂载了三类卷见getKeyRotationPodTemplateSpeckey_rotation.go#L112-L201devices宿主机的/dev目录用于访问加密块设备udev宿主机的/run/udev目录用于与宿主机的 udev 守护进程交互——源码注释明确指出cryptsetup synchronizes with udev on host through semaphore因此同时设置了HostIPC: truebridge宿主机上的 bridge 目录dataDirHostPath/namespace/pvcName/ceph-osdID内部包含 Rook 为加密设备在/var/lib/ceph/osd/下建立的映射入口blockType-tmp。轮换任务处理的设备列表依据 OSD 拓扑动态生成block 设备bluestore_block必然包含若配置了 metadata PVC 与 wal PVC则追加bluestore_metadata与bluestore_wal设备key_rotation.go#L139-L145。此外如果 KMS 为 Vault还会额外挂载 Vault 的 TLS 证书卷。CronJob 的容器以特权模式privileged: true、root 用户运行使用 Rook Operator 镜像并携带 KMS 配置环境变量kms.ConfigToEnvVar、ROOK_CEPH_VERSION以及集群配置环境变量命令行参数为key-management rotate-key pvcClaimName device1 [device2] [device3]。协调Reconcile流程reconcileKeyRotationCronJobkey_rotation.go#L237-L303是调度器的心脏未启用通过标签选择器approok-ceph-osd-key-rotation执行DeleteCollection清理所有已存在的轮换 CronJob忽略 NotFound 错误已启用列出带approok-ceph-osd与osd-over-pvc标签的 OSD Deployment逐个解析 OSD 信息与 PVC 属性跳过未开启加密!osdProps.encrypted的 OSD随后通过SetOwnerReference将 CronJob 归属于对应 OSD Deployment保证 OSD 删除时 CronJob 被级联回收最后以CreateOrUpdateCronJob幂等地创建或更新 CronJob。KMS 侧更新能力UpdateSecret设计文档要求为每种 KMS 类型补充KMS.UpdateSecret()用于将新的 KEK 写入 KMS。其统一接口实现在 pkg/daemon/ceph/osd/kms/kms.go#L233-L266Kubernetes Secrets直接更新同名 Secret 的值updateSecretInKubernetesHashiCorp Vault按GenerateOSDEncryptionSecretName规范化的名称通过 Vault 的PutSecret覆盖写入Vault 原生保留版本历史IBM Key Protect / KMIP / Azure Key Vault当前返回错误update secret is not supported for the %q KMS。也就是说截至当前仓库实现KEK 轮换的 KMS 更新能力仅对 Kubernetes Secrets 与 Vault 可用IBM Key Protect 与 KMIP 在创建PutSecret和查询GetSecret上已有实现kms.go#L82-L231但轮换场景下的原地更新尚未支持。这是使用该功能前必须确认的约束。KMS 提供方由KMS_PROVIDER连接参数决定kms.go#L56-L80为空时默认使用 Kubernetes Secrets。五步安全轮换算法KMS 与 LUKS 槽位的状态机设计文档给出了 KEK 轮换的核心状态机以 K1KMS 中的当前 KEK与 K2待加入的新 KEK为两个密钥版本通过 LUKS 的槽位 0 与槽位 1 做缓冲保证任何一步中断都不会让设备打不开Step操作LUKS Slot 0LUKS Slot 1KMS 中的 Key1获取 K1K1K12将 K1 加入 slot 1K1K1K13生成 K2 并加入 slot 0K2K1K14将 K2 更新到 KMSK2K1K25从 slot 1 移除 K1K2K2设计文档特别强调上述步骤保证了即使操作在任意一步被打断KMS 中的 KEK 也始终能够打开加密设备所有中断引发的边界情况都被覆盖。源码层面的对应实现Daemon 侧入口RotateKeyEncryptionKeypkg/daemon/ceph/osd/key_rotation.go#L31-L102与状态机逐条对应获取 K1kms.GetSecret(secretName)若 Secret 为空直接报错拒绝轮换K1 写入 slot 1对每个设备执行addEncryptionKey(device, currentKey, currentKey, slot1)——用 K1 作为口令、K1 作为新口令写入槽位 1生成 K2 并写入 slot 0先调用oposd.GenerateDmCryptKey()生成新密钥pkg/operator/ceph/cluster/osd/config.go#L61-L68基于随机字节的 base64 编码随后对每个设备先removeEncryptionKeySlot(device, currentKey, slot0)清理可能残留的槽位 0再用addEncryptionKey(device, currentKey, newKey, slot0)将 K2 写入槽位 0KMS 更新与校验kms.UpdateSecret(secretName, newKey)后将新值写回 KMS随后kms.GetSecret回读并严格比对——不一致则整体报错failed to verify the new key in the KMS在验证通过之前绝不进入清理旧密钥的阶段移除旧钥最后对每个设备执行removeEncryptionKeySlot(device, newKey, slot1)用新钥 K2 作为口令清除槽位 1 中的旧钥 K1完成轮换。注意第 5 步使用 K2 作为luksKillSlot的认证口令——因为此时设备已被 K2 接管只有持有 K2 才能合法清理旧槽位。底层 LUKS 命令封装幂等性与失败容忍轮换逻辑依赖的三个 cryptsetup 命令封装位于 pkg/daemon/ceph/osd/encryption.go它们体现了极强的幂等设计luksAddKeyaddEncryptionKeyencryption.go#L234-L283通过临时口令文件传递--key-file与--key-slot若槽位为空则直接添加成功若报错Key slot N is full则调用ensureEncryptionKey内部使用luksChangeKey探测目标槽位中的口令是否恰好等于待写入的新口令相同 → 幂等返回无需任何改动不同 → 先用旧口令luksKillSlot清空该槽位再递归调用luksAddKey写入新口令。luksChangeKeyensureEncryptionKeyencryption.go#L198-L227用于探测目标槽位中是否已存在指定口令若错误信息包含No key available with this passphrase返回false表示不匹配而不视为致命错误。这正是重试/续跑场景下的关键容错CronJob 重跑时如果槽位状态已经符合预期不会重复报错。luksKillSlotremoveEncryptionKeySlotencryption.go#L170-L193删除指定槽位如果错误信息包含Keyslot N is not active槽位本就不存在同样忽略错误——保证轮换流程可安全重入。这三层幂等封装与每步均可中断恢复的设计目标互为印证即便某个步骤失败导致 CronJob 在下个周期重试addEncryptionKey/removeEncryptionKeySlot也能基于当前真实槽位状态继续推进而不会破坏设备可用性。对应测试见 pkg/daemon/ceph/osd/encryption_test.go。CephCluster CR 配置开启 KEK 轮换设计文档给出了新增的security.keyRotation配置段对应源码中的KeyRotationSpecpkg/apis/ceph.rook.io/v1/types.go#L530-L539包含两个字段字段类型默认值说明enabledboolfalsekubebuilder:defaultfalse是否启用 KEK 轮换schedulestringweekly代码内默认见 key_rotation.go#L210-L214轮换调度的 cron 表达式在CephCluster中与既有 KMS 配置组合使用的示例apiVersion: ceph.rook.io/v1 kind: CephCluster metadata: name: rook-ceph namespace: rook-ceph spec: security: kms: connectionDetails: KMS_PROVIDER: vault # 或 ibm-kp / kmip / 空默认 Kubernetes Secrets # ... 其他 KMS 连接参数见 # Documentation/Storage-Configuration/Advanced/key-management-system.md tokenSecretName: vault-token keyRotation: enabled: true # 设计文档示例中的字符串形式实际字段为布尔值 schedule: weekly几点说明schedule使用标准 cron 格式也支持 Kubernetes CronJob 的快捷别名如weekly、daily轮换只对加密且由 PVC 承载的 OSD 生效reconcileKeyRotationCronJob中通过osdProps.encrypted过滤非 PVC 或未启用加密的 OSD 不会创建 CronJobKMS 的完整配置方式Vault token、IBM Key Protect 凭据、KMIP 连接等可参考 key-management-system.md 与 ceph-cluster-crd.md。如何验证轮换是否生效确认 CronJob 已创建kubectl -n rook-ceph get cronjob应看到形如rook-ceph-osd-key-rotation-0、rook-ceph-osd-key-rotation-1的任务每个加密 OSD 一个确认调度节点正确kubectl -n rook-ceph get pod -l approok-ceph-osd-key-rotation -o widePod 所在节点应与对应 OSD 一致查看轮换日志kubectl -n rook-ceph logs job/cronjob 触发的 job应依次出现fetching the current key、adding the current key to slot 1、generating new key、updating the new key in the KMS、Successfully rotated the key等日志关闭轮换将security.keyRotation.enabled置为false后协调器会删除全部轮换 CronJob。边界情况、限制与安全说明基于设计文档与源码实现使用该功能时需要明确以下几点中断安全性五步状态机保证任意步骤中断后KMS 中的 KEK 仍能打开设备每一步设备内至少保留一个可用密钥CronJob 的ForbidConcurrent策略与底层命令的幂等设计共同防止并发与重复执行造成破坏。KMS 更新能力差异当前UpdateSecret仅对 Kubernetes Secrets 与 Vault 实现IBM Key Protect、KMIP、Azure Key Vault 的轮换更新会直接报错。设计文档中的方案表述为需要为每种 KMS 类型增加支持即该能力仍在演进中启用前务必确认你的 KMS 类型受支持。不支持按需轮换非目标明确排除了按需触发临时需要轮换只能调整 schedule 或等待下个周期CronJob 也可手动触发但这不是受支持的正式接口。特权容器轮换 Pod 以 privileged root 运行并挂载宿主机/dev这是操作 dm-crypt 设备所必需的安全边界与 OSD provision 容器一致。默认关闭enabled默认false不会对未显式开启的集群产生额外负载。总结KEK 轮换是 Rook 加密存储安全能力的重要一环通过security.keyRotation声明式配置、按 OSD 粒度生成的 CronJob 调度、LUKS 双槽位缓冲的五步状态机以及 KMS 的密钥版本更新Rook 在无需重启 OSD、无停机的前提下实现了加密密钥的周期性更换。设计文档key-encryption-key-rotation.md奠定了方案骨架而 Operator 与 Daemon 层的源码则把任意步骤可中断、任意状态可重入的工程细节落到了实处。若要进一步深入可依次阅读 pkg/operator/ceph/cluster/osd/key_rotation.go、pkg/daemon/ceph/osd/key_rotation.go、pkg/daemon/ceph/osd/kms/kms.go 及 pkg/daemon/ceph/osd/encryption.go 四份核心文件。【免费下载链接】rookStorage Orchestration for Kubernetes项目地址: https://gitcode.com/gh_mirrors/roo/rook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考