深色模式
秘钥自动化分发:安全下发配置
摘要:密钥分发的底线是「不进 Git 明文、不落明文磁盘、能轮换、可审计」。本文给出三条由轻到重的落地路线:SOPS 加密入 Git、Ansible Vault 下发、K8s External Secrets。
适用环境
- Linux 服务器或 Kubernetes 集群
- 有集中密钥存储需求(≥3 个服务共用密钥时值得做)
- 具备基本的 TLS 证书概念
操作步骤
1. 先明确不该做的事
禁止明文密钥提交 Git、密钥写进镜像、通过聊天工具传密钥;禁止所有服务共用一个超级密钥、密钥永不轮换。必须先检查历史:
bash
git log -p | grep -iE '(password|secret|token|apikey)\s*[:=]' | head1
2. 路线一:SOPS 加密后入 Git(轻量)
bash
curl -sSL -o sops https://github.com/getsops/sops/releases/latest/download/sops-v3.9.0.linux.amd64
chmod +x sops && sudo mv sops /usr/local/bin/
age-keygen -o ~/.age/key.txt
cat > .sops.yaml <<'EOF'
creation_rules:
- path_regex: .*\.enc\.yaml$
age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
EOF
sops secrets.enc.yaml # 编辑并自动加密
sops -d secrets.enc.yaml # 解密查看1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
流水线中解密后注入环境变量,解密产物不要落盘到持久目录。
3. 路线二:Ansible Vault 下发到服务器
yaml
- name: Deploy app secret
ansible.builtin.copy:
content: "{{ vault_db_password }}"
dest: /etc/app/db.password
owner: appuser
mode: '0400' # 仅属主可读
no_log: true # 不在日志中回显1
2
3
4
5
6
7
2
3
4
5
6
7
no_log 很重要
不加 no_log: true 时,Ansible 会把变量值打印到执行日志,等于把密钥写进 CI 日志。
4. 路线三:External Secrets Operator(推荐)
密钥仍存于外部系统(云 KMS / Vault),Operator 同步成 Secret,Git 里只存引用。
bash
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets -n external-secrets --create-namespace1
2
2
yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata: { name: app-secret }
spec:
refreshInterval: "1h"
secretStoreRef: { name: vault-backend, kind: SecretStore }
target: { name: app-secret }
data:
- secretKey: db-password
remoteRef: { key: app/prod, property: db_password }1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
5. 证书自动化
bash
curl https://get.acme.sh | sh
acme.sh --issue -d example.internal --webroot /var/www/html
acme.sh --install-cert -d example.internal \
--key-file /etc/nginx/ssl/key.pem \
--fullchain-file /etc/nginx/ssl/cert.pem \
--reloadcmd "systemctl reload nginx"1
2
3
4
5
6
2
3
4
5
6
危险
私钥一旦进过 Git 历史,删除文件也无济于事,必须立即轮换密钥,并把旧密钥加入失效名单。
验证
bash
sops -d secrets.enc.yaml | head -3
kubectl get externalsecret app-secret -o yaml | grep -i ready
ls -l /etc/app/db.password # 权限应为 -r--------1
2
3
2
3
- [ ] Git 仓库中不存在任何明文密钥
- [ ] 目标机器上的密钥文件权限为 400/600
- [ ] 轮换一次密钥,下游能自动或在 1 小时内拿到新值
常见坑
环境变量被 ps 泄漏
docker run -e PASSWORD=xxx 时同主机 ps 可能看到。优先用文件挂载或 Secret 注入。
Secret 更新了但 Pod 没重载
以环境变量挂载的 Secret 不会自动更新,需重启 Pod;以文件挂载则会自动更新(有延迟)。
用 root 权限跑所有分发
分发脚本应以最小权限运行,只读取自己需要的那一个密钥路径。