HCP Vault Secrets / HCP Vault Dedicated / Vault Enterprise の違いと Terraform による管理方法

## はじめに

Vault は機密情報を安全に管理するためのツールですが、HCP Vault Secrets / HCP Vault Dedicated / Vault Enterprise 3 つの選択肢があります。
それぞれのユースケースや Terraform による管理方法には違いがあり、適切な選択が重要です。
本記事では、Terraform を用いた管理方法の違いに着目して、両者の特徴を比較します。

## Terraform を用いた管理の違い

Vault を Terraform で管理する場合、使用するプロバイダーが異なります。
これは、HCP Vault Dedicated / Vault Enterprise が独立した環境で Vault インスタンスを管理するのに対し、HCP Vault Secrets は HashiCorp Cloud Platform 上でマネージドサービスとして提供されるため、それぞれ異なる API と管理モデルを持つためです。

- **HCP Vault Dedicated / Vault Enterprise**: `vault` provider を使用
  - [Terraform Vault Provider](https://registry.terraform.io/providers/hashicorp/vault/latest)
- **HCP Vault Secrets**: `hcp` provider を使用
  - [Terraform HCP Provider](https://registry.terraform.io/providers/hashicorp/hcp/latest)

この違いが設定方法や運用方法にどのような影響を与えるのかを詳しく見ていきます。

## Terraform Provider の基本的な設定方法

### HCP Vault Dedicated / Vault Enterprise (`vault` provider)

トークンを用いて接続する場合は、[ドキュメント](https://registry.terraform.io/providers/hashicorp/vault/latest/docs)に沿って例えば下記のように provider を定義します。

```hcl
provider "vault" {
  address = "https://vault.example.com"
  token   = var.vault_token 
}
```
- `address`: Vault のエンドポイント URL
- `token`: 認証用のトークン

例えば KV secret engine の値を参照する場合は、[Data Source の vault_generic_secret](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/data-sources/generic_secret) を使用します。
`data` 属性に複数の Secret の値が含まれます。

```hcl
data "vault_generic_secret" "auth" {
  path = "kv_secrets_engine_name/secret_name"
}

resource ... {
  token = data.vault_generic_secret.auth.data["token"]
}
```

### HCP Vault Secrets (`hcp` provider)

クライアント ID とクライアントシークレットで接続する場合は、[ドキュメント](https://registry.terraform.io/providers/hashicorp/hcp/latest/docs)に沿って下記のように provider を定義します。

```hcl
provider "hcp" {
  client_id     = var.hcp_client_id
  client_secret = var.hcp_client_secret 
}
```

- `client_id` / `client_secret`: HCP 認証用のクレデンシャル

Vault Secret の値を参照するには [Data Source の hcp_vault_secrets_secret](https://registry.terraform.io/providers/hashicorp/hcp/latest/docs/data-sources/vault_secrets_secret) を使用します。
`secret_value` 属性に単一の Secret の値が含まれます。

```hcl
data "hcp_vault_secrets_secret" "auth" {
  app_name    = "vault-secrets-app-name"
  secret_name = "secret_name"
}

resource ... {
  token = data.hcp_vault_secrets_secret.auth.secret_value
}
```

:::note warn
どちらの場合も state 内に平文の Secret の値が記録されますので、state の権限管理を適切に実施する必要があります。
:::

## Dynamic Provider Credentials を用いた HCP Terraform との統合

前述のように provider の設定で静的なアクセスキーやシークレットを直接指定する場合、これらの認証情報が漏洩すると攻撃に利用されてしまうなど、セキュリティリスクを伴います。
一方で Dynamic Provider Credentials を使用すると、Terraform の実行環境と Vault の間で適切な信頼関係を構成することで、Terraform の実行時に安全に認証情報を取得できます。この方式では、実行時に必要最小限の権限を持つ一時的な認証情報が自動的に発行され、それを用いて接続する形となります。一時的な認証情報は信頼された実行環境でのみ取得可能なため、よりセキュアな運用が実現できます。
ただし、HCP Vault Dedicated / Vault Enterprise と HCP Vault Secrets では設定方法が異なる点に注意が必要です。
下記に HCP Terraform を Terraform の実行環境とする際の設定概要を記載します。

### HCP Vault Dedicated / Vault Enterprise (`vault` provider) の場合

[ドキュメント](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/vault-configuration#configure-vault)に記載された通り、Vault 側で JWT 認証バックエンドの有効化やポリシー、ロールの作成を実施します。
その上で、Vault に接続する Workspace に下記の環境変数を設定します。

|変数名|値|
|-|-|
|TFC_VAULT_PROVIDER_AUTH|"true"|
|TFC_VAULT_ADDR|Vault インスタンスのアドレス|
|TFC_VAULT_RUN_ROLE|Vault ロールの名前|


これにより、provider の設定から静的な認証情報を完全に排除し、よりセキュアな運用が可能になります：

```hcl
provider "vault" {}
```

### HCP Vault Secrets (`hcp` provider) の場合

[ドキュメント](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/hcp-configuration#configure-hcp) に記載された通り、HCP 上でサービスプリンシパルの作成とワークロード ID プロバイダの追加を行います。
その上で、HCP (Vault) に接続する Workspace に下記の環境変数を設定します。

|変数名|値|
|-|-|
|TFC_HCP_PROVIDER_AUTH|"true"|
|TFC_HCP_RUN_PROVIDER_RESOURCE_NAME|サービスプリンシパルを引き受けるために使用される Workload Identity Provider のリソース名|

:::note warn
私が試した際は、`validating token failed: invalid audience (aud) claim: audience claim does not match any expected audience` というエラーが出ました。
環境変数 `TFC_HCP_WORKLOAD_IDENTITY_AUDIENCE` にドキュメントの Workload Identity Provider 作成手順に記載の `"hcp.workload.identity"` を指定することで解消できました。
:::

これにより、provider の設定から静的な認証情報を完全に排除し、よりセキュアな運用が可能になります：

```hcl
provider "hcp" {}
```