「Amazon S3 マルチリージョンアクセスポイント(MRAP)」のアクセス制御

# 1. はじめに
- 先日の記事[「【ハンズオン】「Amazon S3 マルチリージョンアクセスポイント(MRAP)の使用を開始する」をやってみる」](https://qiita.com/mksamba/items/06eed774ea045179d322)にて、MRAPの基本的な使い方（設定方法、レプリケーションなど）を確認した。
- 本記事では、MRAPへのアクセス制御の方針について、自分が運用しているシステムの場合のユースケースに基づき、何パターンかの設定方法の検証を行う。（MRAPへのアクセス制御の方法は、通常のS3バケットへのアクセス制御をするのと基本的には同じだが、MRAPがある場合、S3バケットとは別にMRAP自体にもアクセスポリシーが設定可能であり、やや複雑でよく分からないため）

# 2. 構成図／接続要件

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/0cacc6fd-15d5-4bc5-8036-4f7105fc635c.png)



- 今回は以下のユースケースを想定したアクセス制御設定を検証する。
    - AWSアカウント#2のEC2インスタンスから、AWSアカウント#1のMRAPを指定して、後段のS3にファイルをアップロードする。
    - EC2インスタンスからMRAPへの経路は、インターネット経由(EC2インスタンスにEIPを付与)、もしくはVPCエンドポイント経由(EC2インスタンスがあるVPCにS3(MRAP)用インタフェースエンドポイント作成)のどちらかとする。
    - AWSアカウント#2のEC2インスタンスからのみアクセスを許可する。（許可条件としてSRCIPもしくはVPCエンドポイントも追加する。）
    - PUTのみを許可する。(LISTやGETを許可しない。)

# 3. 手順

## 3.1 検証パターン

|案  |経路  |方式  | 概要 |
|---|---|---|---|
|1 |インターネット|AWSアカウント#1側のIAMロール|AWSアカウント#1側のIAMロールにアクセス許可を記述し、AWSアカウント#2側でIAMロールをAssumeRoleする。
|2 |VPCエンドポイント|AWSアカウント#1側のIAMロール|同上
|3 |インターネット|バケットポリシー|AWSアカウント#1側のMRAPのアクセスポリシーとS3バケットのバケットポリシーにアクセス許可を記述し、AWSアカウント#2からのみにアクセスを制限する。
|4 |VPCエンドポイント|バケットポリシー|同上

## 3.2 案1 インターネット経由／IAMロールで制御

### 方式
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/aeba104d-48e4-5559-385d-67991a1e51d1.png)


- MRAPアクセスポリシー／S3バケットポリシーを使わず、AWSアカウント#1側のIAMポリシー／IAMロールのみで制御する。
- EC2インスタンスとMRAP間の経路はインターネットとする。

### 設定手順

- AWSアカウント#1でIAMポリシーを作成する。
    - MRAPに対して特定のSRCIPからのアクセスを許可する。
    - 後段のS3バケットに対し、MRAP経由のアクセスのみ許可する。

``` account1-mrap-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject"
            ],
            "Resource": [
                "[東京のバケットARN]",
                "[東京のバケットARN]/*"
            ],
            "Condition": {
                "StringEquals": {
                    "s3:DataAccessPointArn":"[MRAPのARN]"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject"
            ],
            "Resource": [
                "[大阪のバケットARN]",
                "[大阪のバケットARN]/*"
            ],
            "Condition": {
                "StringEquals": {
                    "s3:DataAccessPointArn":"[MRAPのARN]"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
            ],
            "Resource": [
                "[MRAPのARN]"
                "[MRAPのARN]/object/*"
            ],
			"Condition": {
				"IpAddress": {
					"aws:SourceIp": "[AWSアカウント#2のEC2のグローバルIP]/32"
				}
            }
        }
    ]
}
```
- AWSアカウント#1でIAMロール(上記のIAMポリシーをアタッチ)を作成する。
    - IAMロールにおける信頼ポリシーにて、AWSアカウント#2が本ロールをAssumeRole可能とするよう設定する。

```account1-mrap-trust-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                    "AWS": "arn:aws:iam::[AWSアカウント#2のアカウントID]:root"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

- AWSアカウント#2のEC2インスタンス（EIPあり）にて、上記のIAMロールをAssumeRoleし、ファイルのPUTを実行する。

```
# AssumeRoleの実行
RESPONSE=$(aws sts assume-role --role-arn "[AWSアカウント#1からもらったIAMロールのARN]" --role-session-name "MySession")

# 一時的な認証情報を環境変数に設定
export AWS_ACCESS_KEY_ID=$(echo $RESPONSE | jq -r '.Credentials.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(echo $RESPONSE | jq -r '.Credentials.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo $RESPONSE | jq -r '.Credentials.SessionToken')

# MRAPに対してファイルをPUT
aws s3api put-object --bucket [MRAPのARN] --key testfile.txt --body testfile.txt
```

### 動作確認
- AssumeRoleしていない状態ではファイルのPUTは不可だが、AssumeRoleすることでファイルのPUTが可能。
- この案の場合、MRAPのアクセスポリシー、及び後段のS3バケットのバケットポリシーの設定は不要。
- IAMポリシーにて、MRAPへのアクセス許可のみを記述し、後段のS3バケットに関してアクセス許可を記述しない場合、ファイルのPUT不可。そのため、後段のS3バケットに関しても許可の記述が必要。
- 後段のS3バケットの許可設定においてConditionを記述しない、もしくはSRCIPでのConditionを記述した場合、EC2インスタンスから直接後段のバケットを指定してファイルPUTが出来てしまうため、今回はMRAP経由のみを許可するConditionを入れている。

## 3.3 案2 VPCエンドポイント経由／IAMロールで制御

### 方式
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/e1add663-7ded-4967-ec98-d6dc6f6dd5de.png)



- バケットポリシーを使わず、AWSアカウント#1側のIAMポリシー/IAMロールのみで制御する。
- EC2インスタンスとMRAP間の経路はVPCエンドポイント経由とする。

### 設定手順
- AWSアカウント#2にて、MRAP接続用のVPCエンドポイント(com.amazonaws.s3-global.accesspoint)を作成する。
    - 手順はハンズオン資料「[Getting started with Amazon S3 Multi-Region Access Points](https://aws.amazon.com/jp/getting-started/hands-on/getting-started-with-amazon-s3-multi-region-access-points/module-seven/)」を参照。

- AWSアカウント#2にて、STS用のVPCエンドポイント(com.amazonaws.ap-northeast-1.sts)を作成する。

- AWSアカウント#1でIAMポリシーを作成する。
    - 案1と異なり、SRCIPでのアクセス制御は不可(SRCIPを条件にできるのはグローバルIPの場合のみ)のため、類似のアクセス制御としてVPCエンドポイントを用いる。

``` account1-mrap-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject"
            ],
            "Resource": [
                "[東京のバケットARN]",
                "[東京のバケットARN]/*"
            ],
            "Condition": {
                "StringEquals": {
                    "s3:DataAccessPointArn":"[MRAPのARN]"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject"
            ],
            "Resource": [
                "[大阪のバケットARN]",
                "[大阪のバケットARN]/*"
            ],
            "Condition": {
                "StringEquals": {
                    "s3:DataAccessPointArn":"[MRAPのARN]"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
            ],
            "Resource": [
                "[MRAPのARN]"
                "[MRAPのARN]/object/*"
            ],
			"Condition": {
                "StringEquals": {
                    "aws:SourceVpce": "[AWSアカウント#2のVPCエンドポイントID]"
				}
            }
        }
    ]
}
```

- AWSアカウント#1でIAMロールを作成する。IAMロールは案1と同じ。
- AWSアカウント#2のプライベートサブネットのEC2インスタンス(EIPやNATGWなし)にて、上記のIAMロールをAssumeRoleし、ファイルPUTを実行する。

```
# AssumeRoleの実行
RESPONSE=$(aws sts assume-role --role-arn "[AWSアカウント#1からもらったIAMロールのARN]" --role-session-name "MySession" --endpoint https:[STSのエンドポイント名])

# 一時的な認証情報を環境変数に設定
export AWS_ACCESS_KEY_ID=$(echo $RESPONSE | jq -r '.Credentials.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(echo $RESPONSE | jq -r '.Credentials.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo $RESPONSE | jq -r '.Credentials.SessionToken')

# MRAPに対してVPCエンドポイントを指定してファイルをPUT
aws s3api put-object --bucket [MRAPのARN] --key testfile.txt --body testfile.txt --endpoint-url https://accesspoint.[MRAP用のVPCエンドポイント名]
```

### 動作確認
- AssumeRoleを行い、かつIAMポリシーに指定されたVPCエンドポイント経由のファイルPUTの場合のみアクセスが成功する。
- このパターンの場合も、MRAPのアクセスポリシー、及び後段のS3バケットのバケットポリシーの設定は不要。

## 3.4 案3 インターネット経由／バケットポリシーで制御

### 方式
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/52e7d000-a369-d92a-2f18-aada1578eb6d.png)

- AWSアカウント#2側でIAMポリシー/IAMロールを作成する。AWSアカウント#1側で、AWSアカウント#2側のIAMロールに対するアクセス許可を行う。
- EC2インスタンスとMRAP間の経路はインターネットとする。

### 設定手順
- AWSアカウント#2にて、IAMポリシーを作成する。IAMポリシーの内容は案1と同じ。

:::note
IAMポリシーの内容は案1と同一だが、AWSアカウント#1で作成したIAMポリシー/IAMロールを使用した場合、実際にMRAPへのアクセスが許可される。AWSアカウント#2で作成したIAMポリシー/IAMロールの場合、あくまで「AWSアカウント#1のMRAPに対してPUTを出すことを許可する」だけであり、このIAMロールがあるからといってそれだけでMRAPにPUTが成功するわけではない。AWSアカウント#2側のMRAPアクセスポリシー及びバケットポリシーにて、このIAMロール（を持つインスタンス）からのアクセス許可を記述することで最終的にアクセス可能となる。
:::

- AWSアカウント#2にて、IAMロールを作成し、上記のIAMポリシーを紐づける。また、信頼ポリシーとしてEC2によるAssuleRoleを許可する。

``` account2-mrap-trust-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole"
            ],
            "Principal": {
                "Service": [
                    "ec2.amazonaws.com"
                ]
            }
        }
    ]
}
```

- AWSアカウント#1側で、MRAPに対してアクセスポリシーを設定する。
    - AWSアカウント#2のIAMロールからのアクセスのみを許可
    - PUTのみを許可
    - AWSアカウント#2のSRCIPからのアクセスのみを許可

``` account1-mrap-accesspolicy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "[AWSアカウント#2で使用するIAMロール]"
            },
            "Action": [
                "s3:PutObject"
            ],
            "Resource": [
                "[MRAPのARN]",
                "[MRAPのARN]/object/*"
            ],
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": "[AWSアカウント#2のEC2のグローバルIP]/32"
                }
            }
        }
    ]
}
```

- AWSアカウント#1側で、後段のS3バケットに対してバケットポリシーを設定する。

``` account1-bucketpolicy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "[AWSアカウント#2のIAMロール]"
            },
            "Action": "s3:PutObject",
            "Resource": [
                "[自分のバケットARN]",
                "[自分のバケットARN]/*"
            ],
            "Condition": {
                "StringEquals": {
                    "s3:DataAccessPointArn": "MRAPのARN"
                }
            }
        }
    ]
}

```

- AWSアカウント#2のEC2インスタンス（EIPあり、AWSアカウント#2で作成したIAMロール付与）にて、ファイルPUTを行う。

```
aws s3api put-object --bucket [MRAPのARN] --key testfile.txt --body testfile.txt
```

### 動作確認
- MRAPのアクセスポリシー及び後段バケットのバケットポリシーに指定されたIAMロールを使用し、かつ、SRCIPも指定したものと一致している場合にファイルPUTが可能。
- MRAPに対してのみアクセスポリシーで許可設定を入れてもアクセスは失敗する。後段のS3バケットにもバケットポリシーでの許可設定が必要。

## 3.5 案4 VPCエンドポイント経由／バケットポリシーで制御

### 方式
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/72ad8446-b6c5-937d-17da-22f918a78c5c.png)





- AWSアカウント#2側でIAMポリシー/IAMロールを作成する。AWSアカウント#1側で、AWSアカウント#2側のIAMロールに対するアクセス許可を行う。
- EC2インスタンスとMRAP間の経路はVPCエンドポイント経由とする。

### 設定手順
- AWSアカウント#2にて、MRAP接続用のVPCエンドポイント(com.amazonaws.s3-global.accesspoint)を作成する（案2と同じ）。

- AWSアカウント#2にて、IAMポリシーを作成する（案2と同じ。SRCIPではなくVPCエンドポイントを許可条件とする）。
- AWSアカウント#2にて、IAMロールを作成し、上記のIAMポリシーを紐づける。また、信頼ポリシーとしてEC2によるAssuleRoleを許可する（信頼ポリシーは案3と同じ）。

- AWSアカウント#1にて、MRAPアクセスポリシーを作成する。
    - Principalにて、AWSアカウント#2のIAMロールからのアクセスのみを許可
    - PUTのみを許可
    - AWSアカウント#2のVPCエンドポイントからのアクセスのみを許可

``` account1-mrap-accesspolicy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "[AWSアカウント#2で使用するIAMロール]"
            },
            "Action": [
                "s3:PutObject"
            ],
            "Resource": [
                "[MRAPのARN]",
                "[MRAPのARN]/object/*"
            ],
            "Condition": {
                "StringEquals": {
                    "aws:SourceVpce": "[AWSアカウント#2のVPCエンドポイントID]"
                }
            }
        }
    ]
}

```

- AWSアカウント#1にて、後段のS3バケットに対してバケットポリシーを設定する（案3と同じ）。

- AWSアカウント#2のEC2インスタンス（EIPなし、AWSアカウント#2で作成したIAMロール付与）にて、VPCエンドポイントを指定してファイルPUTを行う。

```
aws s3api put-object --bucket [MRAPのARN] --key testfile.txt --body testfile.txt --endpoint-url https://accesspoint.[MRAP用のVPCエンドポイント名]
```
- MRAPのバケットポリシーに指定されたIAMロールを使用し、かつ、指定したVPCエンドポイントを経由している場合にファイルPUTが可能。
- MRAPに対してのみアクセスポリシーで許可設定を入れてもアクセスは失敗する。後段のS3バケットにも上記の許可設定が必要。

# 4. 所感
- アクセス制御の仕方にいろいろなパターンがあり、少し検証するだけでも長くて分かりにくい記事になってしまった。。