概要
Megazone Japan Advent Calendar 2023の14日目に掲載するTech記事を執筆しました。🫡
この記事では、VaultをTerraformに統合して、AWSリソースを構築する方法について説明します。
一般的にはAWSからIAMを発行すると思いますが、VaultとTerraformを統合する場合、次のような違いがあるでしょう。
カテゴリ | AWS IAM発行(一般的) | Vault IAM発行(Terraform統合) |
---|---|---|
管理者の役割 | AWSコンソールにおけるIAM RoleとPolicyの生成及び管理 | Terraformを使用したVault経由のIAM RoleとPolicyの自動生成及び管理 |
開発者の役割 | 管理者によるIAM生成を通じたAWSリソースの構築 | Vaultによる動的IAM発行を使用したAWSリソースの構築 |
セキュリティ | 手動作業に伴うミス発生の可能性 | 資格証明の動的生成及び破棄を通じたセキュリティの強化とリスク低減 |
自動化 | 手動管理の必要性 | 資格証明のライフサイクル自動化による効率向上 |
通常、AWSでIAMを発行した場合、そのIAM情報を開発者に直接渡し、ローカルで管理することが多いです。しかし、何らかの理由でアクセスキーが損傷したり、情報が漏洩するなどのミスが発生するリスクがあります。これらのリスクを軽減するために、VaultをTerraformに統合して資格情報を動的に生成し、コードを通じて管理することで、より効率的かつ安全に管理できると考えられます。
実際に以下のように役割を分けて、VaultをTerraformに統合して、AWSリソースを構築してみます。
管理者はVaultをTerraformに統合し、AWS Secrets Engineを生成する。
開発者はVaultのSecretへのアクセスせずに、Terraformだけで、AWSリソースを構築する。
環境について
本設定環境は以下の通りです。
- macOS
- Vault on Docker(※既に環境にVaultが設置されていれば問題ありません。Vaultがない、またはUIでの管理を試してみたい場合は、こちらのリンクを参考にしてください。)
- Terraform v1.5.0系
- AWS CLI
VaultのTerraform作成について
Terraform作成する内容は以下の通りです。まずは、/creds
の管理者部分から説明します。
.
├── creds # 管理者のTerraform
│ ├── backend.tf
│ ├── data.tf
│ ├── main.tf
│ ├── provider.tf
│ └── vars.tf
└── dev # 開発者のTerraform
├── backend.tf
├── data.tf
├── provider.tf
├── vars.tf
└── vpc.tf
SSM Parameter store作成(管理者の役割)
管理者のTerraformで、vault_aws_secret_backend
のリソースを利用します。これは、HashiCorp Vaultの中でAWSの認証情報(アクセスキーとシークレットキー)を動的に生成するための機能です。
VaultがAWSの認証情報を必要とする際、AWS Access KeyとSecret Keyが必要ですので、SSM Parameter Storeからこれらの情報を読み取るようにします。
- /dev/vault/token: Vault実行用のVAULT_DEV_ROOT_TOKEN_ID
- /dev/vault/aws_secret_access_key: Vault用のAWS Secret key
- /dev/vault/aws_access_key_id: Vault用のAWS Access key
Vault Provider(管理者の役割)
次は、Vault Provider
を使用してVault内でAdministratorAccess
のRole持つAWS Secret Engineを有効化します。Terraformのコードは以下の通りです。
/creds/backend.tf
は、
- Terraformのバージョンは1.5.0以上かつ1.6.0未満のバージョンが要求されています。
- tfstateをS3に管理するようにします。
terraform {
required_version = "~> 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
backend "s3" {
bucket = "" # s3 bucket name
region = "ap-northeast-1"
key = "vault/creds/terraform.tfstate"
encrypt = true
}
}
/creds/data.tf
は
- SSM Parameter Storeから特定のパラメータを取得します。
- IAMポリシーを定義するために使用されるデータソースの宣言を含んでいます。
data "aws_ssm_parameter" "token" {
name = "/dev/vault/token"
}
data "aws_ssm_parameter" "access_key" {
name = "/dev/vault/aws_access_key_id"
}
data "aws_ssm_parameter" "secret_key" {
name = "/dev/vault/aws_secret_access_key"
}
data "aws_iam_policy_document" "policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = ["arn:aws:iam::${data.aws_caller_identity.this.account_id}:root"]
}
}
}
data "aws_caller_identity" "this" {}
data "aws_iam_policy" "policy" {
name = "AdministratorAccess"
}
/creds/main.tf
は
- VaultのAWS secret backendを設定します。
- AWS IAMロールを作成します。
- そのロールにポリシーをアタッチし、最後にVault内でAWS secret backend Roleを作成するためのリソース定義を含んでいます。
resource "vault_aws_secret_backend" "aws" {
access_key = data.aws_ssm_parameter.access_key.value
secret_key = data.aws_ssm_parameter.secret_key.value
path = "aws"
region = var.region
default_lease_ttl_seconds = 3600
max_lease_ttl_seconds = 7200
}
resource "aws_iam_role" "role" {
name = "dev-vault-role"
assume_role_policy = data.aws_iam_policy_document.policy.json
tags = merge(var.tags, {})
}
resource "aws_iam_role_policy_attachment" "attachment" {
role = aws_iam_role.role.name
policy_arn = data.aws_iam_policy.policy.arn
}
resource "vault_aws_secret_backend_role" "role" {
backend = vault_aws_secret_backend.aws.path
name = "dev-vault-role"
credential_type = "assumed_role"
role_arns = [aws_iam_role.role.arn]
}
/creds/provider.tf
は、
- HashiCorp VaultとAWSのプロバイダーを設定するためのものです。
provider "vault" {
address = var.vault_address
token = data.aws_ssm_parameter.token.value
}
provider "aws" {
region = var.region
}
/creds/vars.tf
は、
- Terraformで使用される変数を定義するためのものです。
variable "region" {
type = string
default = "ap-northeast-1"
description = "AWS tokyo region"
}
variable "vault_address" {
type = string
default = "http://127.0.0.1:8200"
description = "URL address for vault endpoint"
}
variable "tags" {
type = map(string)
default = {
env = "dev_vault"
}
description = "dev vault tags"
}
コード作成ができたら、デプロイしてみましょう。
terraform init
terraform plan
terraform apply
結果としては、次のリソースが作成できたと思います。
# aws_iam_role.role
# aws_iam_role_policy_attachment.attachment
# vault_aws_secret_backend.aws
# vault_aws_secret_backend.aws
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
これで、管理者がVault内にAdministratorAccess
のRole持つAWS Secret Engineを作成し、開発者にAWS IAM情報を渡す準備が整いました。
AWS Secret Engineの確認(管理者の役割)
Vault on DockerのVault UIで管理しているため、Web上で確認してみます。
aws/
にAWS Secret Engineの作成ができているようです。
次は、開発者が使うVault Tokenを生成してみます。
aws/
に移動すると、先ほどTerraformのAWS secret backend Roleで作成したdev-vault-role
があります。そのRoleのGenerate credentials
に移動します。
Credential type
をAssumed Role
で選択し、Generate
をクリックします。
これにより、開発者用のSecret KeyとSecurity Tokenが生成されたことが確認できます。
AWS VPC構築(開発者の役割)
AWS Secret Engineを有効にしたので、動作確認をしてみたいと思います。開発者がAWS Secret Engineから取得したAWS Access Credentialsを利用して簡単なAWS VPCをデプロイします。
/dev/backend.tf
は、
- Terraformのバージョンは1.5.0以上かつ1.6.0未満のバージョンが要求されています。
- tfstateをS3に管理するようにします。
terraform {
required_version = "~> 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
backend "s3" {
bucket = "" # s3 bucket name
region = "ap-northeast-1"
key = "vault/dev/terraform.tfstate"
encrypt = true
}
}
/dev/data.tf
は、
- Vaultに対してAWS access credentialsを取得します。
data "vault_aws_access_credentials" "aws" {
backend = "aws"
role = "dev-vault-role" // vault_aws_secret_backend_role名
type = "sts" // AWS Security Token Service
}
/dev/provider.tf
は
- HashiCorp VaultとAWSのプロバイダーを設定するためのものです。
provider "vault" {
address = var.vault_address
}
provider "aws" {
region = var.region
access_key = data.vault_aws_access_credentials.aws.access_key // 下図のAccess Keyを表す
secret_key = data.vault_aws_access_credentials.aws.secret_key // 下図のSecret Keyを表す
token = data.vault_aws_access_credentials.aws.security_token // 下図のSecurity tokenを表す
}
/dev/vars.tf
は、
- Terraformで使用される変数を定義するためのものです。
variable "region" {
type = string
default = "ap-northeast-1"
description = "AWS tokyo region"
}
variable "vault_address" {
type = string
default = "http://127.0.0.1:8200"
description = "URL address for vault endpoint"
}
variable "tags" {
type = map(string)
default = {
env = "dev_vault"
}
description = "dev vault tags"
}
/dev/vpc.tf
は、
- TerraformのAWS VPCリソースです。
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
instance_tenancy = "default"
tags = merge(var.tags, { "Name" = "dev-vault-vpc" })
}
Terraformコードが準備できたら、コマンドを叩く前にVAULT_TOKEN
の環境変数を設定する必要があります。
VAULT_TOKEN
は、Vaultへの認証および認可に使用されるトークンです。Vaultはセキュアなアクセスとシークレットの管理を提供するツールであり、トークンはVaultへのアクセスを許可するための認証手段として機能します。
なお、以下の設定を行います。
- AWS_PROFILEの削除:unset AWS_PROFILE // Terraform上にVault AWS Access Credentialsを利用するために、Localにある aws profileを削除しておく
- VAULT_TOKENの設定:export VAULT_TOKEN=Vault実行用のVAULT_DEV_ROOT_TOKEN_ID
コード作成と環境変数の設定ができたら、デプロイしてみましょう。
terraform init
terraform plan
terraform apply
dev-vault-vpc
というAWS VPC名でデプロイができました。
最後に
IAM情報をVaultとTerraformで統合することにより、開発者がTerraformコードだけで効率的に環境を構築でき、AWS Secrets keyを安全に管理できることがメリットだと思います。
以上、VaultをTerraformに統合してAWSサービスを構築する方法についての記事でした。ありがとうございました!