Vault 与密钥
本章目标:用 Ansible Vault 管理敏感变量,做到 Git 可追踪、磁盘上非明文、运行时受控解密,并理清 Vault 与 SSH 私钥、CI 密钥注入的边界。
Vault 不是权限系统:能拿到 Vault 密码的人就能看到明文。它解决的是「仓库/备份里不出现明文秘密」。
1. 哪些该进 Vault
| 应该 | 不应该 |
|---|---|
| 数据库密码、API Token | 非敏感的端口、包名、开关 |
| 云 AK/SK(若必须给 Ansible 用) | 逻辑主机名、分组结构 |
| TLS 私钥内容、密钥口令 | 公钥(通常可明文) |
应用 SECRET_KEY | 文档、注释、示例占位符 |
原则:最小秘密集。秘密越少,轮换与审计越简单。
2. 加密整个文件(推荐起步)
2.1 目录拆分
group_vars/web/
vars.yml # 明文:端口、副本数…
vault.yml # 密文:密码、Token
group_vars/web/vars.yml:
---
nginx_listen_port: 80
db_host: 10.0.1.21
db_user: app
# 密码在 vault.yml
db_password: "{{ vault_db_password }}"
2.2 创建并编辑
cd ~/ansible-for-ops
mkdir -p group_vars/web
ansible-vault create group_vars/web/vault.yml
写入:
---
vault_db_password: "change-me-strong"
vault_app_secret: "change-me-strong"
命名习惯:Vault 内用 vault_ 前缀,明文 vars 再映射一次,避免「到底哪个文件是真源」混淆。
2.3 已有明文改加密
ansible-vault encrypt group_vars/web/vault.yml
ansible-vault view group_vars/web/vault.yml
ansible-vault edit group_vars/web/vault.yml
ansible-vault decrypt group_vars/web/vault.yml # 一般不需要,紧急才用
ansible-vault rekey group_vars/web/vault.yml # 人员变动时换密码
加密后文件头类似:
$ANSIBLE_VAULT;1.1;AES256
6638...
可安全提交 Git(仍需保护 Vault 密码)。
3. 加密单个字符串
适合「大部分明文、只有一两个字段敏感」:
ansible-vault encrypt_string 'S3cr3t!' --name 'vault_db_password'
把终端输出粘贴进 group_vars/web/vars.yml:
vault_db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
6638...
db_password: "{{ vault_db_password }}"
字段一多,整文件加密的 vault.yml 更好维护;encrypt_string 适合少量嵌入。
4. 运行时如何提供密码
交互
ansible-playbook playbooks/web.yml --ask-vault-pass
密码文件(本地/CI)
umask 077
echo 'your-vault-pass' > ~/.ansible/vault_pass_web
chmod 600 ~/.ansible/vault_pass_web
ansible-playbook playbooks/web.yml \
--vault-password-file ~/.ansible/vault_pass_web
ansible.cfg:
[defaults]
vault_password_file = ~/.ansible/vault_pass_web
把 vault_pass 提交进业务仓库;或在多用户共用的 NFS home 里乱放明文密码文件。
环境变量(注意进程列表与调试日志)
# 自定义脚本包装更常见:从密钥管理器取密码写临时文件
export ANSIBLE_VAULT_PASSWORD_FILE=~/.ansible/vault_pass_web
5. 多密码与 vault-id(多环境)
ansible-vault encrypt group_vars/web/vault.yml --vault-id prod@prompt
ansible-playbook playbooks/site.yml --vault-id prod@~/.ansible/vault_prod
场景:
- staging / prod 使用不同 Vault 密码
- 不同团队角色接触不同秘密集
ansible.cfg 示例:
[defaults]
vault_identity_list = staging@~/.ansible/vault_staging, prod@~/.ansible/vault_prod
6. Playbook 中使用秘密
- name: 写应用环境文件
ansible.builtin.template:
src: app.env.j2
dest: /data/app/.env
owner: app
group: app
mode: "0600"
templates/app.env.j2:
DB_HOST={{ db_host }}
DB_USER={{ db_user }}
DB_PASSWORD={{ db_password }}
APP_SECRET={{ vault_app_secret }}
要点:
- 落盘权限
0600,属主最小化 - 不要
debug: var=db_password - 失败日志也可能回显参数;敏感 task 注意模块是否支持
no_log: true
- name: 可能回显敏感参数的任务
ansible.builtin.command: /usr/local/bin/set-secret
no_log: true
no_log: true 会隐藏该任务输出,排障变难,只加在真正敏感处。
7. 与 SSH 私钥、机器身份的关系
| 秘密类型 | 建议存放 |
|---|---|
| 控制节点 SSH 私钥 | 控制节点文件系统 + 权限 600,或 ssh-agent / 硬件钥;一般不进 Git Vault |
| 应用 TLS key | Vault 加密变量或从密钥系统下发 |
| 云 API Key | CI 密钥注入 / Vault / 云侧 IAM 角色(更优) |
把「登录机器的钥匙」和「应用用的钥匙」分开管理,轮换策略也不同。
8. CI 集成要点
以 GitLab CI / Jenkins 为例,模式相同:
- CI 变量保存 Vault 密码(masked + protected)
- Job 写入临时文件
chmod 600 ansible-playbook --vault-password-file ...- Job 结束删除临时文件
注意:
- MR 流水线不要用生产 Vault 密码(protected branch only)
- 日志中关闭冗余
-vvv,避免模块参数泄露 - 变更人与解密权限可审计(谁触发了 prod job)
伪示例:
echo "$ANSIBLE_VAULT_PASSWORD" > "$CI_PROJECT_DIR/.vault_pass"
chmod 600 "$CI_PROJECT_DIR/.vault_pass"
ansible-playbook -i inventory/prod.ini playbooks/site.yml \
--vault-password-file "$CI_PROJECT_DIR/.vault_pass"
rm -f "$CI_PROJECT_DIR/.vault_pass"
9. 轮换与应急
| 事件 | 动作 |
|---|---|
| 人员离职 | ansible-vault rekey,并轮换被解密过的业务密码 |
| 密码泄露 | rekey + 轮换全部 vault 内秘密 + 审查 Git 历史访问 |
| 紧急明文排障 | decrypt 到安全介质,结束后删明文并 re-encrypt |
Git 历史中若曾提交过明文,加密后历史仍在;需要改写历史或视为已泄露并轮换。
10. 练习与验收
练习 A
创建 group_vars/web/vault.yml,映射 db_password,在 template 中引用;确认未解密时 playbook 失败,解密后成功。
练习 B
用 encrypt_string 嵌一个变量,ansible-vault view 相关文件确认格式。
练习 C
给写 .env 的 task 加 mode: "0600" 与必要的 no_log。
验收清单
- 仓库中无明文业务密码
- vars / vault 拆分清晰
- 本地与 CI 均通过 password-file 解密
- 知道 rekey 与「历史明文」风险
下一章:生产规范与 Web 节点实战 —— 把 Role、护栏、Vault 收成一套可交付工程。