werwerwer
DR 容灾etcd 加密:确定性密钥派生

确定性密钥派生协议

确定性密钥派生协议的技术细节

确定性密钥派生协议

面向需要理解 etcd-encryption-manager deterministic 模式内部约束、CRD 字段、与 etcd-sync 之间契约的实现方与 Agent。

设计原则

原则含义
不同步密钥本身etcd-sync 只同步派生输入(SeedBundle),密钥由 Active 与 Standby 各自派生
输入完全一致 → 输出完全一致HKDF 是确定性算法;只要 root secret + SeedBundle + 字段集合一致,输出 key 一定一致
角色由 marker 自动判定runtime 是 Active 还是 Standby 由 kube-public/etcd-sync ConfigMap 决定,不由人工或 chart 配置
角色翻转触发重启进程启动时拍下角色,运行中检测到翻转持续 60 秒就主动退出,由控制器重启重新初始化

派生算法

输入

输入长度 / 类型来源
rootSecret32 字节原始 byteskube-system/<rootSecretRef.name> 中 root-key 数据键
seed32 字节,hex 编码字符串EtcdKeySeedBundle.spec.seed
replicationGroupID字符串EtcdEncryptionConfig.spec.deterministic.replicationGroupID,与 SeedBundle 中字段一致
dimension字符串,默认 default同上
revisionuint64EtcdKeySeedBundle.spec.revision
derivationAlgorithmhkdf-sha512-v1(默认)或 hkdf-sha256-v1同 SeedBundle

算法

  1. 选择 hash:默认 SHA-512;当 derivationAlgorithm=hkdf-sha256-v1 时改为 SHA-256
  2. 拼接 salt 输入:encodeCtx("etcd-seed-salt/v1", replicationGroupID, dimension, revision, seed),hash 之得到 salt
  3. 拼接 info 输入:encodeCtx("etcd-encryption-key/v1", replicationGroupID, dimension, revision)
  4. 调用 HKDF(hash, rootSecret, salt, info, 32) 派生 32 字节密钥
  5. 输出 base64 编码字符串

encodeCtx 把每个字段编码为 [uint16(len)][bytes],再按顺序拼接,确保字段边界不可被歧义解析。

派生流水线(输入 / 中间态 / 输出):

输出

项目取值
长度32 字节(256 bit AES-GCM key)
编码base64 标准编码字符串
落地位置kube-system/etcd-encryption-key-<revision>,数据键 key

CRD 与作用域

CRDshortName作用是否参与 DR 同步
EtcdKeySeedBundleeksb共享种子 + 派生算法 + revision,spec 不可变是,Active 写入,Standby 只读消费
EtcdKeySeedBundleState—单集群派生执行状态、错误信息否,每集群本地
EtcdEncryptionConfigeec加密类型、rotation、key 策略、deterministic 子配置否,由各自 chart 渲染、保持值一致

EtcdKeySeedBundle.spec 设置了 CEL 校验 self == oldSelf,禁止任何字段修改,避免同步过程中出现"先派生再改 spec"的不一致。

Runtime 角色判定

判定规则:

信号角色
kube-public/etcd-sync ConfigMap 存在Standby
kube-public/etcd-sync ConfigMap 不存在Active
启动时拍下角色,运行中翻转持续 60 秒主动 os.Exit,要求被外部重启

注意:runtime 监控不参与 leader election;每个副本都自己监控自己的角色,目的是确保没有任何老进程带着过期的角色上下文继续工作。

监控副本的检查时序:

这个 marker 与 active-standby-controller-coordination/controller-integration-handbook.md 中 drsyncgate.IsRunning 看的是同一个 ConfigMap。

etcd-sync 规则边界

类别路径规则 data key备注
同步/registry/etcdkeyseedbundles.cluster.alauda.io/sync-prefix-file.txt共享 SeedBundle
忽略/registry/etcdkeyseedbundlestates.cluster.alauda.io/ignore-prefix-file.txt本地执行状态
忽略/registry/secrets/kube-system/etcd-encryption-key-ignore-prefix-file.txt本地派生出的 key Secret
忽略/registry/etcdencryptionconfigs.cluster.alauda.io/defaultignore-equal.txt本地配置
忽略/registry/secrets/kube-system/<rootSecretRef.name>ignore-equal.txt由两侧各自分发
忽略/registry/configmaps/kube-public/etcd-syncignore-equal.txtruntime marker,由本地 etcd-sync 维护

每条规则对应的 ConfigMap 字段语义见 etcd-sync-dynamic-configmap-migration/provider-spec.md 的"支持的数据键"与"排序模型"。

approvalDelay 时序约束

activationPolicy=timed-approval 时,Active 创建本地 EtcdKeySeedBundleState 后会等待 approvalDelay 再自动批准。批准之后 Active 立刻把本地 EncryptionConfig.keys 从 [old, new] 重排为 [new, old],开始用 key_N 加密;新密文随后通过 etcd-sync 进入 Standby。

安全要求:approvalDelay 必须不小于 Standby 端到端预热时间 T_prep,否则会出现新密文先于新密钥到达 Standby 的解密失败窗口。

T_prep 由四个串联分量组成:

分量含义典型量级主要影响因素
T_syncSeedBundle 通过 etcd-sync 复制到 Standby etcd秒级etcd-sync 复制策略、跨地域 RTT、目标 etcd 写入延迟
T_reconcileStandby controller 触发、创建本地 BundleState、读 Root Secret、HKDF 派生、写回 EncryptionConfig秒级controller workqueue、API server 写入延迟
T_deploy把新 encryption-provider.conf 下发到所有控制平面节点数十秒到数分钟控制平面节点数、节点串/并行下发策略、节点可达性
T_reloadkube-apiserver 检测 encryption-provider.conf 变化并加载新 provider秒到分钟是否启用 --encryption-provider-config-automatic-reload(K8s 1.22+);未启用时依赖 apiserver Pod 重启

只有四步都完成,Standby 才进入 Prepared,本地 EncryptionConfig.keys=[old, new],具备解密 revision=N 密文的能力。

时间线:

失败模式:若 approvalDelay < T_prep,Active 在 Standby 仍处于 Preparing(甚至更早阶段)时已经切主。期间被加密同步过去的 Secret/ConfigMap 在 Standby 上会出现 no matching prefix found 类错误,直到 Standby 的预热链路追平为止。

取值建议:

  • 默认 30m 面向常见规模(个位数控制平面节点 + 同城跨集群 etcd-sync + 启用 automatic-reload)有较大安全余量
  • 控制平面节点较多、跨地域复制、或目标 K8s 版本未启用 automatic-reload 时,应按测量到的 T_prep 上限再加安全余量调大
  • 调小到分钟级前必须实测:etcd-sync 抖动峰值、T_deploy 全节点完成的 P99 时间、apiserver reload 收敛时间
  • 任何无法以时间窗口稳定覆盖的场景(Standby 节点周期性失联、维护窗口期)应切换为 manual,由运维显式 gating

approvalDelay 不保证的事:它只控制 Active 端的"等待时间",不读取 Standby 的实际预热结果。即使到期后 Active 自动批准,Standby 也可能因为同步阻塞或节点故障而仍未 Prepared。这是 v1 的已知 best-effort 边界。

失败语义

失败类型表现
Root Secret 长度不是 32 字节派生函数立即返回错误,加密配置无法 reconcile,EtcdEncryptionConfig.status.deployStatus 进入 UpdateFailed
Seed 解码后不是 32 字节同上
derivationAlgorithm 不在支持列表同上,错误信息 unsupported derivation algorithm
Root Secret 在 Active / Standby 内容不同派生不报错,但两边 key 字节级不同;密文同步过去后 Standby 解密失败
replicationGroupID 在两侧不同同上
SeedBundle 没被 etcd-sync 同步过去Standby 缺少派生输入,etcd-encryption-key-<revision> 不会生成

前三类是部署期硬错误,能在配置阶段暴露;后三类只在密文流转后才暴露,必须在交付前用本主题验证清单中"两边 key 一致"那一条覆盖到。

On this page