Kubernetes Secret 加密实践:ETCD 里的敏感信息不能明文存

📅 2026/7/10 15:09:33 👁️ 阅读次数 📝 编程学习
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 以明文写入 ETCD
  • AESCBC/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.3ms
  • KMS 加密:每请求增加约 1-5ms(取决于 KMS 服务的延迟)

KMS 的延迟是外部调用,需要关注 KMS API 的可用性。如果 KMS 返回限流错误,API Server 的 Secret 操作会失败。配置合理的 KMS 速率限制配额是必须的。

不适合 ETCD 加密的场景

  • 对 Secret 读写延迟要求 < 1ms 的实时系统
  • 完全离线部署、无法访问外部 KMS 的边缘集群

五、总结

Kubernetes Secret 的明文存储是默认的安全漏洞。三个落地步骤:

  1. 启用 ETCD 加密:配置EncryptionConfiguration,使用 KMS 提供者
  2. 密钥外移:将加密密钥托管到外部 KMS,不与集群共存
  3. 设置周期轮换:定期更新加密密钥,重加密存量 Secret

ETCD 加密不是终点。结合 RBAC 最小权限、Secret Volume 的适当挂载策略、应用层的二级加密,形成多层防护。Secret 管理的目标不是防住所有攻击,而是让单一层的突破不足以导致凭证泄露。