Day 28:Ansible Vault

昨天写完 playbook 之後,有其中一个问题是需要手动输入 root 的密码,但若是所有机敏资料都要手动输入的话,那怎麽叫自动化?因此今天想讲的是 Ansible 有关这方面的内容。

become password

首先先来看看,Ansible 可以如何取得使用 become 的时候需要的密码。目前的版本中可以分成两种:

  • --ask-become-pass / -K:在 ansible-playbook 加上这个参数,则执行前我们可以手动输入密码。
  • ansible_become_password:设定这个变数,则 Ansible 就会自动使用它作为密码。

若使用第一种,就我目前能想到的只能够透过 pipe 把密码丢进 ansible-playbook 的输入,不过这样需要明文把密码存下来,另外,直接把这种机敏资料打在命令通常不是什麽好主意。还有一个问题是,我们的 host 可能不是使用相同的密码,这个选项应该没办法处理多个密码的情形。

第二种的情形则稍微好点,透过变数的话,我们就可以帮不同 host 设定不同的变数,而且有更多手法可以取得变数(环境变数、档案等),相对来说较为安全。

但是,就算另外存下来了,我们的档案也是明文储存的,为此 Ansible 提供了 Ansible Valut 这个工具,来帮助我们加密资料。

Ansible Vault

那麽该如何使用它呢?我们可以透过 ansible-vault 这个命令来做到。例如说我已经有写好的档案(secret),现在希望把它加密,指令如下:

ansible-vault encrypt secret

输入密码後就完成加密了,内容会变成类似这样:

$ANSIBLE_VAULT;1.1;AES256
65353031363236346136626232376531623736306135356138636364373531633934383738633766
6563646362346563373466653033396566616361626636320a623936326263366632666433393836
30356139623331633631366163363930303232393434653132623131613730383461353266366666
6565316137346536330a326339636366323736666161623430346239626538343866663663646261
6635

那麽该如何解密呢,ansible-vault 有提供 editviewdecrypt 等子命令,都可以帮助我们解开加密过後的档案,另外,密码除了可以透过 stdin 输入以外,这些命令都有提供 --vault-password-file / --vault-pass-file 的参数,让我们可以透过档案输入密码。

使用加密过的档案

那麽接下来就试试看实际在 playbook 里面使用被 vault 加密过後的档案吧,首先创建一个 secret.yml,并在里面设定一个变数 secret

echo "secret: SuperSecretString!" > secret.yml

接着将他加密。

ansible-vault encrypt secret.yml

然後就会得到加密过後的档案了,接着我们还需要定义 playbook,来尝试取用里面的值。

---
- name: Read secret from encrypted vars file
  hosts: local
  vars_files:
    - secret.yml
  tasks:
    - name: Print secret
      ansible.builtin.debug:
        var: secret

这边使用 vars_files 来宣告说这个 play 有一些变数定义在外部档案内,并且在後面用 ansible.builtin.debug 尝试把变数印出来。不过需要注意,在实际应用的场合,是不应该将这些机敏资料印出来的,反而应该要在有用到的 task 加上 no_log: yes 来避免意外的泄漏。

另存成 vault-test.yml 後执行 ansible-playbook vault-test.yml --ask-vault-pass,输入密码就能看到我们成功的取得 secret 这变数了。

管理多组密码

有些时候,我们会因为要做更严格的权限区分,或是分离环境等原因,希望可以有多组密码来管理这些被加密的内容。在 Ansible Vault 里面这可以透过指定 id 来达成(其实不一定要,後面会细讲)。在 ansible-vault 的不少命令中都能看到 --vault-id 这个参数,可以替我们的加密内容附上 id 的资讯。

以加密为例,尝试使用 ansible-vault encrypt --vault-id test@prompt secret.yml 加密与前面例子的同样内容,结果会长得像这样:

$ANSIBLE_VAULT;1.2;AES256;test
38623230623963636634323564346130636161363833643136656334636632353932336164353339
3231323631646632653634653232383939313936643636300a653336303863613266333732343637
62643934643538306666393461343432396439383339626464323766646365323661316164346331
6466663532326365330a653065373237386537626534626262326439326339623037333236643230
39303636653265306239613861373732666230373564376131616461343461643939

可以发现第一行的 header 长得稍微有点不同,後面多了一个 test 的字串,这个位置就表示这段内容的 id。关於 --vault-id 在不同情况下意义可能不大一样,但完整的形式是像 <id>@<password-source> 这样,@ 之前我们指定 id,後面指定密码的来源,有以下选项:

  • prompt:透过 stdin 输入。
  • <file>:从指定的档案里面去读密码进来。
  • <script>:除了直接档案读取,Ansible Vault 还支援使用第三方工具来取得 vault 密码,不过需要遵守以下规范(来源),另外附上一个范例作为参考:
    • 档名结尾需要是 client 或是 client.EXTENSION
    • 需要是可执行档
    • 关於脚本本身
      • 将密码输出到 stdout
      • 接受 --vault-id 做为 CLI 参数
      • 若有其他 prompt,需输出到 stderr

处理完加密之後,重复执行跟之前同样的那份 playbook,不过稍微更改一下参数,变成指定 vault id:ansible-playbook vault-test.yml --vault-id test@prompt。执行後一样可以正常拿到被加密的变数:

不过若是这边在执行时,使用 --ask-vault-pass,并且输入密码的话,也是可以正常执行的。这点感觉还挺违反直觉的,让我们来看看官方文件怎麽说:

Ansible attempts to decrypt vault content with each password. The password with the same label as the encrypted data will be tried first, after that each vault secret will be tried in the order they were provided on the command line.
from Ansible doc

原来,我们指定的 id 只是在建议 Ansible Vault 使用这些指定的密码来解密,但是当 Ansible 解密失败的时候,他会将 CLI 传进去的所有密码一一尝试拿来解密。因此其实就算我们 id 打错或不提供,Ansible Vault 还是可以成功解密的,

那麽 id 到底有什麽好处?关於这点我并没有查到相关的资料,就我目前的认知大概就是可以稍微加速解密的速度,减少解密尝试的次数。不过提到加速,官方文件上有提到,当需要解密的内容较多的时候,可能会有较明显的延迟,建议安装 cryptography 这个 python 套件。

pip install cryptography

小结

今天稍微研究了一下 Ansible Vault,不过这并不是 Ansible 唯一管理机敏资料的方式,有些 plugin 也是可以达到类似目的(例如 community.hashi_vault.hashi_vault),要选择那个具体还是要看专案。


<<:  Day 27 - 利用 WireGuard 建立点对点隧道

>>:  Day 27 - 用 canvas 模拟手机图型解锁

day7 来管理 firewall 吧 (雷)没人知道答案的问题

来部落格看图文并茂文章 补觉鸣诗 firewall 跟 switch 一样,也是存在各种厂牌 好在没...

JS 35 - 用 input 选择图片後显示预览图

大家好! 今天我们要实作的是,用 input 选择图片并在画面上显示预览图。 我们进入今天的主题吧!...

我们的基因体时代-AI, Data和生物资讯 Day01- 超越摩尔定律的资料增长

这个月的规划贴在这边文章中我们的基因体时代-AI, Data和生物资讯 Overview,也会持续调...

AI ninja project [day 27] QLattice --进阶分类

这一篇与[day25]的主要差异在於资料集以及应用上的不同, 参考的官方攻略: https://do...

【从零开始的 C 语言笔记】第八篇-printf 介绍与应用

不怎麽重要的前言 上一篇我们介绍了与输入输出格式相关的语法,想必大家应该多少知道要怎麽使用了,如果有...