Simple Storage Service(S3) の設定の注意点・レビュー観点


## はじめに

今年はS3バケットに関わる仕事に多く関わらせてもらったので、得た知見を共有します。
設定項目が多いので、分かっていてもレビュー時に見落としてしまうことがあると思います。
レビュー時のチェックリスト的に使ってもらえることを目指してます。

修正点あったら指摘していただけると嬉しいです。追記要求なども大歓迎です。

また、CORSなど、ここに書いてない設定については筆者が疎いので深く言及しません（できません）
なるべく使ってみるようにして、追記していきたいと思います。


## バケット名から用途を推測できるか

変数の命名と同じですが、名前が適当だとバケット名を見た時に何に使われているか分からないと確認の手間が発生します。

本番用のバケットには `production` を含めるなどと決めておくと、オペミスも減らせると思います。


NG例
![Screen Shot 2021-11-16 at 21.33.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187545/63c18abd-9225-e737-d6d4-5a045e86c5bf.png)

OK例
![Screen Shot 2021-11-16 at 22.31.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187545/8d1a5b26-646f-e0ca-47cf-dd71121e2f10.png)



## 意図通りのリージョンにバケットを置いているか

ALBのログは同じリージョンのバケットにしか出力できないので注意です。

https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-access-logs.html


EC2からのアクセスはVPCエンドポイントを使えば速度が上がるのですが、VPCと同じリージョンにあるバケットでしか使えないので注意です。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/s3-maximum-transfer-speed-ec2/


## ストレージクラスは合理的か

低頻度でしかアクセスしないようなオブジェクトは `Standard-Infrequent Access` や `One Zone-Infrequent Access` などで保存するとコストを削減することができます。
（One Zone-Infrequent AccessはAZ障害でアクセス不可能になるので注意）

https://aws.amazon.com/jp/s3/storage-classes/?nc1=h_ls


## バージョニングの設定は適切か

オブジェクトを誤って削除してもバージョニングが有効化されていれば元に戻すことができます。
ただし、オブジェクトの過去バージョンに対しても料金が発生するので注意です。

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/Versioning.html


## ライフサイクルルールは適切か

最初の1ヶ月は頻繁にアクセスされるけど、その後はアクセス頻度が激減する。という場合は低頻度用のストレージクラスに移行するようにライフサイクルルールを設定するとコスト削減になります。

ログなどを一定期間後に削除したい場合には削除用のルールを設定することもできます。

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/object-lifecycle-mgmt.html


## 暗号化設定は適切か

大事なデータは暗号化しましょう。ただし、制約がいろいろあるらしいので注意しましょう（あまり詳しくないのでこれくらいで...）

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/bucket-encryption.html


## Block public access の設定は適切か

これをONにしておくと、バケットポリシー/ACLで誤って公開設定にしようとした時にエラーを出してくれます。
パブリックアクセスを許可する場合以外は基本ONにしておくことをオススメします。
![Screen Shot 2021-11-16 at 21.37.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187545/807e1858-f19a-66a4-b3a8-1f87174a2b37.png)

ACLで公開しようとした場合はこんなエラーが出ます。
![Screen Shot 2021-11-16 at 21.38.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187545/a3c17c3e-2e57-a625-9ed4-6c288b29a40d.png)


クラスメソッドさんの記事がわかりやすいです。

https://dev.classmethod.jp/articles/s3-block-public-access/


## ACLを無効化する (2022/01/26 21:29追記)
ACL無効化したら次の2項目を考えなくて済むのでACL無効化しましょう

バケットポリシーのみでアクセス権を制御できるのでシンプルになります
アップロードする時もわざわざ `--acl bucket-owner-full-control` をつけなくて良くなります。

https://aws.amazon.com/jp/about-aws/whats-new/2021/11/amazon-s3-object-ownership-simplify-access-management-data-s3/

https://dev.classmethod.jp/articles/s3-bucket-owner-enforced/


## Object Ownership は原則 `Bucket owner preferred`

バケット所有者（バケットがあるアカウント）がオブジェクトの所有権を持っていない場合、
バケットポリシーでそのオブジェクトへのアクセスを許可することができません。
マルチアカウント構成だとこれがネックになるので、理由がなければ `Bucket owner preferred` にしておくことをオススメします。

![Screen Shot 2021-11-16 at 21.55.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187545/aa0f0e06-2c1c-26de-cccb-0f04a5d1e490.png)

ただし、アプリのユーザが直接オブジェクトを読み書きするような場合、
所有権はそのユーザにあるべきなので `Object Writer` にするべきです。


## 別アカウントのS3バケットにアップロードするときは `--acl bucket-owner-full-control` をつける

```shell
# 例
aws s3 cp test.txt s3://bucket-to-store-something --acl bucket-owner-full-control
```

これを付けないとバケットがあるアカウントのIAMロールでもオブジェクトにアクセスできなくなってしまうので気をつけましょう。
もちろん、アクセスできない方が良い場合は付けないで良いです。


バケットポリシーで強制することもできます。 （2021/11/19 12:20 追記)

```json
{
   "Version": "2012-10-17",
   "Statement": [
      {
         "Sid": "Only allow writes to my bucket with bucket owner full control",
         "Effect": "Allow",
         "Principal": {
            "AWS": [
               "arn:aws:iam::111122223333:user/ExampleUser"
            ]
         },
         "Action": [
            "s3:PutObject"
         ],
         "Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
         "Condition": {
            "StringEquals": {
               "s3:x-amz-acl": "bucket-owner-full-control"
            }
         }
      }
   ]
}
```
下記記事から抜粋

https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html


## AWSアカウントを許可する時はなるべく Organization/Organization Unit(OU) で指定する

OrganizationやOUに対してアクセス許可/拒否をバケットポリシーで設定できます。
これができると、OrganizationやOUのアカウントが増減してもバケットポリシーを変えないで済むので楽です。

（下記はOUを指定してオブジェクトの読み取り権限を与えている例）

```json
{
	"Version":"2012-10-17",
	"Statement":{
		"Sid":"TrainingDataS3ReadOnly",
		"Effect":"Allow",
		"Principal": "*",
		"Action":"s3:GetObject",
		"Resource":"arn:aws:s3:::training-data/*",
		"Condition":{
			"ForAnyValue:StringLike":{
				"aws:PrincipalOrgPaths":["o-myorganization/*/ou-machinelearn/*"]
			}
		}
	}
}
```
下記参考記事より抜粋

https://aws.amazon.com/jp/blogs/security/iam-share-aws-resources-groups-aws-accounts-aws-organizations/


Event Busなど、アクセスポリシーを指定する時にはこの書き方が使えるので積極的に使っていきましょう。

https://qiita.com/kabik/items/b2e1806eadee0970ab14


## バケットポリシーの `Resource` 項目は適切か （2021/11/17 17:33 追記）

下記のようにすると、バケットへのアクセスを許可/拒否します
アクション例: `GetBucketPolicy` `GetBucketAcl`

```
"Action":"s3:Get*",
"Resource": "arn:aws:s3:::bucket-name",
```

バケット内のオブジェクトへのアクセスを許可/拒否したい場合は下記のようにします。
アクション例: `GetObject` `GetObjectAcl`

```
"Action":"s3:Get*",
"Resource": "arn:aws:s3:::bucket-name/*",
```

パスを絞って許可/拒否することもできます

```
"Action":"s3:Get*",
"Resource": "arn:aws:s3:::bucket-name/any-dir/*/some-dir/*",
```


## バケットポリシーで与える権限は最小にする （2021/11/17 17:33 追記）

必要な権限が分からない時、このようなバケットポリシーを設定しまいがちですが、危険です。
書き込みは最小権限だけ許可しましょう。

例えばこれだと、バケット削除、オブジェクト削除、バケットポリシーの変更などなど、`arn:aws:iam::xxxxxxxxxxxx:role/hoge-rol` から大抵のことはできてしまいます。

```json
{
	"Version":"2012-10-17",
	"Statement":{
		"Effect":"Allow",
		"Principal": "arn:aws:iam::xxxxxxxxxxxx:role/hoge-role",
		"Action": "s3:*",
		"Resource": [
			"arn:aws:s3:::bucket-name",
			"arn:aws:s3:::bucket-name/*"
		]
	}
}
```

ツールによるアクセスしかないような場合でも、せめて以下のようにしましょう。
これならバケットが消されたり、バケットポリシーを変更されて公開にされる等のバケットへの危険なアクションを拒否することができます。

```json
{
	"Version":"2012-10-17",
	"Statement":{
		"Effect":"Allow",
		"Principal": "arn:aws:iam::xxxxxxxxxxxx:role/hoge-role",
		"Action": "s3:*",
		"Resource": [
			"arn:aws:s3:::bucket-name/*"
		]
	}
}
```

読み取りアクションは`*` を使って許可しても良いですが、
書き込みアクションはアクション単位で明示的に許可しましょう。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Principal": "arn:aws:iam::xxxxxxxxxxxx:role/hoge-role",
            "Action": [
                "s3:List*",
                "s3:Get*"
            ],
            "Resource": [
                "arn:aws:s3:::bucket-name",
                "arn:aws:s3:::bucket-name/*"
            ]
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Principal": "arn:aws:iam::xxxxxxxxxxxx:role/hoge-role",
            "Action": [
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::bucket-name/*"
        }
    ]
}
```

## おわりに

設定項目多すぎて混乱しますが、それだけ用途が多い重要サービスということですね。

ここに無い項目については適宜追加できたらと思います。