新しくなった IAM 評価論理フローチャートをひとしきり愛でてみた

IAM 評価論理フローチャートが更新されたので 2021年11月17日は IAM 評価論理記念日。

コンバンハ、千葉(幸)です。

2021年11月17日、皆さんはこれがなんの日付だか分かりますか?

そうですね、IAM 評価論理フローチャートが刷新された日ですね。

Document_history_for_IAM_-_AWS_Identity_and_Access_Management

単一のアカウントにおけるプリンシパルからのリクエストが許可されるか拒否されるか、その評価ロジックを表したフローチャートです。親の顔より見た、という方も多いのではないでしょうか。

IAM はとにかく複雑なので一枚の図で漏れなく表現することはできないのですが、従来のものと比較してより詳細に、より正確に評価の流れが表されるようになりました。

穴が開くほど見つめていきましょう。

まとめ

  • 鬼門はリソースベースポリシー、そしてセッションプリンシパル

新旧フローチャート比較

変更が加わったのは以下ページです。

2021年12月現在、日本語のページは未更新のため表示言語を切り替えることで新旧を比較できます。

従来のフローチャートは以下。

PolicyEvaluationHorizontal

新しくなったフローチャートは以下です。

PolicyEvaluationHorizontal111621

何が変わったのか

まず……オシャレになりましたね。従来はカラフルな背景で賑やかな印象を受けますが、新フローチャートではシンプルでシックな装いでまとめられています。

それはそうとして、大きく変更点が加わったのは以下です。

  • リソースベースポリシーの評価の内訳が詳細に
  • アイデンティティベースポリシーの列が従来より左に
  • セッションポリシーの内訳がより詳細に

評価論理そのものが変わったわけではありませんが、評価論理を実態に即した形で読み取りやすくなりました。

リクエストの評価についておさらい

評価論理について見ていく前に基本的な部分をおさらいしておきましょう。

例えばわたしが AWS リソースの操作をしたいとします。この時のプリンシパルはわたしで、IAM ユーザーや IAM ロールなどの AWS エンティティで認証をし、マネジメントコンソールや AWS CLI などの手段で操作を行います。

この時の操作がリクエストで、リクエストには以下のような情報が含まれています。

  • アクションまたはオペレーション
  • リソース
  • プリンシパル
  • 環境データ
  • リソースデータ

これらの中に、各種ポリシーの情報も含まれています。ここでのポリシーとは、IAM リソースにアタッチされるアイデンティティベースポリシー、リソースにアタッチされるリソースベースポリシーのほか、Organizations SCPPermissions boundaryセッションポリシーのことを指します。

リクエストに含まれる情報は AWS によってリクエストコンテキストに収集され、AWS enforcement codeによって評価されます。評価の結果は許可拒否かのいずれかです。

拒否の中でも種類があり、ポリシーステートメントにDenyが含まれていることによる拒否は明示的な拒否(explicit deny)、許可がないことによる拒否は暗黙的な拒否(implict deny)と呼ばれます。

このあたりの詳細は以下を参照してください。

プリンシパル?エンティティ?

例では「わたし」がプリンシパルだと書きましたが、例えばわたしが IAM ユーザー Batchi の認証情報を使用してアクションを実行する場合、その IAM ユーザーBatchi も含めてプリンシパルになる、という考え方をすると理解しやすいかと思います。(正確な表現ではないので、文脈によって使い分けてください。)

IAM ユーザー Batchi は AWS 上で実体を持つので AWS エンティティです。わたしは AWS の世界では実体を持たないので AWS エンティティではありません。(もちろん現実世界では実体を持ってい、ます。)

わたしというプリンシパルが AWS エンティティ IAM ユーザー Batchi を使用してアクションを行うとき、IAM ユーザー Bathi もプリンシパルです。あるいは、リクエストコンテキストに収集された「プリンシパル」の情報にはプリンシパルが認証に使用した AWS エンティティの情報も含まれる、という表現ができます。

ややこしいですね。本エントリにおいて「プリンシパル」と書くときには、その言葉が指す内容にエンティティも含まれている、と思ってください。

アクション?オペレーション?

普段あまり使い分けを意識していなかったのですが、ドキュメントの記述に則れば以下の通りとなるようです。

  • アクション:マネジメントコンソールから操作する場合
  • オペレーション:AWS CLI や AWS API から操作する場合

本エントリでは特に気にせず「アクション」に一本化して記します。

従来の評価論理フローチャート

フローチャートの変更点をより詳細に理解するためにも、まずは従来のフローチャートをじっくり眺めてみましょう。

PolicyEvaluationHorizontal

便宜上、左の列から順に 1-6 の番号が振られているものと考えてください。

1. 拒否の評価

  • 暗黙的な拒否からスタート
  • リクエストコンテキストにあるすべてのポリシーを評価
  • いずれかのポリシーに Deny ステートメントがあるか?
    • ある場合:最終評価は拒否(明示的な拒否)
    • ない場合:2. へ進む

明示的な拒否となるかどうかは先頭で評価されます。いずれかのポリシーに Deny が含まれていた時点で最終評価が拒否で決定します。

「いずれかのポリシーに Deny...」という表現は正確に言えば「リクエストコンテキストに含まれるアクションもしくはオペレーションについて」という接頭辞がつきます。

例えばs3:GetObjectというアクションのリクエストの場合に、ec2:RunInstancesというアクションを Deny するポリシーがあったとしても関係ない、という意味です。

2. Organizations SCP

  • プリンシパルが所属する AWS アカウントには Organizations SCP が適用されているか?
    • ない場合:3. へ進む
    • ある場合:当該アクションは Allow されている?
      • はい:3. へ進む
      • いいえ:最終評価は拒否(暗黙的な拒否)

Organizations SCP は AWS アカウント単位でアクセス制御をするものとして機能します。ここで許可されていないとその時点で暗黙的な拒否となります。

3. リソースベースポリシー

ここがとにかく複雑なところです。

  • リクエスト対象のリソースはリソースベースポリシーを持つ?
    • 持たない場合:4. へ進む
    • 持つ場合:当該アクションは Allow されている?
      • いいえ:4. へ進む
      • はい:最終評価は許可(※例外あり)

従来のドキュメントでは以下の注記が記されています。

IAM ロールまたはユーザーの ARN をリソースベースのポリシーのプリンシパルとして指定した場合、このロジックは異なる動作をする可能性があります。ロールを引き受けるか、ユーザーをフェデレートすると、セッションが生成されます。その場合、リソースベースのポリシーによって付与された有効なアクセス許可は、そのプリンシパルのアクセス許可境界またはセッションポリシーによって制限されます。

リソースベースポリシーがややこしいのは、プリンシパルとして何を指定するかに幅がある点です。例えば「IAM ユーザーの認証情報を使用してアクションを行う」場合のプリンシパルは IAM ユーザーです。ところが「IAM ロールの認証情報を使用してアクションを行う」場合、プリンシパルは IAM ロールではなく「IAM ロールを引き受けたセッション」です。

リソースベースポリシーのPrincipalでは「IAMロール」でも「IAMロールを引き受けたセッション」でも指定でき、それぞれで評価結果が異なる場合があります。

このあたりは新フローチャートの方で改めて取り上げます。

4. IAM アクセス許可の境界

  • プリンシパルに Permissions boundary がアタッチされているか?
    • されていない場合:5. へ進む
    • されている場合:当該アクションは Allow されている?
      • はい:5. へ進む
      • いいえ:最終評価は拒否(暗黙的な拒否)

アクセス許可の境界は Permissions boundary とも呼ばれ、Organizations SCP と考え方は似ています。Organizations SCP がアカウント単位であったのに対し、Permissions boundary は IAM ユーザーもしくは IAM ロールのエンティティ単位でセットされます。

5. セッションポリシー

新フローチャートでは内容が変わっている部分です。

  • プリンシパルにセッションポリシーが適用されているか?
    • 適用されていない場合:6. へ進む
    • 提供されている場合:当該アクションは Allow されている?
      • はい:6. へ進む
      • いいえ:最終評価は拒否(暗黙的な拒否)

これを文面通りに受け取ると、間違ったことが書かれているように感じます。

セッションポリシーとはセッションプリンシパルにのみ割り当てられるポリシーです。セッションプリンシパルとは以下のいずれかを指します。

  • AssumeRole 系のアクションによる IAM ロールを引き受けたセッション
  • GetFederationToken によるフェデレーテッドユーザー

ここで、前者と後者で評価の結果は異なります。具体的にはフェデレーテッドユーザーの場合、セッションポリシーが適用されていない場合は最終評価が拒否(暗黙的な拒否)となります。

このあたりは新フローチャートでは具体的に記述されるようになりました。

6. アイデンティティベースポリシー

  • プリンシパルにアイデンティティベースポリシーがアタッチされているか?
    • されていない場合:最終評価は拒否(暗黙的な拒否)
    • されている場合:当該アクションは Allow されている?
      • はい:最終評価は許可
      • いいえ:最終評価は拒否(暗黙的な拒否)

プリンシパルである IAM ユーザーや IAM ロールにアタッチされているいわゆる IAM ポリシー(管理ポリシー、インラインポリシー)が評価対象です。IAM ユーザーの場合は所属する IAM グループから継承するポリシーも含まれます。

旧フローチャートではここが最後の評価基準でした。

新しくなった評価論理フローチャート

続いて新フローチャートを見ていきます。

PolicyEvaluationHorizontal111621

こちらも左列から順に 1-6 の番号が振られていると捉えてください。変更が加わっていない部分はサラッと流していきます。

1. 拒否の評価

  • 暗黙的な拒否からスタート
  • リクエストコンテキストにあるすべてのポリシーを評価
  • いずれかのポリシーに Deny ステートメントがあるか?
    • ある場合:最終評価は拒否(明示的な拒否)
    • ない場合:2. へ進む

特に変わりありません。

2. Organizations SCP

  • プリンシパルが所属する AWS アカウントには Organizations SCP が適用されているか?
    • ない場合:3. へ進む
    • ある場合:当該アクションは Allow されている?
      • はい:3. へ進む
      • いいえ:最終評価は拒否(暗黙的な拒否)

こちらも特に変わりありません。

3. リソースベースポリシー

  • リクエスト対象のリソースはリソースベースポリシーを持つ?
    • 持たない場合:4. へ進む
    • 持つ場合:当該アクションは Allow されている?
      • いいえ:4. へ進む
      • はい:最終評価はドキュメントを見てね

ややこしさのあまりフローチャートの中では完結しなくなりました。「ドキュメントの該当部を見てね」という記載になっています。その該当部は、フローチャートが更新されるおよそ一ヶ月ほど前に更新が入っており、その時に以下のエントリを書きました。

平たく言うとプリンシパルがセッションプリンシパルの時、リソースベースポリシーのPrincipalとして何を指定しているかによって挙動が枝分かれする、という話がされています。セッションプリンシパルとは先ほど説明した通り以下のことです。

  • AssumeRole 系のアクションによる IAM ロールを引き受けたセッション
  • GetFederationToken によるフェデレーテッドユーザー

該当部のドキュメントの更新内容をサマリした図が以下で、#2 と #5 で暗黙的な拒否となる、というのがポイントです。

フローチャートにおいては後に出てくる各種ポリシーが「許可なし」あるいは「適用できない」で埋められた状態で出てくるので、ちょっと混乱しがちです。チャートの順番に則った形で表現すると以下のようになるでしょうか。

プリンシパルが……

  • rootユーザーの場合:最終評価は許可
  • AWS サービスの場合:最終評価は許可
  • IAM ユーザーの場合:最終評価は許可
  • IAM ロールを引き受けたセッションの場合:
    • リソースベースポリシーのPrincipalで許可されているのが…
      • IAM ロール:4. へ進む
      • ロールを引き受けたセッション:最終評価は許可
  • GetFederationToken によるフェデレーテッドユーザーの場合:
    • リソースベースポリシーのPrincipalで許可されているのが…
      • IAM ユーザー:4. へ進む
      • フェデレーテッドユーザー:最終評価は許可

4. アイデンティティベースポリシー

  • プリンシパルにアイデンティティベースポリシーがアタッチされているか?
    • されていない場合:最終評価は拒否(暗黙的な拒否)
    • されている場合:当該アクションは Allow されている?
      • はい:5. へ進む
      • いいえ:最終評価は拒否(暗黙的な拒否)

旧フローチャートでは最終列だったアイデンティティベースポリシーがステップ 4 に移動してきています。考え方としてはそこまで変わりありませんね。

5. IAM アクセス許可の境界

  • プリンシパルに Permissions boundary がアタッチされているか?
    • されていない場合:6. へ進む
    • されている場合:当該アクションは Allow されている?
      • はい:6. へ進む
      • いいえ:最終評価は拒否(暗黙的な拒否)

特に変わりありません。

6. セッションポリシー

  • プリンシパルはセッションプリンシパルか?
    • いいえ:最終評価は許可
    • はい:セッションポリシーはアタッチされているか?
      • されていない:ロールセッションか?
        • そう:最終評価は許可
        • 違う:最終評価は拒否(暗黙的な拒否)
      • されている:当該アクションは Allow されているか?
        • はい:最終評価は許可
        • いいえ:最終評価は拒否(暗黙的な拒否)

ややこしいチャートになりましたね。

しつこいようですが、ここでのセッションプリンシパルとは以下を指します。

  • AssumeRole 系のアクションによる IAM ロールを引き受けたセッション
  • GetFederationToken によるフェデレーテッドユーザー

ロールセッション(IAM ロールを引き受けたセッション)か?という分岐で No になるということは、つまりフェデレーテッドユーザーであることを示します。

フェデレーテッドユーザーの場合セッションポリシーがアタッチされていないと暗黙的な拒否という扱いになりますので、そこが表現されたチャートになっています。

このあたりの挙動の違いは以下で確認しましたので、あわせてご参照ください。

おまけ:こんな時どうなる

一通りフローチャートを堪能している中で、以下のケースでの評価結果はどうなるんだ?と疑問が生じてきました。

  • プリンシパル:GetFederationToken によるフェデレーテッドユーザー
  • リソースベースポリシー:Principalでフェデレーテッドユーザーを指定して Allow
  • セッションポリシー:アタッチなし

その他のポリシーは特に設定がないものとします。

それは以下の #6 に相当するのでは?と思えなくもないですが、原文ではセッションポリシーが「Implicit deny(暗黙的な拒否)」と記載されており、その解釈に迷いました。

federatedUser

ここでの暗黙的な拒否とは「セッションポリシーは設定されているがそのポリシーの中で Allow が無い」を指していて、「セッションポリシーがアタッチされていない」ことについては言及していないのではと考えたのです。なのでやってみました。

S3 バケットcm-chiba-hogehogeで以下のバケットポリシーを設定します。ここではフェデレーテッドユーザーtestuserからのS3:GetObjectを許可しています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:sts::000000000000:federated-user/testuser"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::cm-chiba-hogehoge/*"
        }
    ]
}

ここで許可したユーザー名を指定して GetFederationToken を実行します。得られた一時的な認証情報を環境変数にセットし、セッションプリンシパルとしてアクションを実行できるようにします。

% OUTPUT=$(aws sts get-federation-token --name testuser)

export AWS_ACCESS_KEY_ID=$(echo $OUTPUT | jq -r .Credentials.AccessKeyId)
export AWS_SECRET_ACCESS_KEY=$(echo $OUTPUT | jq -r .Credentials.SecretAccessKey)
export AWS_SESSION_TOKEN=$(echo $OUTPUT | jq -r .Credentials.SessionToken)

aws sts get-federation-token実行時に--policy-arns--policyオプションを指定していないため、このセッションではセッションポリシーがアタッチされていない状態です。

プリンシパルがtestuserであることを確認した上で、S3:GetObjectを実行します。問題なく成功しました。

% aws sts get-caller-identity
{
    "UserId": "000000000000:testuser",
    "Account": "000000000000",
    "Arn": "arn:aws:sts::000000000000:federated-user/testuser"
}
% aws s3 cp s3://cm-chiba-hogehoge/test.txt .
download: s3://cm-chiba-hogehoge/test.txt to ./test.txt

リソースベースポリシーで許可されていないアクション、例えばs3:PutObjectを実行すると拒否されました。

% aws s3 cp ./test.txt s3://cm-chiba-hogehoge/test.txt
upload failed: ./test.txt to s3://cm-chiba-hogehoge/test.txt An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

ここでは「セッションポリシーは設定されているがそのポリシーの中で Allow が無い」も「セッションポリシーがアタッチされていない」も引っくるめて「暗黙的な拒否」だと考えて良さそうです。

くどいようですがこうなるのはフェデレーテッドユーザーセッションの時だけで、ロールを引き受けたセッションの場合には「セッションポリシーがアタッチされていない」場合の扱いは異なります。ややこし過ぎて堪らないですね。

終わりに

新しくなった IAM 評価論理フローチャートを穴が開くほど見つめてみました。

リソースベースポリシー、そしてセッションプリンシパルがとにかくややこしかったですね。この複雑さを理解してから旧フローチャートを見ると……「足りてないな」と感じる部分があります。更新前のドキュメントをどれだけ読み込んでもいまの理解には辿り着けなかっただろうことを考えると、「更新してくれてありがたい」と同時に「ドキュメントの記述を絶対的に信じるのは危険」という思いも湧いてきます。

そんなところが魅力なので、手を動かしながら正解を探っていく終わりのない旅を続けていきたいと思います。

以上、 チバユキ (@batchicchi) がお送りしました。