S3クロスアカウントでのデータ連携

# はじめに

開発の中でAWSの別アカウント間でS3バケットのデータ連携を行う必要があり、
S3のVPCエンドポイントを使用してプライベートな通信で検証した結果を記事に残したいと思います。

# 構成図
今回の検証では以下のような構成でS3バケットのデータ連携を行いました。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/816973/57c4031b-eac4-4621-d224-184d02656554.png)
S3バケットのデータ連携でIAMポリシー周りでハマってしまいましたので、
そのあたりも記載していきたいと思います。

# 前提
アカウントA

* IAMRoleの作成
* AWS CLIのインストール
* AWS CLIでのPutObject時には「--acl bucket-owner-full-control」オプション必須
* VPCにS3のVPCエンドポイントを作成


アカウントB

* S3バケットの作成
* S3バケットポリシーの設定

# やってみた
アカウントA

* IAMRoleの作成

```ruby:IAMポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:ListAllMyBuckets",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:PutObject",
                "s3:PutObjectAcl",
                "s3:GetObject",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::tests3-001/data01/*"
            ]
        }
    ]
}
```

実行可能なS3アクションはls,put,get,deleteに限定しています。アクセス対象はS3バケット配下にdata01ディレクトリを作成しました。作成したIAMロールはEC2にアタッチしておきます。
なお、"s3:PutObjectAcl"はオブジェクトのアクセス権限を変更するアクションです。これがないとアカウントB側でファイル参照できません。アカウントAからPutする際のコマンドオプション「--acl bucket-owner-full-control」を使用する際に必要となってきます。


* VPCエンドポイントの作成

 以下の記事を参考に作成しました。 
[https://qiita.com/kooohei/items/794a7d6e041f43f98f5b](https://qiita.com/kooohei/items/794a7d6e041f43f98f5b "Qiita Home")


アカウントB

こちらではS3バケット「tests3-001」を作成しました。
作成したS3バケットに以下のバケットポリシーを記載しました。PrincipalにアカウントAで作成したIAMRoleを指定して操作を許可/拒否しています。
IAMRoleとバケット名については環境ごとに読み替えてください。

```ruby:S3バケットポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::xxxxxxxxx:role/IAMRoleName
            },
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::tests3-001",
                "arn:aws:s3:::tests3-001/*"
            ]
        },
        {
            "Effect": "Deny",
            "Principal": {
                "AWS": "arn:aws:iam::xxxxxxxxx:role/IAMRoleName
            },
            "Action": "s3:PutObject",
            "Resource": [
                "arn:aws:s3:::tests3-001",
                "arn:aws:s3:::tests3-001/*"
            ],
            "Condition": {
                "StringNotEquals": {
                    "s3:x-amz-acl": "bucket-owner-full-control"
                }
            }
        }
    ]
}
```
Condition句で[StringNotEquals]を付けて、アカウントAからPUTする際に「bucket-owner-full-control」のオプションを付けていないとAccessDenyとするようにしました。これを付けていなくてもアカウントAからPUTはできるのですが、アカウントBがファイル参照できなくなるので、このCondition句は必須かと思います。

# 接続確認

アクセス可能か、lsコマンドを実行しましたが、AccessDenyとなりました。


```ruby:ls
aws s3 ls s3://tests3-001/data01/
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
```

デバックオプションを付けて、再度確認しましたが、よくわからない。


```ruby:debug
aws s3 ls s3://tests3-001/test01 --debug
```


基本に立ち返り、AWSの公式ドキュメントを参照。
 Amazon S3 のアクション、リソース、条件キー
[https://https://docs.aws.amazon.com/ja_jp/service-authorization/latest/reference/list_amazons3.html](https://https://docs.aws.amazon.com/ja_jp/service-authorization/latest/reference/list_amazons3.html "Qiita Home")


S3のアクションが定義できるリソースがListBucketだけ「buket*」となっていました。		

| Acrtion   　 | Resource　　 |
|:-----------  |------------:|
| ListBucket   | bucket*     |
| PutObject    | object*     |
| PutObjectAcl | object*     |
| GetObject    | object*     |
| DeleteObject | object*     |


IAMポリシーでListBucketのResource定義はBucketそのものにしか、効かないようです。今回、"data01/*"まで定義していたことが問題となっていたようです。

では、"data01/*"配下にList権限を付与するためにはどうすればよいか、再度、調査しました。調査した結果、condition句で対象のディレクトリを定義すれば制限がかかることが判明しました。なので、以下のようにIAMポリシーを修正しました。

```ruby:IAMポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:ListAllMyBuckets",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:DeleteObject",
                "s3:GetObject",
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": [
                "arn:aws:s3:::tests3-001/data01/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::tests3-001"
            ],
            "Condition": {
                "StringLike": {
                    "s3:prefix": "data01/*"
                }
            }
        }
    ]
}
```

# 再度、接続確認

無事、各アクションが実行できました。


```ruby:s3コマンド

aws s3 cp test.txt s3://tests3-001/data01/ --acl bucket-owner-full-control
upload: .\test.txt to s3://tests3-001/data01/test.txt

aws s3 ls s3://tests3-001/data01/
2021-04-13 xx:xx:xx     7 test.txt

aws s3 cp s3://tests3-001/data01/test.txt  c:\users\administrator\test
download: s3://tests3-001/data01/test.txt to .\test

aws s3 rm s3://tests3-001/data01/test.txt
delete: s3://tests3-001/data01/test.txt

aws s3 ls s3://tests3-001/data01/
→ファイルなし(削除された)

```


# おわりに

クロスアカウントの確認が目的でしたが、IAMポリシーのS3アクションについて
ListBucketだけResource対象が違う点は非常に勉強になりました。
今後もAWS周りの記事を投稿していきたいと思います。
本記事が何かのお役に立てれば幸いです。
ありがとうございました。