本記事はAP Tech Blog Week Vol.4のものになります。
今年の4月にリリースされた HashiCorp Vault v1.16.1 で Secrets Sync という機能が登場しました。
Secrets Sync は Vault に格納したシークレットを他のシークレットマネージャーに同期できる機能です。 この機能を使うことでシークレットの一元管理がより便利になり、シークレット管理におけるガバナンスやコンプライアンスの強化に役立ちます。
対応している同期先のシークレットマネージャーは次のとおりです。
- AWS Secret Manager
- Google Cloud Secret Manager
- Microsoft Azure Key Vault
- GitHub Actions
- Vercel
注意点としては、Secrets Sync は Vault Enterprise 限定の機能となっており、コミュニティ版の Vault は Secret Sync を使えません。
また、マネージドサービスの HCP Vault は Enterprise 相当の Vault クラスタを提供しているのですが、本記事の執筆時点では HCP Vault の Secrets Sync は無効化されています。
Secrets Sync がベータ公開されていたときには HCP Vault でも試せていましたが、現在は Vault Enterprise のみ利用できます。
【追記】2024年11月11日にリリースされた v1.18.1 で HCP Vault Dedicated でも Secrets Sync が正式に利用できるようになりました!
Secrets Sync の事前準備
Secrets Sync はシークレットマネージャーに対して複数同時に同期できます。 そこで今回は、Secrets Sync を使って私が普段利用している Microsoft Azure Key Vault と GitHub Actions に HashiCorp Vault のシークレットを同期してみます。
まずは Secrets Sync を試すための準備をします。 なお、本投稿では各サービスについて Secrets Sync に必要な設定のみ記載し、詳しい構築手順については記載しませんのでご了承ください。
Vault Enterprise
シークレットの同期元となる HashiCorp Vault Enterprise に Secrets Sync を設定します。
Vault は初期状態では Secrets Sync が無効化されているため、CLI で Vault にログインしてエンドポイント sys/activation-flags/secrets-sync/activate
から有効化します。
$ vault write -f sys/activation-flags/secrets-sync/activate Key Value --- ----- activated [secrets-sync] unactivated []
つぎに、同期対象となるシークレットを作成します。
今回はシークレットエンジンから設定し、my-secret
というシークレットの中に Key が 'foo'、Value が 'bar' の値を設定しました。
# kv-v2 シークレットエンジンの有効化 $ vault secrets enable -path='sync-demo-kv' kv-v2 Success! Enabled the kv-v2 secrets engine at: sync-demo-kv/ # シークレットを追加 $ vault kv put -mount='sync-demo-kv' my-secret foo='bar' ======= Secret Path ======= sync-demo-kv/data/my-secret ======= Metadata ======= Key Value --- ----- created_time 2024-08-30T12:11:01.025870529Z custom_metadata <nil> deletion_time n/a destroyed false version 1
Azure Key Vault
1つ目のシークレット同期先として Azure Key Vault を用意します。
また、Vault クラスタが Azure Key Vault のシークレットを操作できるようにするために Microsoft Entra ID のサービスプリンシパルを作成し、サービスプリンシパルに対して Azure Key Vault の [Key Vault Secrets Officer] ロールを付与します。
Azure Key Vault とサービスプリンシパルの用意ができたら、それらの情報を使って Vault にシークレットの同期先を設定します。
$ vault write sys/sync/destinations/azure-kv/sync-demo \ key_vault_uri="$KEY_VAULT_URI" \ client_id="$CLIENT_ID" \ client_secret="$CLIENT_SECRET" \ tenant_id="$TENANT_ID" Key Value --- ----- connection_details map[client_id:00000000-0000-0000-0000-000000000000 client_secret:***** key_vault_uri:https://secretssync.vault.azure.net/ tenant_id:00000000-0000-0000-0000-000000000000] name sync-demo options map[custom_tags:map[] granularity_level:secret-path secret_name_template:vault-{{ .MountAccessor | replace "_" "-" }}-{{ .SecretPath }}] type azure-kv
Azure Key Vault への同期準備は以上です。
GitHub Actions
2つ目のシークレット同期先として GitHub リポジトリとパーソナルアクセストークンを用意します。 リポジトリは [Private] に設定しても Secrets Sync はシークレットを同期できます。
パーソナルアクセストークンには [Fine-grained personal access tokens] を利用し、トークンでシークレットを更新できるよう [Repository permissions] の [Secrets] に Read and write
を設定します。
GitHub リポジトリとパーソナルアクセストークンの用意ができたら、それらの情報を使って Vault にシークレットの同期先を設定します。
$ vault write sys/sync/destinations/gh/sync-demo \ access_token="$ACCESS_TOKEN" \ repository_owner="$OWNER" \ repository_name="$NAME" Key Value --- ----- connection_details map[access_token:***** repository_name:vault-secrets-sync repository_owner:apc-nonoshita secrets_location:*****] name sync-demo options map[custom_tags:map[] granularity_level:secret-key secret_name_template:VAULT_{{ .MountAccessor | uppercase }}_{{ .SecretPath | uppercase }}_{{ .SecretKey | uppercase }}] type gh
GitHub への同期準備は以上です。
シークレットの同期を開始してみる
それでは Secrets Sync を試してみましょう!
Vault から Azure Key Vault、GitHub へのシークレットを同期を開始します。
同期を始めるには Azure Key Vault と GitHub それぞれの同期先設定に対してどのシークレットを同期するか指定します。
対象のシークレットは事前に準備したマウントパス sync-demo-kv
のシークレット my-secret
です。
まずは Azure Key Vault から同期します。
$ vault write sys/sync/destinations/azure-kv/sync-demo/associations/set \ mount='sync-demo-kv' \ secret_name='my-secret' Key Value --- ----- associated_secrets map[kv_5a8d942c/my-secret:map[accessor:kv_5a8d942c external_name:vault-kv-5a8d942c-my-secret last_operation:Write mount:sync-demo-kv secret_name:my-secret sync_status:SYNCED updated_at:2024-08-30T21:12:19.951151528+09:00]] store_name sync-demo store_type azure-kv
Azure ポータルから Azure Key Vault を確認するとシークレットが追加されていました。
追加されたシークレットの値を確認すると {"foo":"bar"}
と登録されていました。
続いて GitHub の同期を始めます。
$ vault write sys/sync/destinations/gh/sync-demo/associations/set \ mount=sync-demo-kv \ secret_name=my-secret Key Value --- ----- associated_secrets map[kv_5a8d942c/my-secret/foo:map[accessor:kv_5a8d942c external_name:VAULT_KV_5A8D942C_MY_SECRET_FOO last_operation:Write mount:sync-demo-kv secret_name:my-secret sub_key:foo sync_status:SYNCED updated_at:2024-08-30T21:35:16.738659733+09:00]] store_name sync-demo store_type gh
GitHub リポジトリのシークレット管理画面を見るとシークレットが追加されていました。
GitHub の場合は登録したシークレットの値を参照できません。
GitHub Actions でシークレットを表示してみると bar
登録されていました。
シークレットをローテーションしてみる
次にシークレットの値を bar
から baz
に変更、シークレットをローテーションしてみます。
$ vault kv put -mount=sync-demo-kv my-secret foo=baz ======= Secret Path ======= sync-demo-kv/data/my-secret ======= Metadata ======= Key Value --- ----- created_time 2024-08-30T12:52:19.330632681Z custom_metadata <nil> deletion_time n/a destroyed false version 2
Vault 側のシークレットを更新してすぐに Azure Key Vault と GitHub を確認したところ、両者ともシークレットの値が更新されていました。 Vault から同期先には即座に新しいシークレットの情報が反映されるようです。
シークレットに Key-Value を追加してみる
次に、my-secret
シークレットに新しい Key-Value hoge=fuga
を追加してみます。
$ vault kv patch -mount=sync-demo-kv my-secret hoge=fuga ======= Secret Path ======= sync-demo-kv/data/my-secret ======= Metadata ======= Key Value --- ----- created_time 2024-08-30T13:00:27.595736074Z custom_metadata <nil> deletion_time n/a destroyed false version 3 $ vault kv get -mount=sync-demo-kv my-secret ======= Secret Path ======= sync-demo-kv/data/my-secret ======= Metadata ======= Key Value --- ----- created_time 2024-08-30T13:00:27.595736074Z custom_metadata <nil> deletion_time n/a destroyed false version 3 ==== Data ==== Key Value --- ----- foo baz hoge fuga
Azure Key Vault ではシークレットのバージョンが追加され、シークレットの値も {"foo":"baz","hoge":"fuga"}
と更新されていました。
一方、GitHub には新たにシークレットが追加されました。
GitHub ではシークレットの持ち方が Vault と異なるため、Vault のシークレットのキー名が GitHub のシークレット名に組み込まれて Key-Value が分割されてシークレットが管理されます。 そのため、Vault 側で Key-Value を増やした場合は GitHub のシークレットが増えます。
シークレットを削除してみる
それでは Vault から同期対象のシークレットを削除するとどうなるでしょうか。
my-secret
シークレットを削除してみます。
$ vault kv delete -mount=sync-demo-kv my-secret Success! Data deleted (if it existed) at: sync-demo-kv/data/my-secret
すると、Azure Key Vault と GitHub ともにシークレットが削除されました。 シークレットの削除も即座に同期されるようです。
シークレットを復元させてみる
それでは先ほど Vault で削除したシークレットを復元させてみましょう。
kv-v2 シークレットエンジンではメタデータを削除していなければシークレットデータを復元できます。 復元させるとシークレットのバージョンは先ほど続きの '4' になっています。
$ vault kv put -mount=sync-demo-kv my-secret foo=bar ======= Secret Path ======= sync-demo-kv/data/my-secret ======= Metadata ======= Key Value --- ----- created_time 2024-08-30T13:15:39.665142806Z custom_metadata <nil> deletion_time n/a destroyed false version 4
Azure Key Vault も GitHub もシークレット同期開始時と同じシークレットが作られていました。 一度同期設定をしたシークレットであれば、シークレット削除した場合でも Vault で復元すると同期先にも再度シークレットが同期されるようです。
Vault にないシークレットは設定できない
ここまでの動作確認は 「Vault シークレット作成 → 同期設定」という流れでシークレットを同期していました。 この手順であれば一度同期設定をすると「シークレットの削除 → 復元」が可能であることも確認できました。
しかし、Vault にないシークレットに対して同期設定しようとすると以下のようにエラーになってしまいました。 Secrets Sync は「同期設定 → Vault シークレット作成」という流れでは同期できないようです。
$ vault write sys/sync/destinations/azure-kv/sync-demo/associations/set \ mount='sync-demo-kv' \ secret_name='my-secret2' Error writing data to sys/sync/destinations/azure-kv/sync-demo/associations/set: Error making API request. URL: PUT http://127.0.0.1:8200/v1/sys/sync/destinations/azure-kv/sync-demo/associations/set Code: 400. Errors: * couldn't set association because the secret referenced by mount "sync-demo-kv" and name "my-secret2" does not exist
おわりに
今回は HashiCorp Vault に追加された Secrets Sync というシークレットを同期する機能を使って、Azure Key Vault と GitHub にシークレットを同期してみました。
現在は Vault Enterprise 限定の機能ではありますが、Vault でシークレットを一元管理するメリットが大幅に増える機能追加だと思います。
今後もシークレット同期先として選べるサービスを増やしていく予定らしいので、HCP Vault で使えるようになる日が待ち遠しいですね!