HCP Vaultで一時的なAWSアクセスキーを発行してTerraformを実行してみる

2024.01.10

「ローカルに永続的なAWSアクセスキーを持たずに、Terraformを実行したい」

AWS IAM Identity Centerで一時的なアクセスキーを発行するとか、Terraform CloudのVariablesにAWSアクセスキー置く(IAM Role使う)とか色々方法があります。

今回はHCP Vaultを使ってみます。

HCP Vaultとは

Vaultはシークレット管理ソリューションです。クレデンシャル等をセキュアに管理します。

Vaultはセルフホスト版とSaaS版があります。

セルフホスト版は柔軟性が高いことはメリットですが、サーバーの管理が必要になります。

HCP VaultはSaaS版で、Vaultクラスターに必要なサーバーの管理をHashiCorpが行います。

クラスターをデプロイするクラウドプロパイダーはAWSとAzureから選択可能で、日本リージョン(AWSならap-northeast-1)をサポートしています。

HCP Vault Overview | HashiCorp Cloud Platform | HashiCorp Developer

やってみた

作成する構成は以下です。

HCP Vaultで一時的なAWSアクセスキーを作成して、Terraformでそれを使ってAWSにリソースをデプロイしてみます。

HCP VaultやVaultの設定もTerraformを使います。

コードはGitHubにあります。

terraform-sample/hcp-vault-terraform at main · msato0731/terraform-sample

HCP Service Principalの作成

Vault ClusterをTerraformで作成します。

TerraformでHCPのリソースを操作するために、Service Principalを作成する必要があります。

Authenticate with HCP - HCP Provider | Guides | hashicorp/hcp | Terraform | Terraform Registry

HCPポータルにログインして、Access Control(IAM) -> Service principals -> Create service principalの順に選択します。

名前とRoleを設定してService principalを作成します。

Generate keyを選択して、キーを作成します。Client IDClient secretの内容を控えておきます。

HCP Vault Clusterの作成

Terraformを使用して、HCP Vault Clusterを作成します。

ターミナルを開いて、先ほど作成したキーを環境変数に設定します。

export HCP_CLIENT_ID="<Service Principal Clinet ID>"
export HCP_CLIENT_SECRET="<Service Principal Clinet Secrets>"

以下のファイルを使います。

hcp/main.tf

terraform {
  required_providers {
    hcp = {
      source  = "hashicorp/hcp"
      version = "0.79.0"
    }
  }
}

terraform {
  backend "local" {
    path = "terraform.tfstate"
  }
}

resource "hcp_hvn" "main" {
  hvn_id         = "hvn1"
  cloud_provider = "aws"
  region         = "ap-northeast-1"
  cidr_block     = "172.25.16.0/20"
}

resource "hcp_vault_cluster" "main" {
  cluster_id      = "vault-cluster"
  hvn_id          = hcp_hvn.main.hvn_id
  tier            = "dev"
  public_endpoint = true
}

resource "hcp_vault_cluster_admin_token" "main" {
  cluster_id = hcp_vault_cluster.main.cluster_id
}

上記tfファイルがあるディレクトリに移動し、コマンドを実行して、リソースを作成します。

terraform init
terraform apply

Vault Clusterが作成できたら、Web UIでアクセスできるか確認します。

New admin tokenからGenerate Token->Copyを選択して、Tokenを取得します。

ページ上部の、Launch web UIを選択します。

Token入力が求めれるため、先ほどのTokenを入力します。

以下のようにVaultのWeb UIにアクセスできます。

Vault用のIAMユーザー作成

Vaultは一時的なIAMアクセスキーを作成します。

その際に、Vaultが利用するIAMユーザーが必要です。

CloudShellまたは、ローカルからAWS CLIを実行して、IAMユーザーを作成します。

aws iam create-user --user-name vault-user

必要なIAM権限を公式ドキュメントを参考にします。ポリシーは以下です。 (ハイライト箇所のACCOUNT-IDの部分は、それぞれ置き換えてください。)

policy.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iam:AttachUserPolicy",
        "iam:CreateAccessKey",
        "iam:CreateUser",
        "iam:DeleteAccessKey",
        "iam:DeleteUser",
        "iam:DeleteUserPolicy",
        "iam:DetachUserPolicy",
        "iam:GetUser",
        "iam:ListAccessKeys",
        "iam:ListAttachedUserPolicies",
        "iam:ListGroupsForUser",
        "iam:ListUserPolicies",
        "iam:PutUserPolicy",
        "iam:AddUserToGroup",
        "iam:RemoveUserFromGroup"
      ],
      "Resource": ["arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:user/vault-*"]
    }
  ]
}

以下のコマンドを実行して、ポリシーを割り当てます。

aws iam put-user-policy --user-name vault-user --policy-name vault --policy-document file://policy.json

Vaultにセットするアクセスキーを作成します。

aws iam create-access-key --user-name vault-user

アクセスキーIDとシークレットアクセスキーは、次の手順で利用するため控えておいてください。

AWS - Secrets Engines | Vault | HashiCorp Developer

Vaultの設定

Terraformを使って、Vaultの設定を行います。

TerraformからVaultを操作するために、Token等を環境変数に設定していきます。

先ほどWeb UIにアクセスした時と同様に、Tokenをコピーしてセットします。

export VAULT_TOKEN=<Vault Token>

ClusterのPublic URLもセットします。Cluster URLs -> Publicを選択して、取得します。

export VAULT_ADDR=<Vault Public Cluster URL>

以下のファイルを使います。

vault/main.tf

variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "name" { default = "dynamic-aws-creds-vault-admin" }

terraform {
  required_providers {
    vault = {
      source  = "hashicorp/vault"
      version = "3.23.0"
    }
  }
}

terraform {
  backend "local" {
    path = "terraform.tfstate"
  }
}

resource "vault_aws_secret_backend" "aws" {
  access_key = var.aws_access_key
  secret_key = var.aws_secret_key
  path       = "${var.name}-path"

  default_lease_ttl_seconds = "120"
  max_lease_ttl_seconds     = "240"
}

resource "vault_aws_secret_backend_role" "admin" {
  backend         = vault_aws_secret_backend.aws.path
  name            = "${var.name}-role"
  credential_type = "iam_user"

  policy_document = <<EOF
{
 "Version": "2012-10-17",
 "Statement": [
   {
     "Effect": "Allow",
     "Action": [
       "iam:*", "ec2:*", "s3:*"
     ],
     "Resource": "*"
   }
 ]
}
EOF
}

output "backend" {
  value = vault_aws_secret_backend.aws.path
}

output "role" {
  value = vault_aws_secret_backend_role.admin.name
}

上記tfファイルがあるディレクトリに移動し、コマンドを実行して、リソースを作成します。

実行時にAWSアクセスキーIDとシークレットアクセスキーが求められるため、前の手順で作成したものを入力してください。

terraform init
terraform apply

成功するとVault上にSecrets engines dynamic-aws-creds-vault-admin-pathが作成されます。

AWSリソースのデプロイ

Vaultを使ってローカルの認証情報を使わずにTerraformでAWSリソースをデプロイしてみます。

以下のファイルを使います。

aws/main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.31.0"
    }
    vault = {
      source  = "hashicorp/vault"
      version = "3.23.0"
    }
  }
}

terraform {
  backend "local" {
    path = "terraform.tfstate"
  }
}

data "terraform_remote_state" "vault" {
  backend = "local"

  config = {
    path = "../vault/terraform.tfstate"
  }
}

data "vault_aws_access_credentials" "creds" {
  backend = data.terraform_remote_state.vault.outputs.backend
  role    = data.terraform_remote_state.vault.outputs.role
}

provider "aws" {
  region     = "ap-northeast-1"
  access_key = data.vault_aws_access_credentials.creds.access_key
  secret_key = data.vault_aws_access_credentials.creds.secret_key
}

resource "aws_s3_bucket" "main" {
  bucket = "vault-test-20240105" # 一意な名前を指定してください
}

ハイライト箇所の記述で、VaultからAWS認証情報を取得しています。記述がシンプルでいいですね。

Vaultで一時的なAWSアクセスキーを作成してTerraformを実行するため、ローカルのAWSアクセスキーは不要です。

ローカルの認証情報が使われないように、一応環境変数から消しておきます。

unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN

上記tfファイルがあるディレクトリに移動し、コマンドを実行して、リソースを作成します。

terraform init
terraform apply

Applyが終わるとS3バケットが作成されました。

$ aws s3 ls | grep vault
2024-01-05 08:30:13 vault-test-20240105

IAMユーザーも見てみます。Vaultによって作成される一時的なIAMユーザーを確認できました。

このユーザーは、Vaultで設定したTTLに従ってTerraform実行後に削除されます。

おわりに

HCP Vaultで一時的なAWS認証情報を作成して、Terraformから使ってみました。

HCP VaultはSaaS製品でTerraformのhcpプロパイダーで操作可能なため、簡単にVault Clusterを作成できました。(コード量もHCPとVault合わせて、100行いかないくらいでした)

以上、AWS事業本部の佐藤(@chari7311)でした。

参考