おうち K8s クラスタで HashiCorp Vault を動かしているのですが、Pod が再起動すると Vault が Seal 状態となってしまうので都度 Unseal Key を入力しています。 そろそろ煩わしさが限界なので Azure Key Vault と連携して Vault の Auto Unseal 機能を使ってみます。
Auto-unseal Vault using Azure Key Vault | Vault | HashiCorp Developer
Auto Unseal 設定
Auto Unseal を使うためには以下の設定が必要になります。
- Entra ID サービスプリンシパル
- Azure Key Vault
- Vault Config
サービスプリンシパル
Microsoft Entra ID に Vault の Auto Unseal で使用するサービスプリンシパルを作成します。
私の環境では Dynamic Secrets 用の Vault サービスプリンシパルを作成済みなので今回はこちらを流用します。 Vault サービスプリンシパルのシークレットは Vault がローテーションしてくれるのですが、Auto Uneal は Vault の設定ファイルに直接サービスプリンシパルのシークレットを記述する形式です。 そのため、サービスプリンシパルにもローテーションされない(有効期限を設定している)シークレットを作成します。
もしかしたら Auto Unseal 用のサービスプリンシパルを別途作成するのが安牌かもしれません。
Azure Key Vault
Azure Key Vault を用意して Auto Unseal 時に使用されるキー(今回は generated-key
という名前)を作成します。
そして、Vault サービスプリンシパルがシークレットを参照できるように組み込みロール Key Vault Secrets User
を設定します。
Vault
Vault on K8s は Helm を使ってインストール(実際には ArgoCD から呼び出す Kustomize で Helm を実行)していて、Vault の設定は ConfigMap vault-confg
が /vault/config/extraconfig-from-values.hcl
にマウントされています。
Azure Key Vault と連携して Auto Unseal を使う場合は以下のようなサービスプリンシパルのシークレットを含む設定を使用します。
seal "azurekeyvault" {
client_id = "00000000-0000-0000-0000-000000000000"
client_secret = "************************************"
tenant_id = "00000000-0000-0000-0000-000000000000"
vault_name = "key-vault-name"
key_name = "generated-key"
}
名前の通りですが、各項目は以下のとおり。
項目 | 値 |
---|---|
client_id | サービスプリンシパル のクライアント ID |
client_secret | サービスプリンシパルのシークレット |
tenant_id | Microsoft Entra テナント ID |
vault_name | Azure Key Vault のリソース名 |
key_name | Azure Key Vault 内のキー名 |
この設定を Vault が参照できるようにしてあげる必要があり Helm の values.yaml
で ConfigMap を上書きするやりかたがあるのですが、Git 管理している values.yaml
にサービスプリンシパルのシークレットを直接記入することはしたくありません。
このことは公式でも言及されており、Secret リソースをマウントして Vault に参照させることで回避可能なようです。
Run Vault on Kubernetes | Vault | HashiCorp Developer
じゃあその Secret リソース自体の管理はどうするの……という疑問が湧いてきます。 そもそも Secretリソースを Git 管理しなくていいようにするために Vault(そして Vault Secrets Operator)を導入しているので本末転倒な感じはありますが、一旦ここは目を瞑って Auto Unseal 用の Secret リソースは手動管理、所謂「運用でカバー」します。
まずは以下のコマンドで Auto Unseal の設定を含む Secret リソースを作成します。
cat << EOF > config.hcl
seal "azurekeyvault" {
client_id = "00000000-0000-0000-0000-000000000000"
client_secret = ""************************************""
tenant_id = "00000000-0000-0000-0000-000000000000"
vault_name = "key-vault-name"
key_name = "generated-key"
}
EOF
kubectl create secret generic vault-storage-config --from-file=config.hcl
次に Helm の values.yaml
に以下のような設定をして、Secret リソースをマウントします。
server:
volumes:
- name: userconfig-vault-storage-config
secret:
defaultMode: 420
secretName: vault-storage-config
volumeMounts:
- mountPath: /vault/userconfig/vault-storage-config
name: userconfig-vault-storage-config
readOnly: true
extraArgs: "-config=/vault/userconfig/vault-storage-config/config.hcl"
以上で Vault の設定は完了です。
上述のとおり Secret リソースは手動管理となっています。 もしかしたら Vault Secrets Operator を使ったらいい感じに Auto Unseal の Secret リソースも管理外にすることができるかもしれませんが、一旦今回はここまで。
Vault 移行
設定が完了したら Auto Seal が機能するか確認します。
Vault の初期構築段階で Auto Unseal を使う場合は特に気にする必要なく vault init
コマンドするだけで良いようですが、既に Vault が動いている環境で Auto Unseal を使う場合は Seal 設定の移行が必要です。
Seal/Unseal | Vault | HashiCorp Developer
Vault Pod を再起動すると Seal 状態になっているので vault operator unseal -migrate
コマンドを実行します。
いつも通り Unseal キーを 3 回入力すると Unseal されるとともに Auto Unseal が有効になります。
==> Vault server configuration:
Administrative Namespace:
Api Address: http://10.0.4.33:8200
Cgo: disabled
Cluster Address: https://vault-0.vault-internal:8201
Environment Variables: GODEBUG, HOME, HOSTNAME, HOST_IP, KUBERNETES_PORT, KUBERNETES_PORT_443_TCP, KUBERNETES_PORT_443_TCP_ADDR, KUBERNETES_PORT_443_TCP_PORT, KUBERNETES_PORT_443_TCP_PROTO, KUBERNETES_SERVICE_HOST, KUBERNETES_SERVICE_PORT, KUBERNETES_SERVICE_PORT_HTTPS, NAME, PATH, POD_IP, PWD, SHLVL, SKIP_CHOWN, SKIP_SETCAP, TERM, VAULT_ADDR, VAULT_API_ADDR, VAULT_CLUSTER_ADDR, VAULT_DISABLE_FILE_PERMISSIONS_CHECK, VAULT_K8S_NAMESPACE, VAULT_K8S_POD_NAME, VAULT_PORT, VAULT_PORT_8200_TCP, VAULT_PORT_8200_TCP_ADDR, VAULT_PORT_8200_TCP_PORT, VAULT_PORT_8200_TCP_PROTO, VAULT_PORT_8201_TCP, VAULT_PORT_8201_TCP_ADDR, VAULT_PORT_8201_TCP_PORT, VAULT_PORT_8201_TCP_PROTO, VAULT_SERVICE_HOST, VAULT_SERVICE_PORT, VAULT_SERVICE_PORT_HTTP, VAULT_SERVICE_PORT_HTTPS_INTERNAL, VERSION, container
Go Version: go1.21.1
Listener 1: tcp (addr: "[::]:8200", cluster address: "[::]:8201", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled")
Log Level:
Mlock: supported: true, enabled: false
Recovery Mode: false
Storage: file
Version: Vault v1.15.0, built 2023-09-22T16:53:10Z
Version Sha: b4d07277a6c5318bb50d3b94bbd6135dccb4c601
==> Vault server started! Log data will stream in below:
2023-11-20T18:32:49.634Z [INFO] proxy environment: http_proxy="" https_proxy="" no_proxy=""
2023-11-20T18:32:50.427Z [INFO] incrementing seal generation: generation=1
2023-11-20T18:32:50.429Z [WARN] core: entering seal migration mode; Vault will not automatically unseal even if using an autoseal: from_barrier_type=shamir to_barrier_type=azurekeyvault
2023-11-20T18:32:50.429Z [INFO] core: Initializing version history cache for core
2023-11-20T18:32:50.429Z [INFO] events: Starting event system
2023-11-20T18:34:28.654Z [INFO] core: unsealing using migration seal
2023-11-20T18:34:38.707Z [INFO] core: unsealing using migration seal
2023-11-20T18:34:43.661Z [INFO] core: unsealing using migration seal
2023-11-20T18:34:43.669Z [INFO] core.cluster-listener.tcp: starting listener: listener_address=[::]:8201
2023-11-20T18:34:43.669Z [INFO] core.cluster-listener: serving cluster requests: cluster_listen_address=[::]:8201
2023-11-20T18:34:43.670Z [INFO] core: seal migration initiated
2023-11-20T18:34:43.670Z [INFO] core: migrating from shamir to auto-unseal: to=azurekeyvault
2023-11-20T18:34:44.153Z [INFO] core: seal migration complete
2023-11-20T18:34:44.153Z [INFO] core: post-unseal setup starting
2023-11-20T18:34:44.155Z [INFO] core: loaded wrapping token key
2023-11-20T18:34:44.155Z [INFO] core: successfully setup plugin runtime catalog
2023-11-20T18:34:44.155Z [INFO] core: successfully setup plugin catalog: plugin-directory=""
2023-11-20T18:34:44.157Z [INFO] core: successfully mounted: type=system version="v1.15.0+builtin.vault" path=sys/ namespace="ID: root. Path: "
2023-11-20T18:34:44.158Z [INFO] core: successfully mounted: type=identity version="v1.15.0+builtin.vault" path=identity/ namespace="ID: root. Path: "
2023-11-20T18:34:44.158Z [INFO] core: successfully mounted: type=azure version="v0.16.3+builtin" path=azure/ namespace="ID: root. Path: "
2023-11-20T18:34:44.158Z [INFO] core: successfully mounted: type=cubbyhole version="v1.15.0+builtin.vault" path=cubbyhole/ namespace="ID: root. Path: "
2023-11-20T18:34:44.162Z [INFO] core: successfully mounted: type=token version="v1.15.0+builtin.vault" path=token/ namespace="ID: root. Path: "
2023-11-20T18:34:44.162Z [INFO] core: successfully mounted: type=kubernetes version="v0.17.1+builtin" path=kubernetes/ namespace="ID: root. Path: "
2023-11-20T18:34:44.163Z [INFO] rollback: Starting the rollback manager with 256 workers
2023-11-20T18:34:44.163Z [INFO] rollback: starting rollback manager
2023-11-20T18:34:44.163Z [INFO] core: restoring leases
2023-11-20T18:34:44.165Z [INFO] expiration: lease restore complete
2023-11-20T18:34:44.167Z [INFO] identity: entities restored
2023-11-20T18:34:44.167Z [INFO] identity: groups restored
2023-11-20T18:34:44.167Z [INFO] core: usage gauge collection is disabled
2023-11-20T18:34:44.251Z [INFO] core: post-unseal setup complete
2023-11-20T18:34:44.251Z [INFO] core: vault is unsealed
これ以降 Vault Pod が再起動しても自動的に Unseal されるので、頻繁に Pod が落ちる我が家の環境でもオペレーションなしで Vault が使えるようになりました。 めでたしめでたし。
Auto Unseal をやめる場合
Auto Unseal をやめてデフォルトの Shamir Seal(3 つの Unseal キーを入力する方式)に戻す場合、設定ファイルの seal ブロックに disabled = "true"
を追加して Pod 再起動後に vault operator unseal -migrate
コマンドを実行します。
seal ブロックをコメントアウトするだけだとエラーになって Pod が起動してきませんでした。
seal "azurekeyvault" {
disabled = "true"
client_id = "00000000-0000-0000-0000-000000000000"
client_secret = "************************************"
tenant_id = "00000000-0000-0000-0000-000000000000"
vault_name = "key-vault-name"
key_name = "generated-key"
}