昨天寫完 playbook 之後,有其中一個問題是需要手動輸入 root 的密碼,但若是所有機敏資料都要手動輸入的話,那怎麼叫自動化?因此今天想講的是 Ansible 有關這方面的內容。
首先先來看看,Ansible 可以如何取得使用 become
的時候需要的密碼。目前的版本中可以分成兩種:
--ask-become-pass
/ -K
:在 ansible-playbook
加上這個參數,則執行前我們可以手動輸入密碼。ansible_become_password
:設定這個變數,則 Ansible 就會自動使用它作為密碼。若使用第一種,就我目前能想到的只能夠透過 pipe 把密碼丟進 ansible-playbook
的輸入,不過這樣需要明文把密碼存下來,另外,直接把這種機敏資料打在命令通常不是什麼好主意。還有一個問題是,我們的 host 可能不是使用相同的密碼,這個選項應該沒辦法處理多個密碼的情形。
第二種的情形則稍微好點,透過變數的話,我們就可以幫不同 host 設定不同的變數,而且有更多手法可以取得變數(環境變數、檔案等),相對來說較為安全。
但是,就算另外存下來了,我們的檔案也是明文儲存的,為此 Ansible 提供了 Ansible Valut 這個工具,來幫助我們加密資料。
那麼該如何使用它呢?我們可以透過 ansible-vault
這個命令來做到。例如說我已經有寫好的檔案(secret
),現在希望把它加密,指令如下:
ansible-vault encrypt secret
輸入密碼後就完成加密了,內容會變成類似這樣:
$ANSIBLE_VAULT;1.1;AES256
65353031363236346136626232376531623736306135356138636364373531633934383738633766
6563646362346563373466653033396566616361626636320a623936326263366632666433393836
30356139623331633631366163363930303232393434653132623131613730383461353266366666
6565316137346536330a326339636366323736666161623430346239626538343866663663646261
6635
那麼該如何解密呢,ansible-vault
有提供 edit
、view
、decrypt
等子命令,都可以幫助我們解開加密過後的檔案,另外,密碼除了可以透過 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
--vault-id
做為 CLI 參數處理完加密之後,重複執行跟之前同樣的那份 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),要選擇那個具體還是要看專案。