🔑

Terraformのアクセスキー運用を見直す:AWS IAMロール + MFA(AssumeRole)で安全に実行する方法

に公開

はじめに

ミクステンドでは、AWSの管理にTerraformを使っています。長らくAWSアクセスキーを使ってTerraformを運用していましたが、AIの登場とともにセキュリティの懸念が新たに出てきました。そのため、IAMロール + MFAを用いてTerraformを運用する方法へと変更しました。

この記事ではその方法を紹介します。

まとめ

最初に方法のまとめです。
前提として、AWS IAM Identity Center(旧AWS SSO) は使わない方針です(運用の都合により)。

  1. Terraform 用 IAM ユーザを各メンバーごとに用意し、付与するのは後述のロールを引き受けるのに必要な権限だけ

  2. Terraform 専用の IAM ロールを用意し、Terraform で実際に使う権限はこのロールに集約

  3. Terraform コマンドの実行は、一時クレデンシャル で行う

  4. 一時クレデンシャルの発行には、MFA が必須

目的は

  • AWS アクセスキーを AI に読まれにくくすること
  • 万が一漏洩しても直接触れる権限を狭くすること

経緯や実装例など詳細は以下に書いていきます。

詳細

以前の構成

  • 変数定義ファイルterraform.tfvars*.tfvars など)に、各メンバーの AWS 長期アクセスキー(アクセスキー ID とシークレットアクセスキー)を書いていた
  • そのキーには、広い IAM 権限が付いていた
  • 上記ファイルを .gitignore に含め、GitHub にはコミットされないようにしていた

リポジトリには秘密情報が残らない一方で、AIの登場により 端末上の平文アクセスキーの権限の広さ が問題となりました。

課題

AI と変数定義ファイル

Cursor などの AI 支援ツールを日常的に使っており、プロンプトインジェクション意図しないファイル参照terraform.tfvars / *.tfvars が読まれるおそれがありました。アクセスキーが平文で含まれていたため、会話ログ等への流出リスクもあり、「リポジトリに載せない」だけでは不十分だと判断しました。

アクセスキーの権限

長期アクセスキーに広い権限が載っており、漏洩時の影響が大きく、ローテーションの運用負担も重い状態でした。

対策の方針

前提として、AWS IAM Identity Center(旧AWS SSO) は使わない方針です(運用の都合により)。

整理した方針は次のとおりです。

  1. IAM ユーザーを用意し、付与するのは sts:AssumeRole など、Terraform 用 IAM ロールを引き受けるのに必要な権限だけにする
  2. Terraform 専用の IAM ロールを用意し、terraform plan / terraform apply で実際に使う権限は このロールの IAM ポリシーに集約する
  3. Terraform コマンドの実行は IAM ユーザーではなく、ロールを引き受けたあとに得られる 一時クレデンシャル で行う
  4. ロールの信頼ポリシー側で MFA を必須にする

目的は

  • AWS アクセスキーを AI に読まれにくくすること
  • 万が一漏洩しても直接触れる権限を狭くすること

実装例

1. IAM ユーザーと IAM ロールの作成

resource "aws_iam_user" "terraform_operator" {
  name          = "your-name"
  path          = "/"
  force_destroy = true
}

# ロールを引き受けられるのは上記 IAM ユーザーのみ。MFA 必須。
data "aws_iam_policy_document" "terraform_execution_trust" {
  statement {
    sid     = "TerraformOperatorsAssumeRole"
    effect  = "Allow"
    actions = ["sts:AssumeRole"]

    principals {
      type        = "AWS"
      identifiers = [aws_iam_user.terraform_operator.arn]
    }

    condition {
      test     = "Bool"
      variable = "aws:MultiFactorAuthPresent"
      values   = ["true"]
    }
  }
}

resource "aws_iam_role" "terraform_execution" {
  name               = "terraform_execution"
  description        = "Terraform plan/apply role (権限は環境に応じて絞ること)"
  assume_role_policy = data.aws_iam_policy_document.terraform_execution_trust.json
}

resource "aws_iam_role_policy_attachment" "terraform_execution_admin" {
  role       = aws_iam_role.terraform_execution.name
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
}

data "aws_iam_policy_document" "terraform_operator_assume_execution" {
  statement {
    sid     = "AssumeTerraformExecutionRole"
    effect  = "Allow"
    actions = ["sts:AssumeRole"]
    resources = [
      aws_iam_role.terraform_execution.arn,
    ]
  }
}

resource "aws_iam_user_policy" "terraform_operator_assume_execution" {
  name   = "assume-terraform-execution"
  user   = aws_iam_user.terraform_operator.name
  policy = data.aws_iam_policy_document.terraform_operator_assume_execution.json
}
  • 上記 IAM ユーザーの長期アクセスキーは Terraform では作らず、コンソールで発行する。State ファイルにアクセスキーを載せないため。(MFA もコンソールで有効化)
  • 上の policy_arn は例。環境に合わせて ReadOnlyAccess やカスタムポリシーなどに差し替える

2. AWS CLI のプロファイルと Terraform

AWS CLI と Terraform では 名前付きプロファイルで、「長期キーを持つ IAM ユーザー」→「MFA 付きで IAM ロールを引き受ける」という流れを表します。

~/.aws/credentials

IAM ユーザーの長期キーは ~/.aws/credentials にのみ置きます。

# ~/.aws/credentials
[example-tf-user]
aws_access_key_id     = YOUR_ACCESS_KEY_ID
aws_secret_access_key = YOUR_SECRET_ACCESS_KEY

~/.aws/config

IAM ユーザと IAM ロールのプロファイルを定義します。example-tf-export では credential_processaws configure export-credentials を呼び出して一時クレデンシャルを取得します。

# ~/.aws/config
[profile example-tf-user]
region = ap-northeast-1
output = json

[profile example-tf-role]
region         = ap-northeast-1
output         = json
source_profile = example-tf-user
role_arn       = arn:aws:iam::111122223333:role/terraform_execution
mfa_serial     = arn:aws:iam::111122223333:mfa/tf_yourname

[profile example-tf-export]
credential_process = aws configure export-credentials --profile example-tf-role
  • mfa_serial: その IAM ユーザーの MFA デバイス ARN(IAM コンソールのユーザー詳細で確認)
  • role_arn: Terraform 用 IAM ロールの ARN

3. provider "aws" の指定

providerprofile に、上記の example-tf-export を指定します。

provider "aws" {
  region  = "ap-northeast-1"
  profile = "example-tf-export"
}

4. terraform コマンドの実行

terraform コマンド実行時に MFA コードが聞かれるようになります。

運用時の気付き

local バックエンド(ローカルに stateファイルを置く)では、MFA の入力タイミングと認証まわりの初期化の都合で、最初の terraform コマンド が失敗し、再実行すると成功する挙動となりました。

MFA認証時は同じコマンドを2回実行する手間がありましたが、バックエンドを s3 に変更することで、初回から成功するようになり、この手間がなくなりました。

変更しての所感

これまで「万が一アクセスキーが漏洩したらどうしよう、、、」という一抹の不安が心のどこかにありましたが、この方法に変更することでその不安がなくなり、安心して terraform を触ることができるようになりました。

セキュリティは、チームにとっても個人にとっても大事なものと改めて感じました。

おわりに

IAMロール + MFA でTerraformを安全に運用する方法をまとめて紹介する記事は意外となかったので、この記事が「Terraformのアクセスキー運用から脱却したい」、「Terraformのセキュリティが不安」という方の参考になれば幸いです。

ミクステンド Tech Blog

Discussion