10 - 密钥管理
第 10 章 — 密钥管理
10.1 为什么需要密钥管理?
容器中经常需要使用敏感信息:数据库密码、API Token、TLS 证书等。错误的密钥管理会导致严重安全风险。
❌ 反面教材
# 直接在命令行传递密码(会出现在 ps 和 history 中)
podman run -e POSTGRES_PASSWORD=mysecret postgres
# 在 Dockerfile 中硬编码(会被提交到镜像层)
# ENV API_KEY=sk-1234567890
# 使用环境变量文件(明文存储在代码仓库中)
# .env 中写:DB_PASSWORD=secret123
✅ 正确方式
# 使用 Podman Secrets(加密存储,按需注入)
echo "mysecret" | podman secret create db-password -
podman run --secret db-password,type=env,target=POSTGRES_PASSWORD postgres
10.2 Podman Secrets
10.2.1 基本操作
# 创建 Secret(从标准输入)
echo "my-secret-password" | podman secret create db-password -
# 创建 Secret(从文件)
podman secret create tls-cert /path/to/cert.pem
# 创建 Secret(从字符串)
podman secret create api-key --env API_KEY
# 列出 Secrets
podman secret ls
# 查看 Secret 详情(不显示值)
podman secret inspect db-password
# 删除 Secret
podman secret rm db-password
# 清理所有未使用的 Secret
podman secret prune
10.2.2 在容器中使用 Secret
# 以文件形式挂载(默认挂载到 /run/secrets/<name>)
podman run -d --name db \
--secret db-password \
postgres:16-alpine
# 在容器中读取 Secret 文件
podman exec db cat /run/secrets/db-password
# my-secret-password
# 以环境变量形式注入
podman run -d --name db \
--secret db-password,type=env,target=POSTGRES_PASSWORD \
postgres:16-alpine
# 自定义挂载路径
podman run -d --name app \
--secret db-password,target=password.txt \
myapp:latest
# 使用多个 Secrets
podman run -d --name app \
--secret db-password \
--secret api-key \
--secret tls-cert,target=/app/certs/tls.pem \
myapp:latest
10.2.3 Secret 挂载方式对比
| 参数 | 效果 |
|---|
--secret name | 挂载到 /run/secrets/name(文件) |
--secret name,type=env,target=KEY | 注入为环境变量 KEY |
--secret name,target=/path/file | 挂载到自定义路径 |
10.3 Quadlet 集成
10.3.1 在 .container 文件中使用 Secret
# ~/.config/containers/systemd/app.container
[Unit]
Description=My App
[Container]
Image=registry.example.com/app:v2.0
Secret=app-db-password,type=env,target=DATABASE_PASSWORD
Secret=app-api-key,target=/run/secrets/api-key
Volume=app-data.volume:/data:Z
[Service]
Restart=always
[Install]
WantedBy=default.target
10.4 环境变量管理
10.4.1 环境变量注入方式
# 方式一:-e 参数
podman run -e DB_HOST=localhost -e DB_PORT=5432 myapp
# 方式二:--env-file
cat > .env << 'EOF'
DB_HOST=localhost
DB_PORT=5432
DB_USER=appuser
DB_PASSWORD=secret123
API_KEY=sk-abcdef123456
EOF
podman run --env-file .env myapp
# 方式三:从宿主机环境变量传递
export DB_HOST=localhost
export DB_PASSWORD=secret
podman run -e DB_HOST -e DB_PASSWORD myapp
# 方式四:从 Secret 注入(推荐敏感信息)
podman run --secret db-password,type=env,target=DB_PASSWORD myapp
10.4.2 环境变量 vs Secrets
| 维度 | 环境变量 | Podman Secrets |
|---|
| 存储方式 | 明文 | 加密存储 |
| 可见性 | podman inspect 可见 | 不可见 |
| 历史记录 | 可能出现在 shell history | 不会泄露 |
| 更新 | 需要重建容器 | 可独立更新 |
| 适用场景 | 非敏感配置 | 密码、Token、证书 |
10.5 外部密钥管理集成
10.5.1 HashiCorp Vault
# 从 Vault 获取 Secret 并注入容器
VAULT_TOKEN=$(vault token lookup -format=json | jq -r '.data.id')
DB_PASS=$(vault kv get -field=password secret/myapp/db)
# 创建 Podman Secret
echo "$DB_PASS" | podman secret create db-password -
# 使用
podman run --secret db-password,type=env,target=DB_PASSWORD myapp
10.5.2 AWS Secrets Manager
# 从 AWS Secrets Manager 获取
DB_PASS=$(aws secretsmanager get-secret-value \
--secret-id prod/myapp/db-password \
--query 'SecretString' \
--output text | jq -r '.password')
# 创建 Podman Secret
echo "$DB_PASS" | podman secret create db-password -
# 使用
podman run --secret db-password,type=env,target=POSTGRES_PASSWORD postgres:16
10.5.3 CI/CD 环境中的 Secret 管理
# GitLab CI — 从 CI 变量创建 Secret
# .gitlab-ci.yml
deploy:
script:
- echo "$DB_PASSWORD" | podman secret create db-password -
- podman run -d --secret db-password,type=env,target=POSTGRES_PASSWORD postgres
- podman secret rm db-password # 清理
# GitHub Actions
# - name: Deploy
# run: |
# echo "${{ secrets.DB_PASSWORD }}" | podman secret create db-password -
# podman run -d --secret db-password,type=env,target=POSTGRES_PASSWORD postgres
10.6 生产场景
场景一:数据库密码管理
#!/bin/bash
# 安全地部署数据库
# 1. 生成随机密码
DB_PASS=$(openssl rand -base64 32)
# 2. 创建 Secret
echo "$DB_PASS" | podman secret create db-production-password -
# 3. 部署数据库
podman run -d --name postgres-prod \
--secret db-production-password,type=env,target=POSTGRES_PASSWORD \
-e POSTGRES_USER=appuser \
-e POSTGRES_DB=appdb \
-v pgdata:/var/lib/postgresql/data:Z \
postgres:16-alpine
# 4. 应用使用 Secret
podman run -d --name app-prod \
--secret db-production-password,type=env,target=DB_PASSWORD \
-e DB_HOST=postgres-prod \
-e DB_USER=appuser \
-e DB_NAME=appdb \
myapp:v2.0
echo "数据库密码已安全创建,不会出现在 ps 或 history 中"
场景二:TLS 证书管理
# 创建 TLS 证书 Secret
podman secret create tls-cert /etc/ssl/certs/server.crt
podman secret create tls-key /etc/ssl/private/server.key
# 在 Nginx 中使用
podman run -d --name nginx-tls \
--secret tls-cert,target=/etc/nginx/certs/server.crt \
--secret tls-key,target=/etc/nginx/certs/server.key \
-p 443:443 \
nginx:1.27-alpine
场景三:API Key 轮换
#!/bin/bash
# API Key 轮换脚本
NEW_KEY=$(curl -s https://api.example.com/rotate-key)
# 删除旧 Secret
podman secret rm api-key 2>/dev/null
# 创建新 Secret
echo "$NEW_KEY" | podman secret create api-key -
# 重启使用该 Secret 的容器
podman restart app-using-api
10.7 安全最佳实践
✅ Do’s
# 1. 使用 Secrets 存储所有敏感信息
echo "$password" | podman secret create db-pass -
# 2. 使用完后及时清理临时 Secret
podman secret rm temp-secret
# 3. 使用 --password-stdin 登录仓库
echo "$REGISTRY_TOKEN" | podman login registry.example.com --username user --password-stdin
# 4. 定期轮换密钥
# 5. 使用 .gitignore 排除敏感文件
# .gitignore 中添加:
# .env
# *.pem
# *.key
❌ Don’ts
# 1. 不要在 Dockerfile 中硬编码密码
# ENV PASSWORD=secret ❌
# 2. 不要在命令行传递敏感信息
podman run -e PASSWORD=mysecret ... # ❌ 会出现在 ps 中
# 3. 不要在镜像中存储密钥
# COPY secret.pem /app/secret.pem ❌ 会永久保留在镜像层
# 4. 不要在代码仓库中提交 .env 文件(包含密码的)
# 5. 不要使用 --privileged(会绕过所有安全限制)
10.8 本章小结
| 知识点 | 要点 |
|---|
| 创建 Secret | podman secret create (stdin/文件) |
| 使用 Secret | --secret name(文件)或 type=env,target=KEY |
| 存储位置 | 加密存储,不可通过 inspect 查看 |
| 环境变量 | 非敏感配置使用,敏感信息用 Secrets |
| 外部集成 | Vault、AWS Secrets Manager、CI/CD 变量 |
| 最佳实践 | Secret > 环境变量 > 硬编码 |
下一步
扩展阅读