容器里怎么放密钥
容器与镜像是一对容易出错的组合:镜像层是不可变的,任何在构建阶段写进去的密钥都会永久留在层里,哪怕后续步骤删除文件也依然能从历史层中提取出来。正确做法是运行时注入——Secret 挂载成文件、CSI 驱动按需从 KMS 拉取、启动时从密钥系统获取。注入方式也有优先级:文件挂载优于环境变量,因为环境变量更容易被子进程继承、被诊断接口打印、被 crash 报告收集。
基本信息
| 注入方式 | Secret 卷挂载、CSI 驱动对接 KMS、启动时按需拉取 |
|---|---|
| 优先级 | 文件挂载优于环境变量 |
| 挂载要求 | 只读挂载,避免容器内进程改写密钥文件 |
| 禁止做法 | 在 Dockerfile 里 COPY 或 RUN 写入密钥 |
| 检查点 | 镜像历史与 CI 日志中不得出现密钥材料 |
| 权限 | 按最小权限给应用账号可读即可 |
典型使用场景
- TLS 证书部署:把证书与私钥通过 Secret 挂到容器指定目录。
- 构建流水线:从 KMS 动态获取密钥,而不是写入 CI 变量并打入镜像。
- 多环境部署:不同环境挂载不同密钥,避免密钥随镜像流动。
- 安全审计:检查镜像层与历史记录中是否存在密钥材料。
- 密钥轮换:轮换后由挂载更新驱动,减少重建镜像的操作。
只读挂载与 Secret 读取
只读挂载与 Secret 读取
# 只读挂载密钥目录,容器内进程无法改写
docker run --read-only \
--mount type=bind,src=/run/secrets/tls,dst=/etc/tls,readonly nginx:stable
# 从 Kubernetes Secret 读取证书内容(注意该命令会把明文打到终端)
kubectl get secret tls-secret -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/tls.crt
# 检查镜像历史,确认构建阶段没有写入密钥
docker history --no-trunc <image>注入方式对比
| 方式 | 适用场景 | 风险点 |
|---|---|---|
| Secret 卷挂载 | 常规应用读取文件形式凭据 | 挂载权限过宽、可被容器内进程改写 |
| CSI 驱动对接 KMS | 云上按需获取,避免静态存储 | 依赖驱动与权限配置正确 |
| 环境变量 | 兼容老旧应用的临时手段 | 易被日志、诊断接口与子进程泄露 |
| 镜像内打包 | 任何场景都不推荐 | 镜像层不可删除,密钥长期留存在仓库 |
| 启动时拉取 | 需要在内存中使用而不落盘 | 拉取失败会导致启动阻塞,需要重试策略 |
删除镜像里的密钥文件并不能把它从镜像层中移除:只要有人持有该镜像,仍可逐层提取。发现密钥曾被打进镜像层时,正确处置是轮换密钥并从源头改掉构建流程。