别再把密码写进代码,用 Secret 安全存储 Kubernetes 中的密钥
概述
你有没有这样写过代码?
# 危险!绝对不要这样做!env:-name:DB_PASSWORDvalue:"mySuperSecret123!"或者把 API Key 直接打包进 Docker 镜像?
在 Kubernetes 中,这类敏感信息(密码、Token、证书等)应该用Secret来管理。
什么是 Secret
Secret是 Kubernetes 的一种资源对象,专门用于:
- 存储敏感数据,如:
- 数据库密码
- OAuth Token
- TLS 证书私钥
- 第三方 API 密钥
- 将敏感信息与应用代码/镜像解耦
- 通过 RBAC 控制访问权限
核心原则:敏感信息绝不硬编码,绝不进 Git,绝不进镜像!
Secret vs ConfigMap
| 特性 | ConfigMap | Secret |
|---|---|---|
| 用途 | 非敏感配置(日志级别、开关) | 敏感信息(密码、密钥) |
| 存储格式 | 明文(YAML 中直接可见) | Base64 编码(非加密!) |
| 默认权限 | 所有 Pod 可读(需显式引用) | 同左,但可通过 RBAC 限制 |
| 安全性 | 低 | 中(需配合其他措施) |
说明:
Secret 并不是“加密存储”!它只是 Base64 编码(可轻松解码)。
真正的安全依赖于:
- etcd 启用加密(集群管理员配置)
- 严格的 RBAC 权限控制
- 不将 Secret 写入日志或输出
创建 Secret 的三种方式
方式 1:从字面值创建(适合少量密钥)
kubectl create secret generic db-secret\--from-literal=username=admin\--from-literal=password='S3cr3tP@ss!'查看内容(注意:会显示 Base64 编码):
kubectl get secret db-secret-oyaml输出片段:
data:username:YWRtaW4=# echo -n "admin" | base64password:UzNjcjN0UEBzc2Eh# echo -n "S3cr3tP@ss!" | base64你可以用echo "UzNjcjN0UEBzc2Eh" | base64 -d解码回原文
方式 2:从文件创建(适合证书、密钥文件)
假设你有:
tls.key(私钥)tls.crt(证书)
创建 Secret:
kubectl create secret tls my-tls-secret\--cert=tls.crt\--key=tls.key这是创建 HTTPS 证书 Secret 的标准方式
方式 3:从 YAML 文件定义(最灵活,推荐)
创建secret.yaml:
apiVersion:v1kind:Secretmetadata:name:app-secretstype:Opaque# 默认类型,用于通用密钥data:# 注意:必须是 Base64 编码!database_password:UzNjcjN0UEBzc2Ehapi_key:QVBJLTIwMjQtMTIzNDU2Nzg5MAo=或者用stringData(自动编码,更友好):
apiVersion:v1kind:Secretmetadata:name:app-secretstype:OpaquestringData:# 直接写明文,K8s 自动转 Base64database_password:"S3cr3tP@ss!"api_key:"API-2024-1234567890"强烈推荐用stringData!避免手动编码出错。
应用:
kubectl apply-fsecret.yaml在 Pod 中使用 Secret
场景 1:作为环境变量注入(简单但有风险)
# deployment-env.yamlapiVersion:apps/v1kind:Deploymentmetadata:name:my-appspec:template:spec:containers:-name:appimage:my-app:1.0env:-name:DB_PASSWORDvalueFrom:secretKeyRef:name:app-secretskey:database_password注意:如果应用打印环境变量(如调试日志),密码会泄露
场景 2:挂载为文件(更安全,推荐)
# deployment-volume.yamlapiVersion:apps/v1kind:Deploymentmetadata:name:secure-appspec:template:spec:containers:-name:appimage:my-app:1.0volumeMounts:-name:secret-volumemountPath:"/etc/secrets"readOnly:true# 必须只读!volumes:-name:secret-volumesecret:secretName:app-secrets效果:
- 容器内
/etc/secrets/database_password文件内容 = 密码原文 - 应用通过读文件获取密码(如
fs.readFileSync('/etc/secrets/database_password'))
优势:
- 不会出现在环境变量中
- 文件权限默认为
644,只有容器内进程可读 - 更符合安全最佳实践
实战:部署一个连接数据库的 Python 应用
Step 1:创建 Secret
db-secret.yaml:
apiVersion:v1kind:Secretmetadata:name:postgres-credentialstype:OpaquestringData:username:"app_user"password:"My$tr0ngP@ss!"Step 2:编写 Deployment(挂载为文件)
python-app.yaml:
apiVersion:apps/v1kind:Deploymentmetadata:name:python-db-appspec:replicas:1selector:matchLabels:app:python-dbtemplate:metadata:labels:app:python-dbspec:containers:-name:appimage:python:3.9-slimcommand:["sh","-c"]args:-|USERNAME=$(cat /etc/secrets/username) PASSWORD=$(cat /etc/secrets/password) echo "Connecting to DB as $USERNAME..." # 这里可以写真实连接逻辑 sleep 3600volumeMounts:-name:secret-volmountPath:"/etc/secrets"readOnly:truevolumes:-name:secret-volsecret:secretName:postgres-credentialsStep 3:部署并验证
kubectl apply-fdb-secret.yaml kubectl apply-fpython-app.yaml# 查看日志(不应出现密码!)kubectl logs-lapp=python-db输出:
Connecting to DB as app_user...最佳实践
| 建议 | 说明 |
|---|---|
✅ 使用stringData创建 Secret | 避免手动 Base64 |
| ✅ 挂载为文件而非环境变量 | 减少泄露风险 |
✅ 设置readOnly: true | 防止应用意外修改 |
| ✅ 限制 RBAC 权限 | 只允许必要服务账户读取 |
| ✅ 启用 etcd 加密(集群级) | 防止磁盘泄露(需管理员操作) |
❌ 不要kubectl get secret -o yaml后截图发群里! | Base64 很容易解码 |
| ❌ 不要把 Secret 提交到 Git | 即使是私有仓库也不行! |
终极建议:生产环境考虑使用外部密钥管理服务(如 HashiCorp Vault、AWS Secrets Manager),Secret 仅作临时中转。
常见问题
1:Secret 是加密的,所以是安全的
实际只是 Base64 编码!真正的安全靠 etcd 加密 + RBAC。
2:用了 Secret 就万无一失吗?
→ 如果应用把密码打印到日志,照样泄露!安全是体系工程。
说明:
Secret 是 Kubernetes 提供的“敏感信息传递机制”,不是“保险箱”。
它解决了“如何不把密码写进代码”的问题,但不能替代整体安全策略。
总结
| 问题 | Secret 解法 |
|---|---|
| 密码写死在 YAML? | ✅ 外部化为 Secret |
| 镜像包含 API Key? | ✅ 运行时注入 |
| 多环境密钥混乱? | ✅ 创建prod-secrets/staging-secrets |
| 团队共享密码? | ✅ 通过 RBAC 控制访问 |
