🔑

HCP Vault Secrets でシークレットを管理する

2025/01/03に公開

APIキーなどのシークレットを安全かつ効率的に管理することは、セキュリティリスクを軽減し、開発速度を向上させるために重要です。この記事では、HCP Vault Secretsを使ったシークレット管理について、以下についてお伝えします。

  • HCP Vault Secretsの概要
  • HCP Terraformとの連携
  • Kubernetes環境のSecretリソースとの同期

HCP Vault Secretsの概要

HCP Vault Secrets(以下においてはVault Secretsと記載)とは、HashiCorpが提供しているSaaS型のシークレット管理ツールです。非常にシンプルなので、どなたでも簡単に直ぐに使い始められます。
https://developer.hashicorp.com/hcp/docs/vault-secrets

Vault Secretsのセキュリティモデルに関するドキュメントの中で、ハイレベルなアーキテクチャーが記載されていますので、Vault Secretsがどのような仕組みで稼働しているか概要を把握する事ができます。
https://developer.hashicorp.com/hcp/docs/vault-secrets/security-model

また、最近はVault Secretsの機能強化も積極的に行われており、クラウドプロバイダ等が提供するマネージドシークレット管理ソリューションへの1-Wayのシークレットの同期シークレットローテーション動的シークレットといった機能が利用できる様になってきています。

Vault Secretsの操作は、CLI、GUI、hcpTerraformプロバイダから行う事ができます。CLIに関しては、hcpCLIのvault-secretsサブコマンドを利用する事で、Vault Secretsの操作を行う事が出来る様になっています。

HCP Terraformとの連携

Terraformのワークフロー管理で利用されているHCP Terraformでは、AWS、Azure、Google Cloud、Kubernetes、Vault、HCPといったプロバイダを利用する際に必要な認証情報を動的に扱える機能を提供しています。ただ、これら以外のプロバイダを利用する際は、静的にAPIキーを生成し、それを環境変数等に設定して利用する必要があります。

その様なAPIキーをVault Secretsに保管し、一元管理しておく事で、シークレットを安全に管理する事ができます。さらに、Vault SecretsのSecrets Syncの機能でHCP TerraformのVariablesVariable Setsにシークレットを同期させてあげる事でVault Secrets側でシークレット情報を更新した際に、自動で同期されるので便利です。

設定方法は以下のドキュメントに記載されていますが、非常にシンプルですので、HCP Terraformをお使いの方は併せてVault Secretsも利用するとシークレットの管理をよりシンプルに出来る可能性があります。
https://developer.hashicorp.com/hcp/docs/vault-secrets/integrations/hcp-terraform

以下はDatadogのTerraformプロバイダーを利用する際に必要な認証情報などをVault Secretsに保管しています。


Vault SecretsのApplication名:datadogにシークレットを保管

これをVault SecretsのSecrets Syncを利用して、HCP TerraformのVariable Setsに同期しています。同期先のVariable Setsは事前に作成しておく必要がありますが、同期が正しく開始されると、Key:Valueの値が指定したVariable Setsの中に作成されます。


HCP TerraformのVariable Setsに同期されたシークレット

このVariable SetsをHCP Terraformのプロジェクトワークスペースと紐付ける事で、それらの環境でdatadogプロバイダーを利用するの認証情報をTerraformコード上で指定する必要がなく、HCP Terraform上でTerraform Runを実行できます。

Kubernetes環境のSecretリソースとの同期

シークレットは様々な環境で必要になると思いますが、ここでは、Datadog Operatorを利用し、Datadog Agentをデプロイする際に必要となるDatadogのAPIキー、アプリケーションキーを、Vault Secrets Operator(以下VSOと記載)経由でVault Secretsから取得し、Kubernetes(以下K8sと記載)のSecretリソースに同期する方法を紹介します。

Operatorのインストール

K8sクラスタはTerraformを使ってGKEをプロビジョニングしています。まずは最初にDatadog Operator、VSOをTerraformを利用し、インストールしていきます。

TerraformのワークフローはHCP Terraformを利用しています。事前にプロビジョニングしたGKEはHCP Terraformの別のワークスペースで管理されており、リモートステートでkubernetesプロバイダ、helmプロバイダに必要な認証情報をそのワークスペースで管理されているステートから取得しています。hcpプロバイダに関しては、動的クレデンシャルを利用し、このワークスペースでTerraformの処理を実行するタイミングのみ有効な認証情報を設定しています。

Provider設定
data "google_client_config" "default" {}

data "terraform_remote_state" "gke" {

  backend = "remote"

  config = {
    organization = "HCP_TERRAFORM_ORGANIZATION_NAME"
    workspaces = {
      name = "HCP_TERRAFORM_WORKSPACE_NAME"
    }
  }
}

provider "kubernetes" {
  host                   = "https://${data.terraform_remote_state.gke.outputs.gke_endpoint}"
  cluster_ca_certificate = base64decode(data.terraform_remote_state.gke.outputs.gke_cluster_ca_certificate)
  token                  = data.google_client_config.default.access_token
}

provider "helm" {
  kubernetes {
    host                   = "https://${data.terraform_remote_state.gke.outputs.gke_endpoint}"
    cluster_ca_certificate = base64decode(data.terraform_remote_state.gke.outputs.gke_cluster_ca_certificate)
    token                  = data.google_client_config.default.access_token
  }
}

provider "hcp" {}

各Operatorはhelmプロバイダを利用して、以下の様に設定しています。

Operatorのインストール
resource "helm_release" "datadog_operator" {
  name       = "my-datadog-operator"
  chart      = "datadog-operator"
  namespace  = "default"
  repository = "https://helm.datadoghq.com"
  version    = "1.7.0"
}

resource "helm_release" "vault_secrets_operator" {
  name       = "my-vault-secrets-operator"
  chart      = "vault-secrets-operator"
  namespace  = "default"
  repository = "https://helm.releases.hashicorp.com"
  version    = "0.9.1"
}

正しくインストールされると以下のようにカスタムリソースがK8sクラスタで利用出来る様になるはずです。

VSOカスタムリソース
$ k api-resources |grep hashicorp
hcpauths                                                secrets.hashicorp.com/v1beta1     true         HCPAuth
hcpvaultsecretsapps                                     secrets.hashicorp.com/v1beta1     true         HCPVaultSecretsApp
secrettransformations                                   secrets.hashicorp.com/v1beta1     true         SecretTransformation
vaultauthglobals                                        secrets.hashicorp.com/v1beta1     true         VaultAuthGlobal
vaultauths                                              secrets.hashicorp.com/v1beta1     true         VaultAuth
vaultconnections                                        secrets.hashicorp.com/v1beta1     true         VaultConnection
vaultdynamicsecrets                                     secrets.hashicorp.com/v1beta1     true         VaultDynamicSecret
vaultpkisecrets                                         secrets.hashicorp.com/v1beta1     true         VaultPKISecret
vaultstaticsecrets                                      secrets.hashicorp.com/v1beta1     true         VaultStaticSecret
Datadog Operatorカスタムリソース
$ k api-resources |grep datadog
datadogagents                       dd                  datadoghq.com/v2alpha1            true         DatadogAgent
datadogmetrics                                          datadoghq.com/v1alpha1            true         DatadogMetric
datadogmonitors                                         datadoghq.com/v1alpha1            true         DatadogMonitor

Vault Secretsからのシークレットの取得

Datadog OperatorとVSOのインストールが完了したので、VSOを介してDatadog Agentのインストールに必要な認証情報、APIキーとアプリケーションキーを取得していきます。これらのキーは既にDatadog上で作成され、Vault SecretsのApplicationdatadogで管理されているものとします。

この後の処理もHCP Terraformを利用し、別のワークスペースを用意し実施していきます。プロバイダの設定は先ほどと同様の設定をしています。

VSOのカスタムリソースHCPAuthを利用する際に、HCPへの認証を行う必要があるため、HCPのサービスプリンシパルをTerraformで作成しています。サービスプリンシパルにはロールを付与する必要がありますが、HCPで利用出来るロールに関しては以下のドキュメントで確認できます。
https://developer.hashicorp.com/hcp/docs/hcp/iam/access-management

作成したHCPのサービスプリンシパルに紐付くclientIDclientSecretを、K8sのSecretリソースvso-hcp-spに定義しています。

HCPのサービスプリンシパルの作成
data "hcp_organization" "my_org" {}

data "hcp_project" "default" {}

resource "hcp_service_principal" "vso" {
  name   = "vso-hcp-sp"
  parent = data.hcp_project.default.resource_name
}

resource "hcp_project_iam_binding" "vso" {
  project_id   = data.hcp_project.default.resource_id
  principal_id = hcp_service_principal.vso.resource_id
  role         = "roles/viewer"
}

resource "hcp_service_principal_key" "vso" {
  service_principal = hcp_service_principal.vso.resource_name
}

resource "kubernetes_secret_v1" "vso_hcp_sp" {
  metadata {
    name      = "vso-hcp-sp"
    namespace = "default"
  }

  data = {
    clientID     = "${hcp_service_principal_key.vso.client_id}"
    clientSecret = "${hcp_service_principal_key.vso.client_secret}"
  }

  type = "Opaque"

  depends_on = [hcp_service_principal_key.vso]
}

VSOのカスタムリソースを利用する準備が出来たので、VSOを利用して、Vault Secretsで管理しているDatadogの認証情報をK8sのSecretリソースに同期させます。

以下のTerraformコードでは、K8sプロバイダで利用出来るFunctionを利用して、別のディレクトリで管理しているマニフェストファイルを読み込ませています。

VSOカスタムリソース
resource "kubernetes_manifest" "hcp_vault_secrets_auth" {
  manifest = provider::kubernetes::manifest_decode(templatefile("${path.module}/../../vso/hcp-vault-secrets-auth.yaml", {
    hcp_org_id     = data.hcp_organization.my_org.resource_id,
    hcp_project_id = data.hcp_project.default.resource_id
  }))

  depends_on = [
    kubernetes_secret_v1.vso_hcp_sp
  ]
}

resource "kubernetes_manifest" "hcp_vault_secrets_app" {
  manifest = provider::kubernetes::manifest_decode(templatefile("${path.module}/../../vso/hcp-vault-secrets-app.yaml", {
    app_name = var.vault_secrets_app_name
  }))

  depends_on = [
    kubernetes_manifest.hcp_vault_secrets_auth
  ]
}

上記のTerraformコードで利用しているHCPAuthリソース、HCPVaultSecretsAppリソース、それぞれのマニフェストファイルは以下の通りです。

hcp-vault-secrets-auth.yamlでは、VSOとHCPとの認証設定を行っています。この中で、先ほど作成したサービスプリンシパルをストアしているK8sのSecretリソースを参照しています。

hcp-vault-secrets-auth.yaml
apiVersion: secrets.hashicorp.com/v1beta1
kind: HCPAuth
metadata:
  name: hcp-auth
  namespace: default
spec:
  organizationID: "${hcp_org_id}"
  projectID: "${hcp_project_id}"
  allowedNamespaces: ["*"]
  servicePrincipal:
    secretRef: vso-hcp-sp

hcp-vault-secrets-app.yamlでは、VSOがVault SecretsのターゲットとなるApplication名(Terraform変数vault_secrets_app_nameの値を格納した${app_name})と利用するHCPの認証設定を定義し、取得したシークレット情報をコピーするK8sのSecretリソースに関する設定をしています。

hcp-vault-secrets-app.yaml
apiVersion: secrets.hashicorp.com/v1beta1
kind: HCPVaultSecretsApp
metadata:
  name: datadog
  namespace: default
spec:
  appName: ${app_name}
  hcpAuthRef: hcp-auth
  destination:
    name: datadog-secret
    create: true
    overwrite: true
    labels:
      hvs: "true"
    type: Opaque
  refreshAfter: 1h

VSOがHCPAuthリソースで定義された情報を元にVault Secretsにアクセスし、HCPVaultSecretsAppリソースで定義された情報を元に、Vault Secrets上で管理されているシークレットを取得し、それらをK8sのSecretリソースにコピーします。

この処理が終わった後に、Datadog Agentをデプロイします。Terraformコードに関しては、VSOのカスタムリソースをデプロイした際とほとんど同じですが、depends_onでVSO側のデプロイが完了した後に処理を行う様にしています。

Datadog Operatorカスタムリソース
resource "kubernetes_manifest" "datadog_agent" {
  manifest = provider::kubernetes::manifest_decode(file("${path.module}/../../datadog/operator/datadog.yaml"))

  depends_on = [
    kubernetes_secret_v1.vso_hcp_sp,
    kubernetes_manifest.hcp_vault_secrets_auth,
    kubernetes_manifest.hcp_vault_secrets_app
  ]
}

datadog.yamlは以下の様に設定しています。spec.global.credentialsでAPIキー、アプリケーションキーの設定をしていますが、これらはVSOを介して、Vault Secretsから取得したシークレット情報になっています。

datadog.yaml
apiVersion: datadoghq.com/v2alpha1
kind: DatadogAgent
metadata:
  name: datadog
  namespace: default
spec:
  global:
    clusterName: my-cluster-agent
    kubelet:
      tlsVerify: false
    credentials:
      apiSecret:
        secretName: datadog-secret
        keyName: DD_API_KEY
      appSecret:
        secretName: datadog-secret
        keyName: DD_APP_KEY
  features:
    logCollection:
      enabled: true
      containerCollectAll: true
    apm:
      hostPortConfig:
        enabled: true
    liveProcessCollection:
      enabled: true
    usm:
      enabled: true
  override:
    nodeAgent:
      tolerations:
        - effect: NoSchedule
          key: node-role.kubernetes.io/control-plane
          operator: Exists

HCP Terraform上でterraform applyを実行すると、無事リソースが立ち上がる事が確認できると思います。

$ k get pods
NAME                                                            READY   STATUS    RESTARTS   AGE
datadog-agent-7fblw                                             4/4     Running   0          26h
datadog-agent-9pwk7                                             4/4     Running   0          26h
datadog-agent-gdcbk                                             4/4     Running   0          26h
datadog-agent-tgzmq                                             4/4     Running   0          26h
datadog-cluster-agent-cc8874d64-clf5q                           1/1     Running   0          26h
my-datadog-operator-db85ff84c-rqssq                             1/1     Running   0          26h
my-vault-secrets-operator-controller-manager-8696484498-t4tx6   2/2     Running   0          8d
$ k get deploy
NAME                                           READY   UP-TO-DATE   AVAILABLE   AGE
datadog-cluster-agent                          1/1     1            1           26h
my-datadog-operator                            1/1     1            1           8d
my-vault-secrets-operator-controller-manager   1/1     1            1           8d

Datadogに限らず、さまざまなサービスのAPIキー等のシークレット情報は、多くの場面や環境で利用されることがあると思います。これらをVault Secretsで一元管理することで、セキュリティリスクの一因となるシークレットの散在を効果的に防ぐことができます。

Discussion