使用 Hashicorp Vault 保存 Ansible 的敏感信息
对于敏感信息 (密码/私钥之类), Ansible 提供了 ansible-vault 工具将其加密保存. 运行 playbook 时, 通过输入密码或者读取密码文件来解密, 算是一个简单有效的方案. Hashicorp Vault 则是一个有名的私密信息管理工具. 如果公司已经开始使用 vault, 当然希望能够集中管理各种私密信息, 而不是各个工具各自为战. 这里简单介绍如何使用 vault 的 KV 密文引擎保存敏感信息, 供 ansible-playbook 使用.
准备
通过 ansible 提供的 lookup 类型插件 hashi_vault 可以读取保存在 vault 的 KV 引擎中的数据 1
ansible-doc -t lookup hashi_vault
pip install hvac
来安装 hashi_vault 插件.
本文不会介绍如何启用/配置 vault 密文引擎, 如何配置认证引擎给用户分发 token, 如何为用户设置策略进行权限管理等内容. 假定 vault 在 secret 路径下启用了 KV ver 2 的密文引擎, 用户可以正确获取 token 来访问需要的数据.
使用 KV ver2 引擎的好处是支持多版本, 方便追溯.
示例: rotate password
产生随机新密码保存到 vault 的 secret/dc1/local-password/root 路径下 1
vault kv put secret/dc1/local-password/root value=$(</dev/urandom tr -dc 'A-Za-z0-9+$%#<=>' | head -c12)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15vault kv get secret/dc1/local-password/root
====== Metadata ======
Key Value
--- -----
created_time 2020-06-07T19:30:14.490822153Z
deletion_time n/a
destroyed false
version 1
==== Data ====
Key Value
--- -----
value bqVYx5#VU$0f
ansible 角色定义的 rotate_local_password/tasks/main.yml: 1
2
3
4
5
6---
# tasks file for rotate_local_password
- name: change target account password
user:
name: "{{ account }}"
password: "{{ new_password | password_hash('sha512', 65534 | random(seed=inventory_hostname) | string) }}"
Playbook:
1 | - name: rotate root password in all hosts |
ansible 需要获取用户的 vault token 来访问 vault 数据. 将登录 vault 的用户名/密码或者 vault token 直接写入 lookup 参数显然不合适. 有几种可行的方法:
- 用户先执行
vault login ...
通过认证引擎登录 vault, 这样就会在家目录下产生 .vault-token 文件, 用于和 vault 交互 - 用户将 vault token 通过环境变量 VAULT_TOKEN 导出, 使用此环境变量和 vault 交互
示例: rotate certificate
将更新的 https 证书和私钥保存到 vault 中, 然后通过 ansible 分发到服务器上. 1
2vault kv put secret/dc1/rproxy/certificate value=@fullchain.pem
vault kv put secret/dc1/rproxy/privatekey value=@privkey.pem
查看内容 1
2
3vault kv get -field=data -format=json secret/dc1/rproxy/certificate | jq -r .value
vault kv get -field=data -format=json secret/dc1/rproxy/privatekey | jq -r .value
# 输出略
ansible 角色定义的 deploy_certificate/tasks/main.yml: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17- name: copy certificate content
copy:
content: "{{ certificate_content }}"
dest: /etc/nginx/ssl/server.crt
owner: root
group: root
mode: '0644'
notify: reload nginx
- name: copy key content
copy:
content: "{{ key_content }}"
dest: /etc/nginx/ssl/server.key
owner: root
group: root
mode: '0644'
notify: reload nginx
Playbook: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16---
- name: deploy certificate and private key to all reverse proxies
hosts:
- rproxy
serial: 1
vars:
vault_param: "url=https://vault.svc.vopsdev.com:8200 ca_cert=/etc/pki/ca-trust/source/anchors/private-trusted-bundle.pem"
cert_param: "secret=secret/data/dc1/rproxy/certificate:data {{ vault_param }}"
key_param: "secret=secret/data/dc1/rproxy/privatekey:data {{ vault_param }}"
roles:
- role: deploy_certificate
vars:
certificate_content: "{{ lookup('hashi_vault', cert_param)['value'] }}"
key_content: "{{ lookup('hashi_vault', key_param)['value'] }}"serial: 1
来控制并行度, 确保一台主机上证书更新完成, 服务重新加载配置后再执行下一台 host.