【AWS】アイデンティティベースのポリシーとリソースベースのポリシーの関係性を理解したい

AWSのポリシーはユーザーに対してだけでなく、S3やECRなどのリソースに対してもアタッチ(付与)できます。
この記事では、ユーザーにアタッチするポリシー(アイデンティティベースのポリシー)と、リソースにアタッチするポリシー(リソースベースのポリシー)の関係性を、理解の範囲で説明したいと思います。

※この記事で説明する内容は、**同一アカウント内のポリシー評価**であり、クロスアカウントでは異なります。

## ターゲット

- AWSを使っているけど、ポリシーについて知らない・よく分からない
- ポリシーで許可(Allow)しか設定したことがない
- アイデンティティベースのポリシー、リソースベースのポリシーについて知りたい

## 要約

- アイデンティティベースのポリシーは、ユーザなどにアタッチできる
- リソースベースのポリシーは、リソースや認証ポリシーにアタッチできる
- アイデンティティベースのポリシーとリソースベースのポリシーの評価に優先順位はない
- 明示的な拒否(Deny) > 明示的な許可(Allow) > 暗黙的な拒否(ポリシーで何も設定していない)
- ポリシーを設定するときは、明示的な許可だけでなく明示的な拒否も設定した方がいい

## ポリシーとは

AWSの公式ドキュメントには、以下のように書かれています。

**ポリシーについて**
>ポリシーは AWS のオブジェクトであり、ID やリソースに関連付けて、これらのアクセス許可を定義します。アクセス許可ポリシーを作成してリソースへのアクセスを制限する場合、アイデンティティベースのポリシーまたはリソースベースのポリシーを選択できます。

いきなりポリシーがオブジェクトと言われても難しいですね。文章を要約すると、アクセス許可をする場合は以下の選択肢がある[^1]ということです。

[^1]: 正確には、[アクセスコントロールリスト (ACL)](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies.html#policies_acl)でもアクセス許可を行えますが、別のアカウントに対してとなるため、今回は割愛します。

- [アイデンティティベースのポリシー](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies.html#policies_id-based)
- [リソースベースのポリシー](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies.html#policies_resource-based)

アイデンティティベースのポリシーとリソースベースのポリシーは、ポリシータイプ[^2]のうちの二つです。この記事では、二つの違いや関係性について詳しく説明します。


[^2]: 6つのポリシータイプが存在します。詳しくは、[AWS公式ドキュメント - IAM でのポリシーとアクセス許可](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies.html)を参照してください。

## アイデンティティベースのポリシー、リソースベースのポリシーとは何か

AWSの公式ドキュメントより、アイデンティティベースのポリシー、リソースベースのポリシーとは何かを確認しながら説明します。

**アイデンティティベースのポリシー**
>アイデンティティベースのポリシーは、IAM ユーザー、グループ、ロールにアタッチされます。これらのポリシーを使用すると、そのアイデンティティが実行できる内容 (そのアクセス許可) を指定できます。

アイデンティティベースのポリシーは、以下の三つにアタッチ(付与)出来ます。

- IAM ユーザー
- グループ
- ロール

コンソール画面でIAM ユーザーに直接アタッチする場合を例にします。
IAM > ユーザー からアタッチしたいユーザー名を押下して、既存のポリシーを直接アタッチで選択するポリシーが**アイデンティティベースのポリシー**です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/9dd2a81f-c689-d1a2-7ddd-5e6b59383c1b.png)
これと同様に、グループにアタッチしたり、ロールにアタッチすることも出来ます。

**リソースベースのポリシー**
>リソースベースのポリシーをリソースにアタッチします。
(中略)
リソースベースのポリシーによって、誰がリソースにアクセスでき、彼らがリソースでどのようなアクションを実行できるかを指定できます。

リソースベースのポリシーは、S3やECRなどのリソースにアタッチします。
また、IAM ロールもリソースですので、信頼ポリシーというポリシーが用意されています。
ポリシータイプがリソースベースで、IAM ロール用に用意されたのが信頼ポリシーという表現で伝わるでしょうか？

コンソール画面でECRにアタッチする場合を例にします。
 ECR > リポジトリ からアタッチしたいリポジトリ名を押下して、ナビゲーションペイン(左側のメニュー)でPermissionsを選択するとアクセス許可画面が開きます。右上のポリシーJSONの編集で編集出来るのが**リソースベースのポリシー**です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/ec481ebc-3a2e-2e43-c614-d408f644373c.png)
こちらは、テンプレートは用意されておらず、JSON形式で一から作成が必要です。

## ポリシーの設定と評価

ユーザーとリソースそれぞれにポリシーがアタッチされている場合の評価を考える前に、ポリシーの設定と単体での評価を確認します。ここでは、基本的な内容のみを説明するので、詳しく知りたい方は以下を参照してください。
[AWS公式ドキュメント - JSON ポリシーの概要](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies.html)

ポリシーはJSON形式でアクションに対する評価(許可や制限)を設定できます。また、ワイルドカードとして\*(アスタリスク)を使用できます。
コンソール画面からIAMユーザーにアタッチするときに出てくる選択肢も、JSON形式で確認することができます。
設定していないアクションについては、拒否となります。

以下の例1はアイデンティティベースのポリシーで、S3の全バケットでのアクション拒否とECRの読み取りアクションの許可を表しています。

```json:ポリシー例1(アイデンティティベースのポリシー)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Action": [
                "s3:*"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:GetRepositoryPolicy",
                "ecr:DescribeRepositories",
                "ecr:ListImages",
                "ecr:DescribeImages",
                "ecr:BatchGetImage",
                "ecr:GetLifecyclePolicy",
                "ecr:GetLifecyclePolicyPreview",
                "ecr:ListTagsForResource",
                "ecr:DescribeImageScanFindings"
            ],
            "Resource": "arn:aws:ecr:<リージョン>:<アカウントID>:repository/<リポジトリ名>"
        }
    ]
}
```

このポリシーをユーザーなどにアタッチすることで、アクションを許可・拒否することができます。

JSONの中身を説明したいと思います。
Versionはおまじないですので、Statement内の要素について説明します。
**Effect**では、AllowまたはDenyを選択できます。次のActionで指定したものを許可するか拒否するかを決めます。
**Action**では、許可(Allow)または拒否(Deny)するアクションを指定します。
**Resource**では、どのリソースに対してかを表します。指定する場合は、[Amazonリソースネーム(ARN)](https://docs.aws.amazon.com/ja_jp/general/latest/gr/aws-arns-and-namespaces.html)で指定します。

Statementについて理解した後に例1をもう一度見ると、
一つ目の要素で全てのS3バケット```*```で全てのアクション```s3:*```が拒否されていて、
二つ目の要素でARNが```arn:aws:ecr:{リージョン}:{アカウントID}:repository/{リポジトリ名}```のECRリポジトリに読み込みに関連するアクションが許可されていることが分かると思います。

次の例2は、ECRのリソースベースのポリシーの例です。特定のユーザに対してアクションを拒否しています。

```json:ポリシー例2(リソースベースのポリシー)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyExample",
      "Effect": "Deny",
      "Principal": {
        "AWS": [
          "arn:aws:iam::<アカウントID>:user/<ユーザ名1>",
          "arn:aws:iam::<アカウントID>:user/<ユーザ名2>"
        ]
      },
      "Action": "ecr:*"
    }
  ]
}
```
リソースベースのポリシーは、アタッチされているリソースに適用されるため**Resource**が必要ない代わりに、適用対象を**Principal**で指定します。また、**NotPrincipal**とすることで適用対象の例外を指定することもできます。
指定できる対象は以下です。

- アカウント
- ユーザー
- ロール
- フェデレーティッドユーザー[^3]

[^3]: フェデレーティッドユーザー (外部アイデンティティ) は、社内ディレクトリの AWS 外部で管理されるユーザーですが、一時的なセキュリティ認証情報を使用して AWS アカウントにアクセスできるユーザーです。([AWS IAM のよくある質問 - ID フェデレーション](https://aws.amazon.com/jp/iam/faqs/)より引用)

では、以下の例3の場合はどうなるでしょうか？
(実際はこんなポリシーを作成することはありませんが、理解を深めるための例です。)

```json:ポリシー例3(リソースベースのポリシー)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Deny",
            "Action": [
                "s3:*"
            ],
            "Resource": "*"
        }
    ]
}
```

例3では、全てのS3バケットでの全アクションのAllowとDenyが書かれています。この場合、S3のアクションは全て実行できません。つまり、優先されるのはDenyです。


ここまでに分かったことをまとめると、以下となります。

|パターン| ポリシーで許可されている | ポリシーで拒否されている | 最終的な評価 |
|:-:|:-:|:-:|:-:|
|A |〇  | 〇  |  拒否 |
| B |〇 |  × |  許可 |
| C |× |  〇 |  拒否 |
| D|×  |  × |  拒否 |

先にDからみていきます。ポリシーで特に何も決めなければ拒否となりアクションは実行できません。
これより、デフォルトでは拒否に設定されていることが分かります。これを**暗黙的な拒否**といいます。

次にBです。ポリシーでAllowがあってDenyがない場合は許可になります。ポリシーでAllowにアクションを指定すると**明示的な許可**になります。Bでは、暗黙的な拒否だったアクションが許可になっているので、優先順位は
**明示的な許可 > 暗黙的な拒否**
と分かります。

最後にAです。例2のようにポリシーでAllowとDeny両方ある場合は拒否になります。ポリシーでDenyにアクションを指定すると**明示的な拒否**となります。Aでは明示的な許可と明示的な拒否がありますが、拒否になっていることから
**明示的な拒否 > 明示的な許可**
と分かります。

この説明をまとめると以下のようになります。

| 表現 | 説明 |
|:-:|:-:|
| 明示的な拒否  | ポリシーでアクションを拒否(Deny)した場合の評価  |
| 明示的な許可  |  ポリシーでアクションを許可(Allow)した場合の評価 |
| 暗黙的な拒否  | 全てのアクションに対してのデフォルトの評価  |

#### 優先順位
**明示的な拒否 > 明示的な許可 > 暗黙的な拒否**

## 複数タイプのポリシーを評価する

アイデンティティベースのポリシーとリソースベースのポリシーで同じアクションを設定した場合はどうなるでしょうか？
結論を言うと、ポリシータイプによって優先順位などはありません[^4]。両方のポリシーを併せて、単一のポリシーと同様に評価するイメージです。
[^4]: 評価の順序は存在しますが優先順位はなく、どちらのポリシーで許可(Aloow)や拒否(Deny)を設定しても同じ効果をもちます。詳しくは、[AWS公式ドキュメント - ポリシーの評価論理](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_evaluation-logic.html)を参照してください。
例えば、User1にS3全バケットフルアクセスのアイデンティティベースのポリシーがアタッチされているとします。この場合、個々のバケットにUser1に対してのポリシーがアタッチされていなくても、User1は全バケットへ全てのアクションを実行出来ます。
![aws_policy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/8347a430-5267-1091-85a1-89d3ffa78884.png)
User2はポリシーがアタッチされていません。しかし、BucketAにS3フルアクセスのリソースベースのポリシーがアタッチされているので、User2はBucketAに全てのアクションを実行できます。BucketBへのアクションは、全てのアクションが**暗黙的な拒否**となります。
![aws_policy_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/8939094c-6341-f072-943c-9c1a94f47395.png)
User3には、S3全バケットフルアクセスのアイデンティティベースのポリシーがアタッチされているとします。
しかし、BucketAにUser3からの全てのアクションを拒否するリソースベースのポリシーがアタッチされている場合、User3からBucketAへの全てのアクションが**明示的な拒否**となるため、実行することはできません。
![aws_policy_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/69c8570a-418b-ac2e-1afe-fee7416219e1.png)

## ポリシーを設定するときに意識すべきこと
もし、ルートユーザとUser4にのみS3のBucketCのフルアクセス権限を与えたい場合は、どのようなポリシーにしますか？
以下の例のような設定はおすすめしません。

**適切でない例1**
User4のアイデンティティベースのポリシーしか設定しない。
(ルートユーザは、全権限があるため設定不要)

```json:User4のポリシー(アイデンティティベースのポリシー)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::BucketC/*"
        }
    ]
}
```

**適切でない例2**
BucketCのリソースベースのポリシーしか設定しない。

```json:BucketCのポリシー(リソースベースのポリシー)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowUser4",
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::<アカウントID>:root",
          "arn:aws:iam::<アカウントID>:user/User4"
        ]
      },
      "Action": "s3:*"
    }
  ]
}
```

一見問題ないように感じますが、適切でない理由に気づけたでしょうか？
確かに設定されているポリシーがこれだけであれば、想定通りとなります。
しかし、間違えて許可するべきでないユーザに失敗例1と同様のポリシーをアタッチしたらどうなるでしょうか？
どちらの例でも、BucketCでアクションを実行できてしまいますね。このように、**明示的な許可**のみしか設定しない方法は、ひとつのミスで思わぬトラブルを起こします。
![aws_policy_x1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/c687a2ec-7fbb-9897-d114-e0b4cd39eb81.png)

これを踏まえて、適切な権限設定は以下の2つを守ることで実現すると分かります。

- 許可したい内容は**明示的な許可**をする
- それ以外は**明示的な拒否**をする

今回の例だと、以下のようなポリシーをそれぞれにアタッチすべきです。

```json:User4にアタッチする適切なポリシー(アイデンティティベースのポリシー)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::BucketC/*"
        }
    ]
}
```

```json:BucketCにアタッチする適切なポリシー(リソースベースのポリシー)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyOthers",
      "Effect": "Deny",
      "NotPrincipal": {
        "AWS": [
          "arn:aws:iam::<アカウントID>:root",
          "arn:aws:iam::<アカウントID>:user/User4"
        ]
      },
      "Action": "s3:*"
    }
  ]
}
```

このようにすれば、間違って他のユーザにポリシーをアタッチしてしまっても、BucketCでアクションを実行されません。
![aws_policy_x2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/e8382a44-55e8-d9a0-4cc1-a861db0399cd.png)


## まとめ
ポリシータイプのうち、よく使われる二つについてをまとめてみました。
一番伝えたかったのは、**明示的な拒否**をちゃんと設定しようということです。
長めの記事でしたが、AWSのポリシーについて少しでも理解の助けになれば嬉しいです。
また、この記事では触れていませんが余裕のある方は、他の4タイプのポリシーやクロスアカウントの場合についても調べてみてください。

## 参考サイト

[AWS公式ドキュメント - アイデンティティベースのポリシーおよびリソースベースのポリシー](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_identity-vs-resource.html)

[AWSのポリシーを使いこなそう　ポリシー設計につまづかないためのポイントを整理](https://devlog.arksystems.co.jp/2020/03/12/9338/)