いまさら Ansible Vault (2): せめて開発・検証・本番環境の Vault パスワードを別々に

[前回の記事](https://qiita.com/3244/items/52911e4c8448ad433eb5)では Ansible Vault の基本的な機能を使って、以下の要件までを実現しました。

- 機密情報の変数（パスワードなど）を暗号化する
- ターゲットに配置する機密ファイル（秘密鍵など）を暗号化する
- Vault パスワードをスクリプトで管理する

しかし Vault パスワードが全環境で共通のため、開発メンバー全員が本番環境の機密情報も復号化できてしまいます。

そこで今回は、以下の要件を実現します。

- 開発 / 検証 / 本番環境で別々の Vault パスワードを使用する

参考: [Ansible Vault — Ansible Documentation](https://docs.ansible.com/ansible/2.9_ja/user_guide/vault.html)

- 確認環境:
  - Ansible 2.10.7
  - Ubuntu 20.04 / Debian 10 (buster)

# 環境ごとの Vault パスワードを設定する

## 前回: Vault パスワードが 1 つの場合 

[前回](https://qiita.com/3244/items/52911e4c8448ad433eb5)は以下のように唯一の Vault パスワードを設定したので、`ansible-vault` `ansible-playbook` などのコマンドが使用する Vault パスワードを指定する必要がありませんでした。

```shell:consts.env
ANSIBLE_VAULT_PASSWORD_FILE="/workspace/.vault_pass.sh"
```

## 今回: Vault パスワードが複数の場合 

しかし Vault パスワードが複数になると、どのパスワードを使って暗号化するかを指定したり、暗号化と同じパスワードを使って復号化する必要があります。

複数の Vault パスワードは以下のようにリストにして、環境変数 [ANSIBLE_VAULT_IDENTITY_LIST](https://docs.ansible.com/ansible/2.9_ja/reference_appendices/config.html#envvar-ANSIBLE_VAULT_IDENTITY_LIST) に設定します。

```shell:consts.env
ANSIBLE_VAULT_IDENTITY_LIST="dev@/workspace/vault_pass_dev.sh, stg@/workspace/vault_pass_stg.sh, prd@/workspace/vault_pass_prd.sh"
```

設定値は `{ID 1}@{スクリプト 1}, {ID 2}@{スクリプト 2}, ...` のように、Vault ID と スクリプトのペアをいくつでも指定できます。

上記の例で設定したリストの内容は以下のとおりです

- 環境ごとに使用する Vault パスワードの ID を「dev」「stg」「prd」とする
- それぞれの Vault パスワードを出力するスクリプトは、`/workspace/vault_pass_{Vault ID}.sh` とする

この設定によって、任意の Vault ID に紐づくスクリプトを呼び出して Vault パスワードを取得することができます。

参考:
[複数の vault パスワード — Ansible Vault — Ansible Documentation](https://docs.ansible.com/ansible/2.9_ja/user_guide/vault.html#id13)
[Vault IDs in Ansible 2.4 - Red Hat Learning Community](https://learn.redhat.com/t5/Automation-Management-Ansible/Vault-IDs-in-Ansible-2-4/td-p/1531)
[Ansible Configuration Settings — Ansible Documentation](https://docs.ansible.com/ansible/latest/reference_appendices/config.html)

:::note info
ここで環境変数を定義している *.env ファイルは、開発環境を Docker で構築する際に docker-compose.yml の env_file: に指定することを想定しています。
開発環境が Docker でない場合は ~/.bashrc などで環境変数を export するか、ansible.cfg で設定してください。
:::

# 環境ごとに Vault ID を指定して暗号化する

コマンド `ansible-vault encrypt` の `--encrypt-vault-id` オプションを使えば、暗号化するファイルごとに Vault ID を指定することができます。

以下の例では、環境名（= Vault ID）と同名のディレクトリ内に配置された vault という名前の平文ファイルを暗号化します。

```shell:
.
├── dev
│   └── group_vars
│       └── all
│           ├── vars
│           └── vault  # 暗号化する
├── stg
│   └── group_vars
│       └── all
│           ├── vars
│           └── vault  # 暗号化する
└── prd
    └── group_vars
        └── all
            ├── vars
            └── vault  # 暗号化する
```

変数ファイル vars と vault の関係は、前回の記事で紹介した公式ベストプラクティスのとおりです。

参考: [機密情報の変数（パスワードなど）を暗号化する](https://qiita.com/3244/items/52911e4c8448ad433eb5#%E6%A9%9F%E5%AF%86%E6%83%85%E5%A0%B1%E3%81%AE%E5%A4%89%E6%95%B0%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89%E3%81%AA%E3%81%A9%E3%82%92%E6%9A%97%E5%8F%B7%E5%8C%96%E3%81%99%E3%82%8B)

以下は環境と同名の Vault ID を `--encrypt-vault-id` に指定して、各環境の vault ファイルを暗号化する例です。

```shell:
for id in $(ls .); do
  # 環境ごとのディレクトリ名と Vault ID は同じ（dev|stg|prd）
  find ${id} -name "vault" -exec \
    ansible-vault encrypt --encrypt-vault-id ${id} {} +
done
```

`ansible-vault encrypt` コマンドは、`ANSIBLE_VAULT_IDENTITY_LIST` で Vault ID に紐付けられたスクリプトを呼び出して、取得した Vault パスワードを使って指定のファイルを暗号化します。

:::note warn
暗号化前の vault ファイルをコミットしてしまわないように注意しましょう。
:::

# 復号化に使用する Vault パスワード

各ファイルを復号化する時には、暗号化に使用したものと同じ Vault パスワードを使う必要があるのですが、`ansible-vault edit` や `ansible-playbook` などのコマンドでは Vault ID を指定する必要がありません。

実は暗号化されたファイルを見ると、

```
$ANSIBLE_VAULT;1.2;AES256;dev
34336462316665666231643965643533383165373964323663373036623034626237333361376231
6135346338376464643034376339323461316534323965370a346332343530626436663731333438
(略)
```

このように 1 行目の終わりに、暗号化に使用された Vault ID が記録されています。

`ansible-vault` `ansible-playbook` などのコマンドは、ファイルに記録された Vault ID と `ANSIBLE_VAULT_IDENTITY_LIST` の定義から、暗号化に使用されたものと同じ Vault パスワードを取得して復号化してくれます。

したがって、Vault ID やパスワードを環境ごとに区別して取り扱うのは、最初に暗号化する時だけです。
以後はこれらの違いを気にしないで `ansible-vault edit` や `ansible-playbook` を実行することができます。

# 次回の課題

以上で Vault パスワードを環境ごとに別々にできたのですが、まだ Vault パスワードを管理するために PC 内のファイルや環境変数を使用する必要があります。

そこで次回は、Vault パスワードをクラウドに保存して、Vault パスワード取得用スクリプトもクラウドから取得する処理に改良します。

前回: [いまさら Ansible Vault で変数とファイルを暗号化して、Vault パスワードをスクリプトで管理する - Qiita](https://qiita.com/3244/items/52911e4c8448ad433eb5)
次回: [いまさら Ansible Vault (3): Vault パスワードを環境ごとの AWS アカウントの SSM パラメータストアで管理する - Qiita](https://qiita.com/3244/items/4cf79dd140be6caa0c5b)