Principal 要素で IAM ロールを指定するのと IAM ロールを引き受けたセッションを指定するのは何が違うのか? 72 個のパターンで考えてみた

IAM ロールと評価論理を愛する男として、やらずにはいられませんでした。

コンバンハ、IAM 評価論理おじさん(幸)です。

先日、リソースベースポリシーの Principal 要素で指定するプリンシパルごとの挙動の違いが AWS ドキュメントに記載されました。

ドキュメントの追記内容をざっくり表したのが以下図で、囲っている部分が IAM ロールと IAM ロールを引き受けたセッション(ロールセッション)の差異を表しています。

リソースベースポリシーでロールセッションを直接許可している場合は、Permissions boundary やセッションポリシーの暗黙的な拒否が評価対象にならないということが判明し、とても満足な更新内容でした。

とは言え、ここで示されているのは以下のパターンにおける挙動です。

  • 同一アカウントでのアクセス
  • アイデンティティベースポリシーで許可なし
  • Permissions boundary(アクセス許可の境界)がアタッチされており許可なし
  • セッションポリシーがアタッチされており許可なし

他のパターンの時はどうなるんだ?というのが気になってしまったので整理してみました。それは、 IAM ロール、そして IAM 評価論理を愛する男として避けられない戦いだったのです。

IAM ロールとロールセッションの指定で何が違うのか

いきなりまとめです。

プリンシパルがロールセッションである場合に、リソースベースポリシーの Principal 要素 として IAM ロールを指定した時とロールセッションを指定した時との違いを表したのが以下です。ついでに「 AWS アカウント全体」を指定した時も付け合わせてみました。

右の(番号を除いた) 3 列が Principal 要素で指定するプリンシパルの場合分けを表します。最終的な評価結果として、以下のいずれかになります。

  • 明示的な拒否
  • 暗黙的な拒否
  • 許可

結果として、差異があるのは同一アカウントのアクセスにおいてリソースベースポリシーで許可があり、アクセス許可の境界もしくはセッションポリシーで許可がない場合(#14、#18)であることがわかりました。

この表を見て「なんのこっちゃ……」となった方も大丈夫です、後で見方が出てきます。

IAM ロールを引き受けたセッションとは何か

そもそものところをおさらいしておきます。

IAM ロールの権限を利用してアクションを実行したいとなった場合、その主体は IAM ロール自身ではありません。

IAM ユーザーや外部 Idp、AWS サービスなどが IAM ロールを引き受ける(ata:AssumeRole)アクションを実行すると一時的に有効な認証情報が払い出されます。その認証情報を引き受けたセッションが実行主体(プリンシパル)となります。 IAM ユーザーがロールを引き受けた例は以下の通りです。

IAMRoleSession

IAM ロールにはアイデンティティベースポリシーPermissions boundary をアタッチでき、そのロールを引き受けたセッションはそれらのポリシーを引き継ぎます。加えて、そのセッションのみで有効なセッションポリシーを割り当てることもできます。

同じ IAM ロールから異なる名称のセッションを生成できます。例えばマネジメントコンソールでスイッチロールを行う場合はスイッチ元の IAM ユーザー名がセッション名となりますし、EC2 インスタンス上のアプリケーションがロールを引き受ける際にはインスタンス ID がセッション名となります。AWS CLI で AssumeRole する際は、任意のセッション名を指定できます。

アクションの実行先の AWS リソースベースポリシーの Principal 要素では、IAM ロール単位での指定もセッション単位での指定もできます。IAM ロール単位で指定した場合、そのロールから生成されたセッションすべてに対して「許可/拒否」の指定を行います。

IAMRoleSession (1)

ロールセッション単位で指定した場合、対象がより具体的に制限されるというのはもちろんですが、一部のケースで評価論理が異なります。それが冒頭で述べた差異です。ごく一部のケースのみ、ロールセッション単位での指定特有の挙動を示します。

Principal ごとの評価論理の比較表の見方

冒頭の表について補足説明していきます。

Principal 指定のパターン

繰り返しになりますが、右の 3 列は以下を示しています。

  • AWS アカウントを指定した場合
  • IAM ロールを指定した場合
  • ロールセッションを指定した場合

それぞれ、以下のような形式での指定となります。

AWSアカウント単位指定

"Principal": { "AWS": "arn:aws:iam::123456789012:root" }

もしくは

"Principal": { "AWS": "123456789012" }

IAMロール指定

"Principal": { "AWS": "arn:aws:iam::AWS-account-ID:role/role-name" }

ロールセッション指定

"Principal": { "AWS": "arn:aws:sts::AWS-account-ID:assumed-role/role-name/role-session-name" }

試行パターンを構成する要素

以下のパターンの組み合わせからなります。

項目 パターン
アカウント構成 クロスアカウント / 同一アカウント
リソースベースポリシー 許可なし / 許可あり / 拒否
ID ベースポリシー 許可なし / 許可あり / 拒否
アクセス許可の境界・セッションポリシー 未アタッチ / 許可なし / 許可あり / 拒否

2 * 3 * 3 * 4 で 72 パターンです。

同一アカウントアクセスかクロスアカウントアクセスか

ここでは「ロールセッション」と「 AWS リソース」が同一 AWS アカウントにあるかどうかを表します。IAM ロールを引き受ける AWS エンティティ( IAM ユーザーなど)がどのアカウントに属するかは関係ありません。

CrossAccount2

リソースベースポリシーと ID ベースポリシー

それぞれ以下のパターンを考えます。

  • 許可なし(暗黙的な拒否)
  • 許可あり(許可)
  • 拒否(明示的な拒否)

リソースベースポリシーは S3 バケットなどの AWS リソースに割り当てるポリシーで、ID(アイデンティティ)ベースポリシーはいわゆる IAM ポリシーです。後者は、ここでは IAM ロールにアタッチされたポリシーを指します。

リソースベースポリシーで「許可なし」の場合、Principal 要素の場合分けは存在しないため、結合して表現しています。

evaliationLogic

アクセス許可の境界・セッションポリシー

どちらもガードレールとして機能するものです。これらで許可が与えられていないと暗黙的な拒否となります。そしてこれらで許可が与えられていても、それだけで最終評価が許可になることはありません。

今回の表ではアクセス許可の境界とセッションポリシーを合算した結果を一列で表しています。具体的には以下の組み合わせを表します。

Permissions_boundary

これらのポリシーは「アタッチされていない」状態と「アタッチされていて許可がない」状態では評価が変わることがある、というのがポイントです。

評価論理パターンの結果について補足

表を再掲します。せっかくなので今回は画像ではない表を貼っておきます。

# アカウント構成 リソースベース ポリシー ID ベース ポリシー 境界・セッションポリシー AWS アカウント指定 IAM ロール指定 ロールセッション指定
1 同一アカウント 許可なし 許可なし 未アタッチ 暗黙的な拒否 暗黙的な拒否 暗黙的な拒否
2 同一アカウント 許可なし 許可なし 許可なし 暗黙的な拒否 暗黙的な拒否 暗黙的な拒否
3 同一アカウント 許可なし 許可なし 許可 あり 暗黙的な拒否 暗黙的な拒否 暗黙的な拒否
4 同一アカウント 許可なし 許可なし 拒否 明示的な拒否 明示的な拒否 明示的な拒否
5 同一アカウント 許可なし 許可 あり 未アタッチ 許可 許可 許可
6 同一アカウント 許可なし 許可 あり 許可なし 暗黙的な拒否 暗黙的な拒否 暗黙的な拒否
7 同一アカウント 許可なし 許可 あり 許可 あり 許可 許可 許可
8 同一アカウント 許可なし 許可 あり 拒否 明示的な拒否 明示的な拒否 明示的な拒否
9 同一アカウント 許可なし 拒否 未アタッチ 明示的な拒否 明示的な拒否 明示的な拒否
10 同一アカウント 許可なし 拒否 許可なし 明示的な拒否 明示的な拒否 明示的な拒否
11 同一アカウント 許可なし 拒否 許可 あり 明示的な拒否 明示的な拒否 明示的な拒否
12 同一アカウント 許可なし 拒否 拒否 明示的な拒否 明示的な拒否 明示的な拒否
13 同一アカウント 許可 あり 許可なし 未アタッチ 暗黙的な拒否 許可 許可
14 同一アカウント 許可 あり 許可なし 許可なし 暗黙的な拒否 暗黙的な拒否 許可
15 同一アカウント 許可 あり 許可なし 許可 あり 暗黙的な拒否 許可 許可
16 同一アカウント 許可 あり 許可なし 拒否 明示的な拒否 明示的な拒否 明示的な拒否
17 同一アカウント 許可 あり 許可 あり 未アタッチ 許可 許可 許可
18 同一アカウント 許可 あり 許可 あり 許可なし 暗黙的な拒否 暗黙的な拒否 許可
19 同一アカウント 許可 あり 許可 あり 許可 あり 許可 許可 許可
20 同一アカウント 許可 あり 許可 あり 拒否 明示的な拒否 明示的な拒否 明示的な拒否
21 同一アカウント 許可 あり 拒否 未アタッチ 明示的な拒否 明示的な拒否 明示的な拒否
22 同一アカウント 許可 あり 拒否 許可なし 明示的な拒否 明示的な拒否 明示的な拒否
23 同一アカウント 許可 あり 拒否 許可 あり 明示的な拒否 明示的な拒否 明示的な拒否
24 同一アカウント 許可 あり 拒否 拒否 明示的な拒否 明示的な拒否 明示的な拒否
25 同一アカウント 拒否 許可なし 未アタッチ 明示的な拒否 明示的な拒否 明示的な拒否
26 同一アカウント 拒否 許可なし 許可なし 明示的な拒否 明示的な拒否 明示的な拒否
27 同一アカウント 拒否 許可なし 許可 あり 明示的な拒否 明示的な拒否 明示的な拒否
28 同一アカウント 拒否 許可なし 拒否 明示的な拒否 明示的な拒否 明示的な拒否
29 同一アカウント 拒否 許可 あり 未アタッチ 明示的な拒否 明示的な拒否 明示的な拒否
30 同一アカウント 拒否 許可 あり 許可なし 明示的な拒否 明示的な拒否 明示的な拒否
31 同一アカウント 拒否 許可 あり 許可 あり 明示的な拒否 明示的な拒否 明示的な拒否
32 同一アカウント 拒否 許可 あり 拒否 明示的な拒否 明示的な拒否 明示的な拒否
33 同一アカウント 拒否 拒否 未アタッチ 明示的な拒否 明示的な拒否 明示的な拒否
34 同一アカウント 拒否 拒否 許可なし 明示的な拒否 明示的な拒否 明示的な拒否
35 同一アカウント 拒否 拒否 許可 あり 明示的な拒否 明示的な拒否 明示的な拒否
36 同一アカウント 拒否 拒否 拒否 明示的な拒否 明示的な拒否 明示的な拒否
37 クロスアカウント 許可なし 許可なし 未アタッチ 暗黙的な拒否 暗黙的な拒否 暗黙的な拒否
38 クロスアカウント 許可なし 許可なし 許可なし 暗黙的な拒否 暗黙的な拒否 暗黙的な拒否
39 クロスアカウント 許可なし 許可なし 許可 あり 暗黙的な拒否 暗黙的な拒否 暗黙的な拒否
40 クロスアカウント 許可なし 許可なし 拒否 明示的な拒否 明示的な拒否 明示的な拒否
41 クロスアカウント 許可なし 許可 あり 未アタッチ 暗黙的な拒否 暗黙的な拒否 暗黙的な拒否
42 クロスアカウント 許可なし 許可 あり 許可なし 暗黙的な拒否 暗黙的な拒否 暗黙的な拒否
43 クロスアカウント 許可なし 許可 あり 許可 あり 暗黙的な拒否 暗黙的な拒否 暗黙的な拒否
44 クロスアカウント 許可なし 許可 あり 拒否 明示的な拒否 明示的な拒否 明示的な拒否
45 クロスアカウント 許可なし 拒否 未アタッチ 明示的な拒否 明示的な拒否 明示的な拒否
46 クロスアカウント 許可なし 拒否 許可なし 明示的な拒否 明示的な拒否 明示的な拒否
47 クロスアカウント 許可なし 拒否 許可 あり 明示的な拒否 明示的な拒否 明示的な拒否
48 クロスアカウント 許可なし 拒否 拒否 明示的な拒否 明示的な拒否 明示的な拒否
49 クロスアカウント 許可 あり 許可なし 未アタッチ 暗黙的な拒否 暗黙的な拒否 暗黙的な拒否
50 クロスアカウント 許可 あり 許可なし 許可なし 暗黙的な拒否 暗黙的な拒否 暗黙的な拒否
51 クロスアカウント 許可 あり 許可なし 許可 あり 暗黙的な拒否 暗黙的な拒否 暗黙的な拒否
52 クロスアカウント 許可 あり 許可なし 拒否 明示的な拒否 明示的な拒否 明示的な拒否
53 クロスアカウント 許可 あり 許可 あり 未アタッチ 許可 許可 許可
54 クロスアカウント 許可 あり 許可 あり 許可なし 暗黙的な拒否 暗黙的な拒否 暗黙的な拒否
55 クロスアカウント 許可 あり 許可 あり 許可 あり 許可 許可 許可
56 クロスアカウント 許可 あり 許可 あり 拒否 明示的な拒否 明示的な拒否 明示的な拒否
57 クロスアカウント 許可 あり 拒否 未アタッチ 明示的な拒否 明示的な拒否 明示的な拒否
58 クロスアカウント 許可 あり 拒否 許可なし 明示的な拒否 明示的な拒否 明示的な拒否
59 クロスアカウント 許可 あり 拒否 許可 あり 明示的な拒否 明示的な拒否 明示的な拒否
60 クロスアカウント 許可 あり 拒否 拒否 明示的な拒否 明示的な拒否 明示的な拒否
61 クロスアカウント 拒否 許可なし 未アタッチ 明示的な拒否 明示的な拒否 明示的な拒否
62 クロスアカウント 拒否 許可なし 許可なし 明示的な拒否 明示的な拒否 明示的な拒否
63 クロスアカウント 拒否 許可なし 許可 あり 明示的な拒否 明示的な拒否 明示的な拒否
64 クロスアカウント 拒否 許可なし 拒否 明示的な拒否 明示的な拒否 明示的な拒否
65 クロスアカウント 拒否 許可 あり 未アタッチ 明示的な拒否 明示的な拒否 明示的な拒否
66 クロスアカウント 拒否 許可 あり 許可なし 明示的な拒否 明示的な拒否 明示的な拒否
67 クロスアカウント 拒否 許可 あり 許可 あり 明示的な拒否 明示的な拒否 明示的な拒否
68 クロスアカウント 拒否 許可 あり 拒否 明示的な拒否 明示的な拒否 明示的な拒否
69 クロスアカウント 拒否 拒否 未アタッチ 明示的な拒否 明示的な拒否 明示的な拒否
70 クロスアカウント 拒否 拒否 許可なし 明示的な拒否 明示的な拒否 明示的な拒否
71 クロスアカウント 拒否 拒否 許可 あり 明示的な拒否 明示的な拒否 明示的な拒否
72 クロスアカウント 拒否 拒否 拒否 明示的な拒否 明示的な拒否 明示的な拒否

流石にすべてのパターンで実際に手を動かして試したわけではありません。以下の原則に則り結果を埋めている部分がほとんどです。

  • どれかひとつのポリシーで拒否があれば最終結果は明示的な拒否
  • クロスアカウントの場合リソースベースポリシーと ID ベースポリシーの双方で許可がないと暗黙的な拒否
  • ID ベースポリシーが評価対象になる場合、アクセス許可の境界・セッションポリシーが「許可なし」だと暗黙的な拒否
  • アクセス許可の境界・セッションポリシーだけで許可があっても暗黙的な拒否
  • どれでも許可がないと暗黙的な拒否

改めて見ると、拒否が強いというのがよく分かりますね。

試行したパターンから抜粋

実際に手を動かして試したパターンがいくつかあります。基本的には以下の構成で行いました。

IAMRoleSession_test

EC2 インスタンス上で IAM ロールを引き受けて S3 に関する AWS CLI を実行します。AWS 管理ポリシーAmazonS3FullAccessを ID ベースポリシーや Permissions boundary にアタッチ/デタッチし許可の有無をコントロールします。

今回の評価論理には直接関係ありませんが、Systems Manager 経由での接続が可能なように AWS 管理ポリシーAmazonSSMManagedInstanceCore( S3 アクションの許可なし)もアタッチします。

試行の結果をいくつかピックアップして紹介します。

なお、#14 相当の試行は冒頭で取り上げた以下エントリで既に済ませているため、ここでは割愛します。

A. 同一アカウントアクセスでアカウント単位の許可を与えないパターン

#5 に該当する部分です。

そもそも同一アカウントのアクセスにおいて「アカウント単位の許可を与えない」ことができるのか?とふと疑問が浮かびました。

S3 のバケットポリシーをブランクにし、バケット ACL から ACL の読み取り権限を削除しました。

S3ACL

( S3 の ACL については以下をご参考ください。)

IAM ロール側のポリシー設定は以下の状態でコマンドを実行していきます。

ポリシー種別 アタッチするポリシー
アイデンティティベースポリシー AmazonSSMManagedInstanceCore、AmazonS3FullAccess
Permissions boundary なし

aws sts get-caller-identityによりプリンシパルを確認します。(これ以降もおまじないのように実施していきます。)

sh-4.2$ aws sts get-caller-identity
{
    "Account": "000000000000",
    "UserId": "AROAQ3BIIH735AQHQPIOW:i-047875da17caa9cc2",
    "Arn": "arn:aws:sts::000000000000:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-047875da17caa9cc2"
}

↑ロールセッションとしてコマンドを実行しているが分かります。

バケット ACL の読み取りを行います。

sh-4.2$ aws s3api get-bucket-acl --bucket chibayuki-hoge-hoge
{
    "Owner": {
        "DisplayName": "members-27412",
        "ID": "xxxxxxxxxx893e1296771435ec161876764435f7f067bcf180a50exxxxxxxxxx"
    },
    "Grants": [
        {
            "Grantee": {
                "Type": "CanonicalUser",
                "DisplayName": "members-27412",
                "ID": "xxxxxxxxxx893e1296771435ec161876764435f7f067bcf180a50exxxxxxxxxx"
            },
            "Permission": "READ"
        },
        {
            "Grantee": {
                "Type": "CanonicalUser",
                "DisplayName": "members-27412",
                "ID": "xxxxxxxxxx893e1296771435ec161876764435f7f067bcf180a50exxxxxxxxxx"
            },
            "Permission": "WRITE"
        },
        {
            "Grantee": {
                "Type": "CanonicalUser",
                "DisplayName": "members-27412",
                "ID": "xxxxxxxxxx893e1296771435ec161876764435f7f067bcf180a50exxxxxxxxxx"
            },
            "Permission": "WRITE_ACP"
        }
    ]
}

↑バケットポリシーでも ACL でも明示的に許可を与えていない状態ですが、ACL を参照できました。

念のためバケットポリシーも確認します。参照できた上で、ポリシーが存在しないことが確認できました。

sh-4.2$ aws s3api get-bucket-policy --bucket chibayuki-hoge-hoge

An error occurred (NoSuchBucketPolicy) when calling the GetBucketPolicy operation: The bucket policy does not exist

暗黙的な許可とでも表現するのか、同一アカウントアクセスにおいてはアカウント単位の許可を明示的に与えなくても問題なく動作することがわかりました。

この考え方が他のリソースベースポリシーでも同一かは確認できていません。少なくともリソースベースポリシーの一種である IAM ロールの信頼ポリシーでは、同一アカウントであっても明示的な許可が必要です。リソースごとに詳細を確認するようにするといいでしょう。

番外編:SNS トピックポリシーで同一アカウントへの許可を与えない

S3 バケット以外のリソースベースポリシーでも試してみようということで、SNS トピックで試してみました。

デフォルトで生成されるポリシーを少しカスタマイズし、以下のようなポリシーを持つ SNS トピックを作成しました。

{
  "Version": "2008-10-17",
  "Id": "__default_policy_ID",
  "Statement": [
    {
      "Sid": "__default_statement_ID",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::000000000000:root"
      },
      "Action": [
        "SNS:Publish",
        "SNS:RemovePermission",
        "SNS:SetTopicAttributes",
        "SNS:DeleteTopic",
        "SNS:ListSubscriptionsByTopic",
        "SNS:GetTopicAttributes",
        "SNS:Receive",
        "SNS:AddPermission",
        "SNS:Subscribe"
      ],
      "Resource": "arn:aws:sns:us-east-1:999999999999:MyTopic"
      }
  ]
}

このリソースはアカウント9999...に存在するものですが、Principal として0000...のみを指定しています。

この状態で9999...のアカウント上での操作で、問題なく SNS トピックを削除できました。「暗黙的な許可」は SNS トピックでも有効なようでした。

B. ID ベースで許可なしで権限の境界で許可ありのパターン

#15 のパターンでの試行です。

IAM ロール側でのポリシー構成は以下です。

ポリシー種別 アタッチするポリシー
アイデンティティベースポリシー AmazonSSMManagedInstanceCore
Permissions boundary カスタムポリシー

アイデンティティベースポリシーでは S3 に関する許可がありません。

Permissions boundary にはひとつのポリシーしかアタッチできないため、AmazonSSMManagedInstanceCoreAmazonS3FullAccessを合算した以下のポリシーを作成しアタッチしました。

カスタムポリシー

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:DescribeAssociation",
                "ssm:GetDeployablePatchSnapshotForInstance",
                "ssm:GetDocument",
                "ssm:DescribeDocument",
                "ssm:GetManifest",
                "ssm:GetParameter",
                "ssm:GetParameters",
                "ssm:ListAssociations",
                "ssm:ListInstanceAssociations",
                "ssm:PutInventory",
                "ssm:PutComplianceItems",
                "ssm:PutConfigurePackageResult",
                "ssm:UpdateAssociationStatus",
                "ssm:UpdateInstanceAssociationStatus",
                "ssm:UpdateInstanceInformation"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssmmessages:CreateControlChannel",
                "ssmmessages:CreateDataChannel",
                "ssmmessages:OpenControlChannel",
                "ssmmessages:OpenDataChannel"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2messages:AcknowledgeMessage",
                "ec2messages:DeleteMessage",
                "ec2messages:FailMessage",
                "ec2messages:GetEndpoint",
                "ec2messages:GetMessages",
                "ec2messages:SendReply"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": "*"
        }
    ]
}

この状態で S3 バケットポリシーの Principal 要素での指定を変更しつつ挙動を確認していきます。

ロールセッション指定の場合

問題なくアクションは成功します。セッションポリシーに直接許可が与えられているため、アクセス許可の境界が許可なしの場合(#14相当)でも結果は同じです。

sh-4.2$ aws sts get-caller-identity
{
    "Account": "000000000000",
    "UserId": "AROAQ3BIIH735AQHQPIOW:i-047875da17caa9cc2",
    "Arn": "arn:aws:sts::000000000000:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-047875da17caa9cc2"
}
sh-4.2$ aws s3api get-bucket-policy --bucket chibayuki-hoge-hoge
{
    "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:sts::000000000000:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-047875da17caa9cc2\"},\"Action\":\"s3:*\",\"Resource\":[\"arn:aws:s3:::chibayuki-hoge-hoge\",\"arn:aws:s3:::chibayuki-hoge-hoge/*\"]}]}"
}

IAM ロール指定の場合

今回試行した中で一番むず過ぎるだろ……となった結果です。

IAM ロール指定でも問題なくアクションは成功します。

sh-4.2$ aws sts get-caller-identity
{
    "Account": "000000000000",
    "UserId": "AROAQ3BIIH735AQHQPIOW:i-047875da17caa9cc2",
    "Arn": "arn:aws:sts::000000000000:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-047875da17caa9cc2"
}
sh-4.2$ aws s3api get-bucket-policy --bucket chibayuki-hoge-hoge
{
    "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::000000000000:role/AmazonSSMRoleForInstancesQuickSetup\"},\"Action\":\"s3:*\",\"Resource\":[\"arn:aws:s3:::chibayuki-hoge-hoge\",\"arn:aws:s3:::chibayuki-hoge-hoge/*\"]}]}"
}

「リソースベースポリシーで許可が与えられているからアクセス許可の境界の状態に関わらず成功する」というわけではありません。ここでアクセス許可の境界で「許可なし」の場合(#14相当)は失敗するからです。

以下のような理解をしました。

  • プリンシパルがロールセッションで Principal 要素で IAM ロールを指定して許可を与えた場合、アクセス許可の境界及びセッションポリシーが評価対象になる
  • アクセス許可の境界及びセッションポリシーが「未アタッチ」もしくは「許可あり」の場合、評価結果は許可となる
    • 「許可なし」の場合は暗黙的な拒否となる

難しいですね。

AWS アカウント単位指定の場合

こちらの場合はアクションは拒否されます。

sh-4.2$ aws sts get-caller-identity
{
    "Account": "000000000000",
    "UserId": "AROAQ3BIIH735AQHQPIOW:i-047875da17caa9cc2",
    "Arn": "arn:aws:sts::000000000000:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-047875da17caa9cc2"
}
sh-4.2$ aws s3api get-bucket-policy --bucket chibayuki-hoge-hoge

An error occurred (AccessDenied) when calling the GetBucketPolicy operation: Access Denied

わたしはこれを勝手に「許可を与える抽象度が高過ぎる」と呼んでいます。

ロールセッション < IAM ロール < AWS アカウントの順で抽象度は高くなります。(逆に言えば左に行くほどより具体的な許可を与えている。)

C. ID ベースで許可ありで権限の境界で許可なしのパターン

#18 相当のパターンです。IAM ロール側のポリシー構成は以下の通りです。

ポリシー種別 アタッチするポリシー
アイデンティティベースポリシー AmazonSSMManagedInstanceCore、AmazonS3FullAccess
Permissions boundary AmazonSSMManagedInstanceCore

結果的には #14 と同じ評価になりました。アクセス許可の境界が評価対象になる場合、そこでの「許可なし」を ID ベースポリシーによる許可で上書きできるようなものではない、ということですね。

ロールセッション指定の場合

リソースベースポリシーによるロールセッションへの直接的な許可により、 Permissions boundary は評価対象とならずアクションは成功します。

sh-4.2$ aws sts get-caller-identity
{
    "Account": "000000000000",
    "UserId": "AROAQ3BIIH735AQHQPIOW:i-047875da17caa9cc2",
    "Arn": "arn:aws:sts::000000000000:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-047875da17caa9cc2"
}
sh-4.2$ aws s3api get-bucket-policy --bucket chibayuki-hoge-hoge
{
    "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:sts::000000000000:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-047875da17caa9cc2\"},\"Action\":\"s3:*\",\"Resource\":[\"arn:aws:s3:::chibayuki-hoge-hoge\",\"arn:aws:s3:::chibayuki-hoge-hoge/*\"]}]}"
}

IAM ロール指定の場合

リソースベースポリシーによる直接の許可がないため、Permissions boundary が評価対象となりそこで拒否されます。

sh-4.2$ aws sts get-caller-identity
{
    "Account": "000000000000",
    "UserId": "AROAQ3BIIH735AQHQPIOW:i-047875da17caa9cc2",
    "Arn": "arn:aws:sts::000000000000:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-047875da17caa9cc2"
}
sh-4.2$ aws s3api get-bucket-policy --bucket chibayuki-hoge-hoge

An error occurred (AccessDenied) when calling the GetBucketPolicy operation: Access Denied

AWS アカウント単位の指定の場合

そもそもリソースベースポリシーによる許可がないため、暗黙的な拒否となります。

sh-4.2$ aws sts get-caller-identity
{
    "Account": "000000000000",
    "UserId": "AROAQ3BIIH735AQHQPIOW:i-047875da17caa9cc2",
    "Arn": "arn:aws:sts::000000000000:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-047875da17caa9cc2"
}
sh-4.2$ aws s3api get-bucket-policy --bucket chibayuki-hoge-hoge

An error occurred (AccessDenied) when calling the GetBucketPolicy operation: Access Denied

結局ロールセッションを指定するのってどういう場合?

ロールセッションを指定することで評価論理の結果が変わることが分かりました。

とは言え、果たしてどういった場合にロールセッション指定を行うのでしょうか? IAM ロールを指定するだけでなく「それを誰が引き受けるのか」まで指定するのは細かすぎる気がします。(もちろんセッション名が必ず引き受け元のエンティティ名と一緒になるというわけではありません。)

実際の運用を考えると、セッション名まで指定するのは変更や管理の負荷が高くなり現実的ではないと考えます。よって、もし使うとしたら「既にロールが使われている環境でワークアラウンド的に使用する」というものになるのでは、と思いました。

SessionRole

複数の EC2 インスタンスや IAM ユーザーが共通して使用する IAM ロールがあった場合に、それを引き受ける特定のエンティティ(図では赤字で表現しているもの)にのみ新たなアクセス要件が発生した、というケースです。

要件が恒久的なものであればきちんと IAM ロールを分離して管理するのがよいですが、「ちょっと一時的にこのユーザーにだけアクセスさせたい」という場合に、リソースベースポリシー側の指定を変更することで実現するパターンを想定しています。

とは言えどの道リソースベースポリシーの修正は必要になるため、そこまで筋がよい方法ではない気もしています。「こういうケースでロールセッション指定を使っている」という例があれば、ぜひ聞いてみたいです。

終わりに

プリンシパルがロールセッションの時に、リソースベースポリシー側の Principal 要素で IAM ロールを指定した場合とロールセッションを指定した場合の評価論理の差異を確認しました。

クロスアカウントの場合は差異がなく、同一アカウントアクセスの場合のごく一部で差異があることが分かりました。明示的な拒否などは自明な部分もありパターンから除外してもよかったのですが、ひとまずすべて網羅したことで頭の中がスッキリして良かったです。

今回の表を完成させるために追加で手を動かして初めて把握した部分もあり、改めて評価論理は難しいなと認識しました。この難しさが皆さんに伝わっていれば幸いです。

最後に頑張って作った表をおかわりしておしまいです。

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