1. 为什么我们需要加密Kubernetes Secrets

Kubernetes Secrets是用来存储和管理敏感信息(如密码、OAuth令牌、SSH密钥等)的对象。但默认情况下,这些Secrets只是做了简单的Base64编码,而不是真正的加密。这就好比你把家门钥匙藏在门口的地毯下 - 任何人都知道去那里找。

在Kubernetes集群中,Secrets数据最终会存储在ETCD这个分布式键值存储中。如果不加密,任何能够访问ETCD的人(比如系统管理员)都可以直接查看这些敏感信息。这显然不符合安全最佳实践。

想象一下这样的场景:你的生产环境中有一个包含数据库密码的Secret。如果黑客入侵了你的ETCD,或者有内部人员滥用权限,他们就能轻松获取这些关键凭证,进而控制你的整个数据库。这绝对不是我们想看到的结果。

2. ETCD数据加密基础

2.1 Kubernetes中的静态数据加密

Kubernetes提供了静态数据加密(Encryption at rest)功能,可以在数据写入ETCD之前对其进行加密。这就像给你的敏感数据加了一个保险箱,即使有人拿到了ETCD的数据文件,没有密钥也无法解密内容。

Kubernetes使用一个称为"EncryptionConfiguration"的配置文件来定义加密方案。这个配置文件告诉API服务器如何加密和解密资源。

2.2 支持的加密方案

Kubernetes支持多种加密方案,主要包括:

  • AES-CBC:使用AES算法在CBC模式下的加密,密钥长度可以是128位、192位或256位
  • AES-GCM:使用AES算法在GCM模式下的加密,提供更好的性能和安全性
  • Secretbox:使用XSalsa20和Poly1305算法的加密方案
  • KMS插件:与外部密钥管理服务集成的方案

其中,AES-GCM是目前推荐的选择,因为它既安全又高效。而KMS插件方案则适合需要更高安全级别的场景。

3. 配置ETCD数据加密

3.1 创建EncryptionConfiguration文件

让我们从最基本的AES-GCM加密开始。首先,我们需要生成一个加密密钥,然后创建EncryptionConfiguration文件。

# encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <base64-encoded-secret>
      - identity: {}  # 允许未加密的资源被读取(向后兼容)

要生成一个安全的密钥,我们可以使用以下命令:

# 生成32字节(256位)的随机密钥并使用base64编码
head -c 32 /dev/urandom | base64

3.2 应用加密配置

有了配置文件后,我们需要让API服务器使用它。这通常需要修改API服务器的启动参数:

# 修改kube-apiserver的启动参数
--encryption-provider-config=/etc/kubernetes/encryption-config.yaml

在kubeadm管理的集群中,我们可以通过修改/var/lib/kubelet/config.yaml文件来添加这个参数。

3.3 验证加密是否生效

应用配置后,我们可以通过以下步骤验证加密是否正常工作:

  1. 创建一个测试Secret:

    kubectl create secret generic test-secret --from-literal=mykey=mydata
    
  2. 直接查询ETCD查看数据:

    ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
    --cacert=/etc/kubernetes/pki/etcd/ca.crt \
    --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \
    --key=/etc/kubernetes/pki/apiserver-etcd-client.key \
    get /registry/secrets/default/test-secret
    

如果配置正确,你应该看到加密后的数据,而不是原始的"mydata"。

4. 使用外部密钥管理服务(KMS)

虽然本地加密提高了安全性,但密钥仍然存储在集群中。为了更高级别的安全,我们可以集成外部密钥管理服务(Key Management Service, KMS)。

4.1 KMS插件工作原理

KMS插件通过gRPC与外部密钥管理服务通信。当API服务器需要加密数据时:

  1. 将明文发送给KMS插件
  2. KMS插件转发给外部KMS服务
  3. KMS服务返回加密后的数据
  4. API服务器将加密数据存储到ETCD

解密过程则相反。关键在于加密密钥永远不会暴露给Kubernetes集群。

4.2 配置AWS KMS插件示例

AWS提供了KMS插件实现。下面是一个使用AWS KMS的EncryptionConfiguration示例:

# encryption-config-kms.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - kms:
          name: aws-kms
          endpoint: unix:///var/run/kms-plugin/socket.sock
          cachesize: 100
          timeout: 3s
      - identity: {}

4.3 部署KMS插件

部署AWS KMS插件通常需要以下步骤:

  1. 在AWS中创建KMS密钥并记录其ARN
  2. 创建IAM角色和策略,允许插件访问KMS
  3. 部署插件作为DaemonSet或Deployment
  4. 配置API服务器使用插件
# 部署AWS KMS插件的Deployment示例
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: aws-encryption-provider
  namespace: kube-system
spec:
  replicas: 2
  selector:
    matchLabels:
      app: aws-encryption-provider
  template:
    metadata:
      labels:
        app: aws-encryption-provider
    spec:
      containers:
      - name: aws-encryption-provider
        image: amazon/aws-encryption-provider:latest
        args: ["--key=arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"]
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: sock
          mountPath: /var/run/kms-plugin
      volumes:
      - name: sock
        emptyDir: {}
EOF

5. 密钥轮换策略

5.1 为什么需要轮换密钥

即使使用了加密,定期轮换密钥也是安全最佳实践。这可以:

  • 限制密钥泄露的影响范围
  • 符合合规要求
  • 应对潜在的密钥破解风险

5.2 本地加密密钥轮换

对于本地加密方案,轮换密钥需要:

  1. 在EncryptionConfiguration中添加新密钥(确保放在keys数组的第一个位置)
  2. 重启API服务器
  3. 重写所有Secrets以使用新密钥加密
# 更新后的encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key2  # 新密钥
              secret: <new-base64-encoded-secret>
            - name: key1  # 旧密钥
              secret: <old-base64-encoded-secret>
      - identity: {}

5.3 KMS密钥轮换

对于KMS方案,轮换通常更简单:

  1. 在KMS服务中创建新密钥
  2. 更新插件配置使用新密钥ARN
  3. 滚动重启插件和API服务器

大多数KMS服务(如AWS KMS)支持自动密钥轮换,可以进一步简化这个过程。

6. 实际应用场景分析

6.1 金融行业应用

在金融行业,合规要求通常非常严格。使用KMS集成方案可以:

  • 满足PCI DSS等标准对密钥管理的要求
  • 实现职责分离(密钥管理与集群管理分开)
  • 提供详细的密钥使用审计日志

6.2 多租户环境

在多租户Kubernetes环境中,ETCD加密可以:

  • 防止租户间通过ETCD访问互相的Secrets
  • 结合RBAC提供纵深防御
  • 支持租户特定的加密密钥(通过多个EncryptionConfiguration)

6.3 混合云部署

对于跨云部署的场景,外部KMS方案可以:

  • 提供一致的密钥管理体验
  • 避免被单一云厂商锁定
  • 支持企业级密钥管理策略

7. 技术优缺点对比

7.1 本地加密方案

优点:

  • 实现简单,不需要额外基础设施
  • 性能开销小
  • 适合小型或测试环境

缺点:

  • 密钥仍然存储在集群内
  • 密钥轮换复杂
  • 缺乏高级密钥管理功能

7.2 KMS集成方案

优点:

  • 更高的安全性(密钥不存储在集群中)
  • 专业的密钥管理功能(轮换、审计等)
  • 符合严格合规要求

缺点:

  • 需要额外的基础设施
  • 可能引入性能开销
  • 增加系统复杂性

8. 注意事项与最佳实践

8.1 加密前的Secrets

启用加密前存在的Secrets不会被自动加密。你需要手动更新它们:

kubectl get secrets --all-namespaces -o json | kubectl replace -f -

8.2 性能考虑

加密会增加API服务器的CPU开销。对于大规模集群:

  • 考虑使用AES-GCM而不是AES-CBC
  • 对KMS插件进行性能测试
  • 可能需要增加API服务器资源

8.3 备份与恢复

加密会影响备份策略:

  • 备份ETCD数据时,确保也备份EncryptionConfiguration
  • 恢复集群时,需要使用相同的加密密钥
  • 考虑将加密配置纳入版本控制(不含密钥)

8.4 多集群管理

管理多个集群时:

  • 考虑使用集中式的KMS服务
  • 为每个集群使用不同的加密密钥
  • 文档化每个集群的加密方案

9. 总结

Kubernetes Secrets加密是保护集群敏感数据的关键措施。从基本的ETCD加密到与外部KMS服务集成,Kubernetes提供了多种方案来满足不同安全需求。

对于大多数生产环境,特别是受严格合规要求约束的环境,推荐使用KMS集成方案。虽然设置更复杂,但它提供了企业级的安全保障和密钥管理能力。

无论选择哪种方案,都要确保:

  1. 理解加密的范围和限制
  2. 实施适当的密钥轮换策略
  3. 考虑加密对运维流程(如备份、恢复)的影响
  4. 定期审计加密配置和访问权限

通过合理配置ETCD数据加密和外部密钥管理,你可以显著提升Kubernetes集群的安全性,保护关键业务数据不被泄露。