「ローカルに永続的な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 ID
とClient 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)でした。