Kubernetes Secret 加密实践:ETCD 里的敏感信息不能明文存 Kubernetes Secret 加密实践ETCD 里的敏感信息不能明文存一、Secret 是明文大多数 Kubernetes 集群的第一个安全盲区执行kubectl get secret inference-api-secret -o yaml输出内容里的data.token字段是 Base64 编码的。很多人认为这是加密的。它不是。Base64 是编码不是加密。echo c2VjcmV0 | base64 -d就能还原secret。任何一个能访问 ETCD 的实体——集群管理员、备份文件、自动化工具——都能直接读取所有 Secret 的明文。实际影响有多大一个推理服务的 Secret 里可能存放模型仓库的访问凭证数据库连接字符串含密码第三方 API Key内部服务间的 JWT 签名密钥TLS 私钥如果一个承载了这些敏感信息的 Secret 在 ETCD 中以明文存储一旦 ETCD 数据被泄露——比如备份文件被下载、运维人员的调试命令输出了 ETCD 内容——所有凭证同时暴露。Kubernetes 从 1.7 开始提供了 EncryptionConfiguration 机制支持在写入 ETCD 前对 Secret 做服务端加密。KMS 插件进一步将加密密钥外移至云 KMS 服务避免密钥与密文共存。二、ETCD 加密的数据流写入前加密读取时解密EncryptionConfiguration 在 API Server 和 ETCD 之间插入了一层加密代理。数据在进入 ETCD 前加密在从 ETCD 读取后解密。sequenceDiagram participant User as kubectl/Client participant API as API Server participant Enc as 加密层 participant ETCD as ETCD User-API: kubectl create secret API-Enc: Secret 对象明文 Enc-Enc: AES-CBC 加密br/密钥来自本地或 KMS Enc-ETCD: 加密后的 Secret 写入 Note over ETCD: 存储的是密文 User-API: kubectl get secret API-ETCD: 读取 Secret ETCD-API: 返回密文 API-Enc: 解密 Secret Enc-User: 返回明文 Secret三种加密提供者模式Identity不加密默认。Secret 以明文写入 ETCDAESCBC/AESGCM使用本地密钥加密。密钥存储在 API Server 节点上KMS密钥托管在外部 KMS如 AWS KMS、Azure Key Vault。API Server 不持有密钥本身生产环境应该使用 KMS 模式。本地密钥加密的弱点是密钥和密文存储在同一集群——如果 API Server 节点被攻破密钥泄露。KMS 模式将密钥外移至云 KMS即使整个集群被导出没有 KMS 解密权限就无法还原 Secret。三、配置 ETCD 加密从创建到验证# encryption-config.yaml apiVersion: apiserver.config.k8s.io/v1 kind: EncryptionConfiguration resources: - resources: - secrets providers: # 优先级1: AES-GCM KMS 加密最强 - kms: apiVersion: v2 name: aws-kms-provider endpoint: unix:///var/run/kmsplugin/socket.sock cachesize: 1000 timeout: 3s # 优先级2: 本地 AES-GCM降级方案 - aescbc: keys: - name: key1 secret: base64-encoded-32-byte-key # 优先级3: 明文存储从不回退 - identity: {}提供者按优先级顺序工作。新写入使用列表中的第一个提供者加密。读取时按顺序尝试解密——这支持加密密钥的平滑轮换。package secretencrypt import ( context crypto/aes crypto/cipher crypto/rand encoding/base64 fmt ) // AESGCMEncryptor 使用 AES-256-GCM 做 Secret 加密的辅助工具 // 用于在应用层面额外加密 Secret 中的特定字段 type AESGCMEncryptor struct { aead cipher.AEAD } // NewAESGCMEncryptor 初始化加密器 func NewAESGCMEncryptor(key []byte) (*AESGCMEncryptor, error) { if len(key) ! 32 { return nil, fmt.Errorf( AES-256 需要 32 字节密钥当前 %d 字节, len(key), ) } block, err : aes.NewCipher(key) if err ! nil { return nil, fmt.Errorf(创建 AES 密码失败: %w, err) } aead, err : cipher.NewGCM(block) if err ! nil { return nil, fmt.Errorf(创建 GCM 模式失败: %w, err) } return AESGCMEncryptor{aead: aead}, nil } // Encrypt 加密敏感字段 // 返回 Base64 编码的密文nonce 密文 认证标签 func (e *AESGCMEncryptor) Encrypt(plaintext []byte) (string, error) { // 生成 12 字节随机 nonce nonce : make([]byte, e.aead.NonceSize()) if _, err : rand.Read(nonce); err ! nil { return , fmt.Errorf(生成 nonce 失败: %w, err) } // AES-GCM 加密并附加认证标签 ciphertext : e.aead.Seal(nonce, nonce, plaintext, nil) // Base64 编码以便存入 K8s Secret 的 data 字段 return base64.StdEncoding.EncodeToString(ciphertext), nil } // Decrypt 解密敏感字段 func (e *AESGCMEncryptor) Decrypt(encoded string) ([]byte, error) { ciphertext, err : base64.StdEncoding.DecodeString(encoded) if err ! nil { return nil, fmt.Errorf(Base64 解码失败: %w, err) } nonceSize : e.aead.NonceSize() if len(ciphertext) nonceSize { return nil, fmt.Errorf(密文长度不足可能被截断) } nonce, ciphertext : ciphertext[:nonceSize], ciphertext[nonceSize:] plaintext, err : e.aead.Open(nil, nonce, ciphertext, nil) if err ! nil { return nil, fmt.Errorf( 解密失败密钥不匹配或密文被篡改: %w, err, ) } return plaintext, nil }应用层额外加密的场景KMS 加密保护了 ETCD 中的数据但如果某个 Pod 被攻破、直接读取了挂载的 Secret Volume——仍然是明文。对特别敏感的字段在应用层做二次加密密钥从 KMS 远程获取而非存于集群内提供纵深防护。四、加密密钥管理与轮换密钥轮换的平滑过渡。修改EncryptionConfiguration不会自动重新加密已存在的 Secret。所有的 Secret 需要用旧密钥保持不变直到被更新。可以用以下命令强制触发重加密kubectl get secrets --all-namespaces -o json | \ kubectl replace -f -重加密是一个有风险的操作。在生产集群执行前需要确保备份 ETCD 数据、确认 KMS 可用、在低峰期执行。性能影响。ETCD 加密对 API Server 的延迟有小幅影响AES-GCM 加密每请求增加约 0.1-0.3msKMS 加密每请求增加约 1-5ms取决于 KMS 服务的延迟KMS 的延迟是外部调用需要关注 KMS API 的可用性。如果 KMS 返回限流错误API Server 的 Secret 操作会失败。配置合理的 KMS 速率限制配额是必须的。不适合 ETCD 加密的场景对 Secret 读写延迟要求 1ms 的实时系统完全离线部署、无法访问外部 KMS 的边缘集群五、总结Kubernetes Secret 的明文存储是默认的安全漏洞。三个落地步骤启用 ETCD 加密配置EncryptionConfiguration使用 KMS 提供者密钥外移将加密密钥托管到外部 KMS不与集群共存设置周期轮换定期更新加密密钥重加密存量 SecretETCD 加密不是终点。结合 RBAC 最小权限、Secret Volume 的适当挂载策略、应用层的二级加密形成多层防护。Secret 管理的目标不是防住所有攻击而是让单一层的突破不足以导致凭证泄露。