サイトアイコンmaita tomoya dev io

#91 AWS IAMの全体像を理解する — ロール・ポリシー・信頼関係の仕組み

はじめに

S3バケットに対して別サービスのロールからアクセスを許可する設計をした際、IAMの理解が曖昧なまま進めてしまい時間がかかった。この記事ではIAMの全体像を整理し、「誰が」「何に」「何をできるか」を制御する仕組みを体系的にまとめる。

IAMの登場人物

IAMには大きく4つの概念がある。

概念何か
ユーザー人間がAWSを操作するためのID開発者のAWSアカウント
ロールサービスやアプリが一時的に引き受ける権限セットLambda関数、ECSタスク
ポリシー「何を許可/拒否するか」のJSON定義S3読み取り許可
信頼ポリシー「誰がこのロールを引き受けられるか」の定義ECSタスクがこのロールを使える

ポリシーの種類

アイデンティティベースポリシー(IAMポリシー)

ロールやユーザーに「あなたは何ができるか」をアタッチする。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-bucket/output/*"
    }
  ]
}

これをロールにアタッチすると、そのロールは my-bucket/output/* を読める。

リソースベースポリシー(バケットポリシー等)

リソース側に「誰がアクセスしてよいか」を定義する。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/my-app-role"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-bucket/output/*"
    }
  ]
}

S3バケット自身が「このロールからのGetObjectを許可する」と宣言している。

両方必要なケース

同一アカウント内: どちらか一方で許可されていればアクセス可能。

クロスアカウント: 両方で許可が必要。

  • 呼び出し側ロールのIAMポリシーで Allow
  • リソース側のバケットポリシーで Principal に呼び出し側を指定

信頼ポリシー(AssumeRole)

ロールには「誰がこのロールになれるか」を定義する信頼ポリシーがある。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ecs-tasks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

これにより、ECSタスクがこのロールを引き受けて動作できる。

評価ロジック

AWSがアクセスを判定する順序:

1. 明示的Deny → 即拒否(最優先)
2. Organizations SCP → 組織レベルの制限
3. リソースベースポリシー → 許可があればOK(同一アカウント)
4. アイデンティティベースポリシー → 許可があればOK
5. Permissions boundary → 上限チェック
6. セッションポリシー → AssumeRole時の追加制限
7. デフォルト → 暗黙的Deny

重要なのは「明示的Denyは何よりも優先される」という点である。

実務でよくあるパターン

パターン1: サービスAからS3バケットへのアクセス

[サービスA (ECSタスク)]
    ↓ AssumeRole
[タスクロール]
    ↓ IAMポリシーで s3:GetObject 許可
[S3バケット]
    ↓ バケットポリシーでタスクロールを Principal に指定
[オブジェクト取得成功]

同一アカウントならバケットポリシー OR IAMポリシーどちらか一方でよい。

パターン2: Presigned URL生成

Presigned URLは「URLを生成したロールの権限」でアクセスする。つまり:

  • URL生成側のロールに s3:GetObject が必要
  • バケットポリシーでそのロールを許可する(クロスアカウントの場合)

URLにアクセスするユーザーはAWS認証不要だが、URL生成側の権限が足りなければ403になる。

パターン3: 最小権限の設計

{
  "Effect": "Allow",
  "Action": "s3:GetObject",
  "Resource": "arn:aws:s3:::my-bucket/output/*"
}
  • Action: 必要な操作のみ(s3:* は使わない)
  • Resource: 必要なパスのみ(バケット全体は避ける)
  • Condition: 必要に応じてIPやVPCエンドポイントで制限

デバッグ手順

アクセスが拒否された場合のチェックリスト:

  1. IAMポリシー確認: ロールにアタッチされたポリシーでActionとResourceが許可されているか
  2. リソースポリシー確認: バケットポリシーのPrincipalにロールARNが含まれているか
  3. 明示的Deny確認: どこかにDenyステートメントがないか
  4. 信頼ポリシー確認: サービスがそのロールをAssumeRoleできるか
  5. ARN確認: アカウントID、リージョン、リソース名にtypoがないか
  6. IAM Policy Simulator: AWS ConsoleのPolicy Simulatorで検証

よくある罠

原因対策
同一アカウントなのに403リソースARNのtypoコピペで正確に
クロスアカウントで403片方しか設定してない両側で許可必要
ワイルドカードが効かないResource: "*" だが s3:ListBucket は バケットARN指定が必要Action別にResourceを分ける
Presigned URLが403URL生成側ロールの権限不足生成側に GetObject 付与

まとめ

質問答え
誰が操作するか→ ロール/ユーザーを特定
何に対して操作するか→ リソースARNを特定
何の操作か→ Action(GetObject, PutObject等)を特定
どこに書くか→ IAMポリシー or リソースポリシー or 両方

IAMの設計は「主語(誰が)」「動詞(何をする)」「目的語(何に対して)」を明確にすれば迷わない。クロスアカウントの場合だけ「両側で許可」を忘れなければ大丈夫である。