ansible-vault — リポジトリに入れてよいシークレット
一言でいうと
Vaultは、シークレットをリポジトリの外に出すツールではなく、リポジトリの中に暗号文として置いて、実行時にだけ復号するツールです。
なぜ必要なのか
.envを.gitignoreに入れたからといって、安全にはなりません。環境変数は、同じホストで/proc/PID/environからそのまま読め、クラッシュレポートやCIログやコンテナイメージのレイヤーに載って外へ出ていきます。さらに、もっと重要な問いが残ります。シークレットが漏れたとき、何分以内にそのキーを取り消して交換できるかという問いです。ほとんどのチームでは、答えは「わかりません」です。どこに何セットコピーされているのかわからないからです。
Vaultは、この問題の半分を解決します。シークレットがコードと同じリポジトリにあるので、どこに何個あるかを数えられ、レビューを経て、履歴が残ります。残りの半分(自動ローテーション、短命の認証情報)は、外部のシークレットマネージャーの領域です。
どう動くのか
Vaultは、AES256の共通鍵暗号です。鍵はパスワード1つで、そのパスワードは、ファイルまたはプロンプトで渡します。
| コマンド | 役割 |
|---|---|
ansible-vault create |
新しい暗号化ファイルを作成 |
ansible-vault encrypt |
既存の平文ファイルを暗号化 |
ansible-vault view |
復号して表示するだけ |
ansible-vault edit |
復号 → 編集 → 再暗号化 |
ansible-vault rekey |
新しいパスワードで再暗号化 |
ansible-vault encrypt_string |
値1つだけを暗号化して、YAMLにインラインで埋め込む |
暗号化されたファイルの1行目は、$ANSIBLE_VAULT;1.1;AES256の形式のヘッダーです。この行がなければ、そのファイルは平文です。監査のとき、最初に見るのがこのヘッダーです。
encrypt_stringは、ファイル全体ではなく値1つだけを暗号化します。そうすると、変数ファイルの残りは平文なのでdiffを読むことができ、シークレットだけが暗号文として残ります。実務では、こちらのほうがよく使われます。
--vault-idは、複数の鍵をラベルで区別します。prod@파일(プレースホルダーはファイルです)のように書くと、ヘッダーにラベルが埋め込まれるので、本番の鍵と開発の鍵を混ぜて使って事故が起きるのを防げます。
シークレットを扱うタスクには、必ずno_log: trueを付けます。そうしないと、せっかく暗号化した値が、実行ログに平文で出力されます。ログ収集システムは、たいていアプリケーションよりアクセス権限が広いです。
現場での姿
1つ目は、パスワードファイルの場所です。.vault_passをリポジトリの中に置くのは、錠のすぐ隣に鍵を貼り付けておくのと同じです。必ず.gitignoreに入れ、権限を600にします。
2つ目は、取り消しが先だということです。シークレットが平文でコミットされているのを見つけたら、順序は取り消し → 影響調査 → 履歴の整理です。履歴の書き換えから始めるのは、順序が間違っています。公開された値は、すでに自動スキャナーに収集されているかもしれず、フォークやクローンやCIキャッシュに残ります。履歴の整理は、流出を元に戻す措置ではなく、再発を減らすための衛生管理的な作業です。
3つ目は、rekeyは古い鍵を無効にするということです。rekeyのあとは、古いパスワードでそのファイルを開けません。複数の人が使う鍵なら、交換の時点を調整する必要があります。
シークレットが漏れるほかの経路
ファイルを暗号化してno_logを付けても、値が流れ出る経路がまだいくつか残っています。点検リストとして書いておくとよいでしょう。
コマンドライン引数: 値をコマンドの引数として渡すと、同じホストの別のユーザーが、プロセス一覧でそのまま見られます。ファイルや標準入力で渡す方法があれば、そちらを使います。
シェルの履歴: 人が手で入力したコマンドに値が入っていると、そのままファイルに残ります。このファイルは、たいていバックアップにも含まれます。
エラーメッセージと例外: 失敗したリクエストをまるごとログに残すコードがよくありますが、その中に認証ヘッダーが入っています。ログにリクエストを残すときは、何を消すかを事前に決めておく必要があり、そのリストは、新しいヘッダーができるたびに更新されなければなりません。
中間ファイル: テンプレートで設定ファイルを作る過程で、一時ファイルがデフォルトの権限で一時的に置かれてから削除されることがあります。ごく短い間でも、その時点でほかのプロセスが読める可能性があるので、最初から狭い権限で作る必要があります。
バックアップとスナップショット: 暗号化されたファイルはそのままバックアップされても問題ありませんが、復号された結果物がサーバーに残っていると、それがバックアップに載ります。そのため、デプロイの結果物の権限と場所を決めておくことが、シークレット管理の一部です。
ここからさらに一歩進むと、検知です。リポジトリにシークレットが平文で入ってくるのを、コミットの段階やCIで捕まえるツールがあり、これを仕掛けておけば、前に述べた「取り消し → 調査 → 整理」を経験せずに済みます。人の注意力に頼るルールはいつか失敗しますが、コミットを止める検査は毎回動作します。
次のラボですること
パスワードファイルを安全に作り、変数ファイルをまるごと暗号化し、値1つだけをインラインで暗号化します。プレイブックで復号された値を使って、権限600のファイルを作り、no_logでログへの流出を防ぎます。rekeyとvault-idを経て、最後にリポジトリ全体を監査します。