🌉

TerraformでのIAM role設定はこれで決定!

2024/03/24に公開

挨拶

畑田です。Terraformで環境構築中です。
AWSにTerraformで環境構築するのは初めての経験でした。それにあたり、既存のTerraformのソースを参照しながら勉強していたのですが、IAMの設定方法1つとっても書き方が複数あり、戸惑うことが多かったです。
その経験から、IAMの設定方法をまとめておこうと考えました。それに際して基本的な事項のおさらいも行っていこうと思います。

免責

この文章ではTerraformの実行方法や、HashiCorp Configuration Language (HCL)の文法は解説せず、既知のものとして扱います。

結論

まずはこのように書けば良いというsnippetを記載します。
前提としてRDS Proxyにアクセス権限を与える意図があるものとします。

data "aws_iam_policy_document" "rds_proxy_assume_role_policy_document" {
  statement {
    principals {  # 誰が (RDSが)
      type        = "Service"
      identifiers = ["rds.amazonaws.com"]
    }
    actions = ["sts:AssumeRole"]  # 何をして (STSからIAM roleをassumeして)
    effect  = "Allow"  # 良い
  }
}

data "aws_iam_policy_document" "rds_proxy_policy_document" {
  version = "2012-10-17"
  statement {
    resources = ["arn:aws:secretsmanager:*:*:*"]  # 誰に (AWS Secrets Managerに対して)
    actions = [  # 何をして (以下4つの操作をして)
      "secretsmanager:GetResourcePolicy",
      "secretsmanager:GetSecretValue",
      "secretsmanager:DescribeSecret",
      "secretsmanager:ListSecretVersionIds"
    ]
    effect    = "Allow"  # 良い
  }
}

resource "aws_iam_policy" "rds_proxy_policy" {
  name        = "rds-proxy-policy"
  description = "Policy for RDS Proxy"
  policy      = data.aws_iam_policy_document.rds_proxy_policy_document.json
}

resource "aws_iam_role" "rds_proxy_role" {
  name               = "rds-proxy-role"
  assume_role_policy = data.aws_iam_policy_document.rds_proxy_assume_role_policy_document.json
  managed_policy_arns = [
    aws_iam_policy.rds_proxy_policy.arn,  # 作成したポリシーをアタッチ
    "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore",  # AWS managedなポリシーをアタッチ
  ]
}

IAMについて

AWSにおけるIAMとはAWS Identity and Access Managementの略称であり、AWS上のリソースに認証および認可を提供する仕組みです。今回の登場人物は以下です。

  • STS (AWS Security Token Service)
  • IAM policy
  • IAM role

STS

いきなりIAMの接頭辞がついていないリソースからの説明になります。
公式ドキュメントによれば、ユーザーやAWSリソースが、一時的に他のAWSリソースへアクセスできるように、認証認可に関する秘匿情報を与える機能ということです。
詳細は公式ドキュメントに譲りますが、STSの仕組みによって、不用意に関係者やアプリケーションに認証情報を配布する必要がなくなったり、ある権限を与えるためにいちいち認証を作る必要がなくなったり、安全に認証認可を管理できたりする機能が提供されています。
STSの提供する機能の中でも今回注目するのはAssumeRole APIです。AWS上の全てのリソースは、IAM role ARNを指定してAssumeRole APIを呼び出すと、そのARNのroleに紐づいた権限のある一時的な認証秘匿情報を得ることができます。

IAM policy

IAM policyとはIAMの構成する単位のうち最も小さいものの一つです。
簡単のために不確かな表現をすると、「誰 (何)が」、「誰 (何)に」、「何を」、「して良いか、あるいはしてはならないか」というルールを示すドキュメントです。ここで「ドキュメント」と言ったのは、実際にAWSでは大半のIAM policyがJSON形式で管理されるからです。
consoleから操作する分にはそのJSONの形式を強く意識することはありません。しかし、TerraformやCDKなどで操作したり、consoleからでも複雑なルールを設定したりする場合は、簡単な文法や決まり事を知っておくと便利でしょう。
以下が公式ドキュメントから拝借したJSONのexampleです。

JSONのexample
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListAllMyBuckets",
        "s3:GetBucketLocation"
      ],
      "Resource": "arn:aws:s3:::*"
    },
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::BUCKET-NAME",
      "Condition": {"StringLike": {"s3:prefix": [
        "",
        "home/",
        "home/${aws:username}/"
      ]}}
    },
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::BUCKET-NAME/home/${aws:username}",
        "arn:aws:s3:::BUCKET-NAME/home/${aws:username}/*"
      ]
    }
  ]
}

こちらがIAM policyに指定できる内容を一覧したものです。個人的にはNotXxxxはあまり使いません。また評価のロジックに関しては明示的に許可したもの以外否定が優先されます。

element 役割
Version IAM policyをJSON形式で表す文法のversionを表す。IAM policyのversionではないことに注意されたい。
Id IAM policyのidentifierであり、IAM policyのタイプや使用場面によって使用の可否が決まります。
Statement IAM policyのコアとなる部分で、上述した「誰 (何)が」、「誰 (何)に」、「何を」、「して良いか、あるいはしてはならないか」というルールを書く場所です。このelementには配列形式で複数のルールを与えることができます。
Sid Statementのidentifierであり、原則としてoptionalですが、使用場面によってrequiredになる場合があります。Statementのelementの一つ。
Principal AWS上のリソースを指定して「誰 (何)が」を表現するStatementのelementの一つ。
NotPrincipal Principalの否定に当たるStatementのelementの一つ。
Resource AWS上のリソースを指定して「誰 (何)に」を表現するStatementのelementの一つ。
NotResource Resourceの否定に当たるStatementのelementの一つ。
Action 対象となるリソースへの操作を指定して「何を」表現するStatementのelementの一つ。
NotAction Actionの否定に当たるStatementのelementの一つ。
Effect AllowまたはDenyを指定して「して良い (してはいけない)」を表すStatementのelementの一つ。
Condition Statementのelementの一つ。そのStatementを適用する条件を記述するelementであり、StringEqualsStringLikeなどの条件演算子とともに用います。

IAM role

IAM roleは特定のアクセス許可権限をもつIAMの単位です。
IAM userにおいてもAWS上のリソースに対してアクセス許可権限を設定でき、IAM roleとよく似た機能を持っているため、これに対比して説明することで理解の助けとします。
IAM userに対してアクセス許可権限を付与したい場合、あるIAM userに特定の権限を付与するという形式になります。一方で、IAM roleは一度作成すれば、それを任意のリソースに引き受けさせることでアクセス権限の付与を実現できます。
例えば、2人の異なるIAM userがいて、同じ権限を2人に付与したいとき、それぞれに同じ権限を設定し直す必要があるのに対し、IAM roleは一度作成したら、そのIAM roleを引き受けさせるだけで簡単に権限を委任できます。
また、IAM userに対してアクセス許可権限を設定すると、そのIAM userは付与された権限を剥奪されない限り権限を行使し続けられます。これに対して、IAM roleではSTSによって提供される一時的なアクセス権限情報を用いるため、あるIAM roleを引き受けたリソースが一時的にその権限を行使することができるようになり、そのroleの有効性やroleの引き受けが終了すると元の権限に戻る仕組みになります。
これにより、IAM roleを用いることで、AWS上のリソースは場面に応じた権限を自在にかつセキュアに引き受け、その権限を行使できるようになっています。

TerraformにおけるIAM role設定のややこしさ

consoleから設定する分には特段難しくはないですが、それをTerraformで実現するとある程度正しい知識を要求されるので多少骨が折れます。以下にその内容を書き下します。

IAM policyの設定の仕方が5種類ある

この記事を書くにあたって先行記事を調べたところ、こちらがありましたので、詳細はこの記事を参照してください。

  • aws_iam_policy_documentを用いる方法
  • jsonencodeを用いる方法
  • ヒアドキュメントを用いる方法
  • IAM policyが書かれたJSONファイルを読み込む方法
  • templateとなるJSONファイルを読み込む方法

AssumeRole policyを明示的に設定する必要がある

上に示したAssumeRole APIですが、consoleでroleを作成して、割り当てる際には意識することなく操作されます。
しかし、Terraformにおいてはaws_iam_roleassume_role_policy attributeに明示的にpolicyのJSONを渡してあげる必要があります。

IAM roleにIAM policyをアタッチする方法が2種類以上ある

  • managed_policy_arns attributeを使用する方法
    • この方法を用いると、Terraformで管理しているIAM roleにconsoleから手動でIAM policyをアタッチしてもTerraformからの設定が優先され、次のterraform applyで手動設定のものはdetachされます。
  • aws_iam_role_policy_attachment resourceを使用する方法
    • 上の逆であり、手動設定のIAM policyがTerraformの預かり知らぬところで設定されてもTerraformは無視するため、その手動設定のものは残り続けます。Terraformで誤って削除されたくないリソースなどに使うのが向いていると考えられます。

意見

以上を踏まえて結論の項に書いた内容が自分なりのデファクトスタンダード的な書き方になりました。

  1. IAM policyでaws_iam_policy_documentを用いる。
    • JSONのコメント機能の弱さなどJSON自体への使用感の悪さと、HCLのインフラリソース定義における秀出から、JSONよりHCLでの管理を好むから。
    • HCLのmodule機能などの応用に耐える仕様で設定ファイルを記述できるから。
  2. managed_policy_arns attributeを使用してIAM roleにIAM policyをattachする。
    • 原則全てのリソースをIaCで管理することで、IaCから享受できる利益を最大化したいから。
  3. プロジェクトのIAM roleの記述方法は1, 2に統一する。
    • 統一されていない記述方法が単純に読みにくいから。
    • 記述方法が統一されている中で、異なる書き方がある場合、そうしている意図が必ずあるはずであり、それが伝わりやすくなるから。

あとがき

記載内容の間違いのご指摘や訂正案、より良い内容のご提案など、忌憚なきご意見を頂けますと幸いです。
今後、アプリの環境ごと再現性を高めながらデプロイメントの簡単化及び高頻度化を図ることで、質の高いアプリを量産していくため、積極的に社内のinfrastructureをIaCで管理していきたいと考えております。

R&Dテックブログ

Discussion