在容器化和微服务架构中,敏感数据(如数据库密码、API Key、TLS证书等)的管理是一个核心的安全挑战。将这些信息直接写入Dockerfile或容器镜像中是一种非常危险的做法,因为一旦镜像泄露,所有机密信息也将暴露无遗,且通过简单的docker history命令即可追溯。
Kubernetes提供了原生的解决方案:Secret对象。本文将详细介绍如何利用Secret安全地存储敏感数据,并将其以最小权限的方式挂载到Pod中。
1. 为什么不能把密码写在镜像里?
将密码写入镜像有以下主要风险:
- 层可见性: 容器镜像由多层构建而成。即使用户在后续层删除了密码文件,该文件在历史层中仍然存在,可通过工具提取。
- 源码暴露: 如果密码硬编码在源代码中,则代码仓库泄露直接导致密码泄露。
- 缺乏动态性: 更改密码需要重建和推送整个镜像,运维成本高且易出错。
2. 理解 Kubernetes Secret 对象
Secret对象专门用于存储敏感数据。它将数据存储在集群的Etcd数据库中,默认情况下,这些数据是Base64编码的,而不是加密的。注意:Base64编码并非加密,它只是一种传输编码,用于处理二进制数据。 真正的安全依赖于Etcd的访问控制和可选的Etcd静态加密。
2.1 创建 Secret:Base64编码
我们首先需要将数据转换为Base64格式(虽然kubectl工具可以代劳,但了解手动编码是重要的)。
假设我们要存储的用户名是 app_user,密码是 S3cureP@ssw0rd。
# 编码用户名
echo -n 'app_user' | base64
# 输出: YXBwX3VzZXI=
# 编码密码
echo -n 'S3cureP@ssw0rd' | base64
# 输出: SzNjdXJlUEBzc3cwcmQ=
2.2 使用 YAML 定义 Secret
创建 db-secret.yaml 文件:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
username: YXBwX3VzZXI=
password: SzNjdXJlUEBzc3cwcmQ=
应用 Secret:
kubectl apply -f db-secret.yaml
# 快捷创建方式(不推荐在生产环境直接使用,因为命令历史可能暴露秘密)
# kubectl create secret generic db-credentials --from-literal=username=app_user --from-literal=password='S3cureP@ssw0rd'
3. 安全地将 Secret 挂载到 Pod 中
将 Secret 挂载到 Pod 的最安全方式是作为卷文件,而不是环境变量。环境变量容易通过简单的ps命令或者应用日志泄露。
当Secret作为卷挂载时,Kubernetes会在Pod的文件系统中创建一个tmpfs(内存文件系统)卷,将Secret的每个键/值对映射为一个文件。应用可以直接读取这些文件。
3.1 Pod 定义与 Secret 挂载
创建 secure-app-pod.yaml 文件:
apiVersion: v1
kind: Pod
metadata:
name: secure-db-app
spec:
containers:
- name: my-db-client
image: busybox:latest
command: ["sh", "-c", "sleep 3600"]
volumeMounts:
# 将Secret挂载到 /mnt/secrets 目录下
- name: db-secret-volume
mountPath: "/mnt/secrets"
readOnly: true # 确保容器无法修改密钥
volumes:
- name: db-secret-volume
secret:
secretName: db-credentials
defaultMode: 0440 # 设定文件权限为只读,确保只有属主和属组可以访问
应用 Pod:
kubectl apply -f secure-app-pod.yaml
3.2 容器内验证与访问
进入运行中的 Pod 检查文件系统:
kubectl exec -it secure-db-app -- sh
# 查看挂载路径,注意权限是 0440
/ # ls -l /mnt/secrets
total 0
-r--r----- 1 root root 16 Apr 1 12:00 password
-r--r----- 1 root root 8 Apr 1 12:00 username
# 直接读取文件内容,Kubernetes已自动Base64解码
/ # cat /mnt/secrets/username
app_user
/ # cat /mnt/secrets/password
S3cureP@ssw0rd
4. 最佳实践总结
- 避免环境变量: 优先将 Secret 作为卷文件挂载,避免敏感信息出现在进程列表或日志中。
- 最小权限: 使用 readOnly: true 和 defaultMode 确保 Pod 对敏感文件只有读取权限,并限制权限为最低需求(如 0440)。
- Etcd 加密: 确保 Kubernetes 集群的 Etcd 存储启用了静态加密(Encryption at Rest),这是防止攻击者访问 Etcd 数据库后直接读取 Base64 数据的关键步骤。
- 使用外部 KMS/Secret Manager: 对于极高安全要求或多云环境,应考虑使用 HashiCorp Vault、AWS Secrets Manager 或 Azure Key Vault 等外部密钥管理系统,并结合 K8s CSI Secrets Store Driver 进行集成。
汤不热吧