- はじめに
- 検証環境
- 検証の題材のPlaybook
- Ansible Vaultの基本的な仕組み
- Vault Passwordとは
- 暗号化の対象
- Ansibleのベストプラクティス
- Vault IDとは
- 付録:ファイルレベルの暗号化のサブコマンド
- 参考
はじめに
Ansible Vaultの使い方を調べてみたので、その結果を残しておく。
Ansibleを利用してサーバ構築を行う際には、パスワードなどのセンシティブな情報をhost_vars
やgroup_vars
に配置することがある。
Ansible Vaultを利用すると、このような秘密にしたい情報を暗号化しておくことができる。
検証環境
$ cat /etc/os-release | grep PRETTY_NAME PRETTY_NAME="Ubuntu 20.04 LTS" $ ansible --version ansible 2.9.7 config file = None configured module search path = ['/home/redj/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] ansible python module location = /home/redj/.local/lib/python3.8/site-packages/ansible executable location = /home/redj/.local/bin/ansible python version = 3.8.2 (default, Mar 13 2020, 10:14:16) [GCC 9.3.0]
検証の題材のPlaybook
まずは、Ansible Vaultによる暗号化/復号化の検証の題材にするPlaybookを示す。 ファイル構成は以下。
vault-test |-- host_vars | `-- localhost.yml `-- playbook.yml
今回の検証ではhost_vars/localhost.yml
の中に秘密情報が入っていると仮定し、その秘密情報をAnsible Vaultで暗号化/復号化してみる。
このファイルの内容を以下に示す。
foo: FOO bar: BAR
また、Playbook実行のエントリーポイントとなるplaybook.yml
の内容を以下に示す。
- hosts: localhost connection: local gather_facts: no tasks: - debug: msg: "foo={{ foo }}, bar={{ bar }}"
変数foo
とbar
の内容を表示しているだけの単純なもの。
暗号化と復号化の検証さえできればよいので、ローカルコネクションで済ませている。
そして、このPlaybookを実行すると、以下のようになる。
$ ansible-playbook playbook.yml [WARNING]: (……インベントリの指定がないという指摘なので、警告は無視する……) PLAY [localhost] ************************************************************************************************** TASK [debug] ****************************************************************************************************** ok: [localhost] => { "msg": "foo=FOO, bar=BAR" } PLAY RECAP ******************************************************************************************************** localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
出力結果に"msg": "foo=FOO, bar=BAR"
とあり、期待通り変数展開されていることが分かる。
今回の検証では、Ansible Vaultによる暗号化/復号化を行っても、同様に期待通りに変数展開されることを確認する。
Ansible Vaultの基本的な仕組み
秘密にしたい情報は、コマンドansible-vault
を利用して事前に暗号化しておく。
暗号化に際して、コマンドansible-vault
はVault Passwordと呼ばれる共通鍵(自分で決めた任意の文字列)を利用する。
図示すると、以下のようになる。
そして、その暗号化された情報をhost_vars
などに配置して、コマンドansible-playbook
でPlaybookを実行する。
暗号化された情報は、共通鍵であるVault Passwordを利用して、Playbookの実行中に自動的に復号化される。
図示すると、以下のようになる。
Vault Passwordとは
Vault Passwordとは、Ansible Vaultが暗号化と復号化で利用する共通鍵で、利用者が自分で決める任意の文字列である。
通常は、Vault Passwordをファイルに書き込んでおいて、コマンドラインパラメータでファイルパスを指定することになる。
例えば、Vault Passwordがhogehoge
の場合は、単に以下のようなファイルtest_vault_password
を準備すればよい。
$ echo -n "hogehoge" > test_vault_password $ cat test_vault_password hogehoge
特定のコマンドラインパラメータを指定することで、実行中に対話的にVault Passwordを入力することもできるが、そういった方法はここでは言及しない。
暗号化の対象
Ansible Vaultの暗号化の対象には、以下の2種類がある。
- ファイルレベルの暗号化 (File-level encryption)
- 変数レベルの暗号化 (Variable-level encryption)
変数レベルの暗号化は、YAMLにおける値の暗号化を想定したものである。
ファイルレベルの暗号化
ファイル全体を暗号化する方式を、ファイルレベルの暗号化という。
以下のコマンドを実行することで、ファイルhost_vars/localhost.yml
全体が暗号化される。
$ ansible-vault encrypt \ --vault-id test@test_vault_password \ host_vars/localhost.yml
ここで、コマンドライン中の--vault-id test@test_vault_password
の部分でVault Passwordを指定しているが、詳細は後述する。
ファイルhost_vars/localhost.yml
の内容を確認してみると、以下のように書き換わっている。
$ANSIBLE_VAULT;1.2;AES256;test 34633833356339363463306664666561353663363934646334383338636465323765373235366231 6236613861336239626338383030613863303066636561320a653132643463393735306563653138 33343831653434373862653363373531313264383830313635636135373531373066643363303239 6631663031343063370a663230616362386162643436333036353035613033636531363233666335 35383962663038363431643631646631643934616164626362663366366134656362
これで暗号化は完了。
復号化の確認のために、以下のようにコマンドansible-playbook
を実行する。
Vault Passwordは、暗号化した時と同様にコマンドラインパラメータ--vault-id
で指定する。
$ ansible-playbook \ --vault-id test@test_vault_password \ playbook.yml [WARNING]: (……警告は無視する……) PLAY [localhost] ************************************************************************************************** TASK [debug] ****************************************************************************************************** ok: [localhost] => { "msg": "foo=FOO, bar=BAR" } PLAY RECAP ******************************************************************************************************** localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
出力結果を確認すると、自動的にhost_vars/localhost.yml
が復号化されて、期待通りの変数展開が行われていることが確認できる。
変数レベルの暗号化
YAMLファイル中の個々の変数定義を暗号化する方式を、変数レベルの暗号化という。
以下のコマンドを実行することで、FOO
という値を暗号化することができる。
Vault Passwordはコマンドラインパラメータ--vault-id
で指定する。
$ ansible-vault encrypt_string \ --vault-id test@test_vault_password \ 'FOO' !vault | $ANSIBLE_VAULT;1.2;AES256;test 32393933393231336263333538323866633037616337663730633339303431313038343935383834 6237326433333437393366313039653065636630333837350a623339353136343537306132306663 63306433643336626533383733666439323836363136346635333230653765303965356364333438 6137656561313130330a346235646633366562343032326166663939343165346364396164393465 3138 Encryption successful
そして、以下のようにhost_vars/localhost.yml
にこの出力結果をコピペする。
foo: !vault | $ANSIBLE_VAULT;1.2;AES256;test 32393933393231336263333538323866633037616337663730633339303431313038343935383834 6237326433333437393366313039653065636630333837350a623339353136343537306132306663 63306433643336626533383733666439323836363136346635333230653765303965356364333438 6137656561313130330a346235646633366562343032326166663939343165346364396164393465 3138 bar: BAR
これで暗号化は完了。
復号化の確認のために、以下のようにコマンドansible-playbook
を、パラメータ--vault-id
付きで実行する。
$ ansible-playbook \ --vault-id test@test_vault_password \ playbook.yml [WARNING]: (……警告は無視する……) PLAY [localhost] ************************************************************************************************** TASK [debug] ****************************************************************************************************** ok: [localhost] => { "msg": "foo=FOO, bar=BAR" } PLAY RECAP ******************************************************************************************************** localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
出力結果を確認すると、自動的に変数foo
の値が復号化されて、期待通りの変数展開が行われていることが確認できる。
もちろん、変数foo
に加えて、変数bar
の値も暗号化しても、同様に期待通りに動作する。
余談だが、以下のようにコマンドラインパラメータ--name
を利用することで、YAMLのキーも出力結果に含まれるようになるので、コピペが少し楽になる。
$ ansible-vault encrypt_string \ --vault-id test@test_vault_password \ --name foo \ 'FOO' foo: !vault | $ANSIBLE_VAULT;1.2;AES256;test 34373064336233363430636134383135303233323333633834336337333966346662363964363635 3430326265333237313664386131363666636264303531370a303939376139313336333961313439 32356431316231303636623462613232323432346261316561666333323439623934313938666536 3530613033633963330a623038383834303866653339303331333930653832613431653062663830 6430 Encryption successful
また、複数の変数を一度に暗号化することもできるが、詳細は割愛する。
Ansibleのベストプラクティス
Ansibleのベストプラクティスでは、実際に利用される変数を含むファイルと、暗号化された変数を含むファイルの、2種類のファイルに分割する方式を提示している。
例えば、以下のようにhost_vars
(あるいはgroup_vars
)の中にホスト名(あるいはグループ名)と同じ名前のディレクトリを作成し、その中に実際に利用される変数を含むファイルvars
と、暗号化された変数を含むファイルvault
を配置する。
vault-test |-- host_vars | `-- localhost | |-- vars | `-- vault `-- playbook.yml
ファイルvars
の内容の例を以下に示す。
foo: "{{ vault_foo }}" bar: "{{ vault_bar }}"
ここで、変数名にプレフィックスvault_
を付与した変数は、ファイルvault
で暗号化された変数である。
ファイルvault
の内容の例を以下に示す。
ただし、説明の都合上ここでは平文で示しているが、本来は暗号化されている。
vault_foo: FOO vault_bar: BAR
つまり、Playbookなどの変数の利用者は、実際に利用される変数(例えばfoo
)を通して、暗号化された変数(例えばvault_foo
)を間接参照する方式である。
この方式の動機は、ファイルレベルの暗号化では変数名が判別不能なのだが、これによるPlaybook内の変数の検索性(grep)の低下を防ぐところにある。
ちなみに、ベストプラクティスの例では、ファイル名はvars
とvault
になっているが、ファイル名およびその個数は任意との記述がある。
Vault IDとは
ここでようやく--vault-id
で指定する、Vault IDを説明する。
なぜ最後の方で説明するかというと、無駄にややこしいから。
Vault IDとは、Playbook中で複数のVault Passwordを区別して利用するためのIDであり、そのフォーマットは以下の通り。
label@source
label
はVault Passwordに付けられた名前であり、source
はVault Passwordが保存されているファイルのパスである。
使い方の例を示す。
まずhost_vars/localhost.yml
について、変数レベルの暗号化を利用して、変数foo
をVault ID test@test_vault_password
で、変数bar
をVault ID other@other_vault_password
で暗号化したとする。
コマンドラインは以下の通り。
$ ansible-vault encrypt_string \ --vault-id test@test_vault_password \ --name foo \ 'FOO' (出力結果は省略) $ ansible-vault encrypt_string \ --vault-id other@other_vault_password \ --name bar \ 'BAR' (出力結果は省略)
この出力結果をhost_vars/localhost.yml
にコピペすると、内容は以下のようになる。
foo: !vault | $ANSIBLE_VAULT;1.2;AES256;test 63643064613338386334323730613439313164626338386464623033616138386662653462376133 (以下略) bar: !vault | $ANSIBLE_VAULT;1.2;AES256;other 34653962306238643839303634636438396239623838656631333562353138303865633962386564 (以下略)
ここで、変数foo
の暗号文の以下の部分を見てみると、末尾のtest
の部分が暗号化時に指定したラベルであり、また復号化でもこのラベルを指定することになる。
$ANSIBLE_VAULT;1.2;AES256;test
同様に変数bar
はother
というラベルが関連付けられていることが分かる。
このような複数のVault Passwordを利用して暗号化した情報を扱うPlaybookを実行するには、コマンドラインパラメータ--vault-id
を複数個指定すればよい。
$ ansible-playbook \ --vault-id test@test_vault_password \ --vault-id other@other_vault_password \ playbook.yml [WARNING]: (……警告は無視する……) PLAY [localhost] ************************************************************************************************** TASK [debug] ****************************************************************************************************** ok: [localhost] => { "msg": "foo=FOO, bar=BAR" } PLAY RECAP ******************************************************************************************************** localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
・・・ところが。
Ansible Vaultのドキュメントには以下のようにある。
By default the vault-id label is only a hint, any values encrypted with the password will be decrypted. The config option DEFAULT_VAULT_ID_MATCH can be set to require the vault id to match the vault ID used when the value was encrypted. This can reduce errors when different values are encrypted with different passwords.
以下に、個人的な主観を含めた意訳を示す。
デフォルトではVault IDのラベルはただのヒントであり、Vault Passwordで暗号化された値は手当たり次第に復号化されます。 設定オプションDEFAULT_VAULT_ID_MATCHをセットすることで、暗号化で指定したVault IDと一致するVault IDを要求することができます。 このデフォルトの挙動は、それぞれの値が異なるVault Passwordを利用して暗号化されている場合に、復号化のエラーを減らすことができます。
デフォルトの挙動が緩すぎて、ラベルの意味がないじゃない。。。
試しに、暗号化では指定していない適当なラベルでPlaybookを実行すると、確かに復号化が成功した。 ダメでしょう、この挙動は。
$ ansible-playbook \ --vault-id xxxxx@test_vault_password \ --vault-id yyyyy@other_vault_password \ playbook.yml [WARNING]: (……警告は無視する……) PLAY [localhost] ************************************************************************************************** TASK [debug] ****************************************************************************************************** ok: [localhost] => { "msg": "foo=FOO, bar=BAR" } PLAY RECAP ******************************************************************************************************** localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
付録:ファイルレベルの暗号化のサブコマンド
ファイルレベルの暗号化のサブコマンドは6つ存在していて、ややこしい。
ansible-vault create
ansible-vault edit
ansible-vault rekey
ansible-vault encrypt
ansible-vault decrypt
ansible-vault view
そのため、成果物との関係性を以下に図示してみた。
実際に実行するサブコマンドのほとんどは、encrypt
だと思う。
真面目に運用するなら、rekey
、decypt
、view
も時々利用すると思う。
個人的には、create
とedit
はまず使わない。
参考
Ansible User Guide -> Ansible Vault
Ansible User Guide -> Working With Playbooks -> Best Practices -> Variables and Vaults
Ansible User Guide -> How to build your inventory -> Organizing host and group variables