S3のアクセスコントロールまとめ


S3のアクセスコントロールをよく理解していないのでまとめる。
日本語ドキュメントは更新されてない場合が多いので英語ドキュメントを参照することをお勧めする。
ただ、S3のアクセスコントロールについては枯れているので日本語ドキュメントでも問題ないと思う。

[開発者ガイド](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/Welcome.html)
[Developer Guide](http://docs.aws.amazon.com/AmazonS3/latest/dev/Welcome.html)

# 前提知識

大概のクラウドストレージサービスには意図せぬ公開のリスクがある。
S3も同様にワンクリックでカジュアルな情報漏えいが可能である。
CLIはともかく、コンソールでのバケット作成時に公開するのか/しないのか選択できてもいいのにと思う。

こちらの記事でS3うっかり公開の防止や発見について書いてあるので読んでおくこと。
Trusted Advisor、AWS Config、S3アクセスチェック、などについて書かれている。

[意図せず公開しているS3バケットはありませんか？〜S3アクセス権限設定を見直そう〜](https://dev.classmethod.jp/cloud/aws/check-your-s3-access-configuration/)

# 基礎知識

## アクセスコントロールの種類

S3はアクセスコントロールのために３つの方法を提供している。
AWS全般のアクセスコントロールの種類やサービスごとの対応状況については[IAMと連携するAWSサービス](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html)が参考になる。

* ACL(アクセスコントロールリスト)
* バケットポリシー
* IAMポリシー

| アクセスコントロールタイプ | AWSアカウントレベルの制御 | IAMユーザレベルの制御 | 形式 |
|:----------------------|:---------------------:|:---------------:|:----:|
| ACL                   |          ○            |        ☓        | XML  |
| バケットポリシー         |          ○            |        ○        | JSON |
| IAMポリシー            |          ☓            |        ○        | JSON |

[アクセスコントロールリスト（ACL）の概要](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/acl-overview.html)

### ACL

バケット/オブジェクトのサブリソースとしてXMLで定義する。
AWS Management Console からは、各バケット/オブジェクトの Properties > Permissions で設定できる。

ACLは設定でdisableに出来るようになった。
disableにした場合、ポリシーだけでアクセス制御を行うことになる。

[Controlling ownership of objects and disabling ACLs for your bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html)
※ 2022-01-31時点で日本語訳はない

### バケットポリシー

バケット/オブジェクトのアクセス権をJSONで定義する。
AWS Management Console からは、バケットの Properties > Permissions > Add(Edit) Bucket Policy で設定できる。

### IAMポリシー

IAMのリソースに紐づき、S3を含むAWSリソースへのアクセス権をJSONで定義する。
AWS Management Console からは、IAMのホームディレクトリでユーザ/グループ/ロールのポリシー設定ができる。

## リクエストの認可

あとで書く。
[Amazon S3 がリクエストを許可する方法](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/how-s3-evaluates-access-control.html)

## 評価論理

許可/拒否を決めるルール。ここが1番重要な気がする。
基本は下記の通りで **明示的拒否** > **許可** > **デフォルト拒否** の順に強い。

* **デフォルト拒否** (ただし自分自身からのアクセスは除く)
* **許可** は **デフォルト拒否** に優先する
* **明示的拒否** は **許可** に優先する
* ポリシーの評価順序は重要ではない

[IAM ポリシーの評価論理](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/AccessPolicyLanguage_EvaluationLogic.html)

個人的に間違えそうなのが、デフォルト拒否/明示的拒否に、許可を組み合わせた場合。
条件Aに一致するリクエストが拒否される時、設定によってどういう評価になるかを表にしてみる。

|  条件 |     可否     |     評価     |
|:-----:|:----------:|:------------:|
| NOT A | Allow(許可) | デフォルト拒否 |
|   A   | Deny(拒否)  | 明示的拒否    |

これに評価が許可になる条件Bを組み合わせると、

> (NOT A) or B → デフォルト拒否 or 許可 → **許可**
> A or B → 明示的拒否 or 許可 → **拒否**

となり、評価がデフォルト拒否になっているからといって、拒否設定した気になっていると、
他のポリシーとの組み合わせでうっかり許可になってしまう場合がある。

## 対象リソースの指定

バケットポリシーやIAMポリシーで、ある Action を許可/拒否する場合、対象 Resource を ARN で指定する。
バケットに対する Action は Resource としてバケットの ARN(arn:aws:s3:::bucket) を指定する。 
オブジェクトに対する Action は Resource としてオブジェクトの ARN(arn:aws:s3:::bucket/*) を指定する。

AWS Management Console から特定のS3バケットの操作を許可する例。

```javascript:IAMポリシー抜粋
{
   "Statement":[
      // S3ホームディレクトリでバケット一覧を表示するために必要
      {
         "Effect":"Allow",
         "Action":[
            "s3:ListAllMyBuckets"
         ],
         "Resource":"arn:aws:s3:::*"
      },
      // バケットに対する操作を許可
      {
         "Effect":"Allow",
         "Action":[
            "s3:ListBucket",
            "s3:GetBucketLocation"
         ],
         "Resource":"arn:aws:s3:::bucket"
      },
      // オブジェクトに対する操作を許可
      {
         "Effect":"Allow",
         "Action":[
            "s3:PutObject",
            "s3:GetObject",
            "s3:DeleteObject"
         ],
         "Resource":"arn:aws:s3:::bucket/*"
      }
   ]
}
```

## AWS Account ID と Canonical User ID の確認方法

AWS Management Console に AWSアカウントでログイン後、
右上のメニュー > Security Credentials を開き Account Identifiers から確認できる。
ただし、AWSアカウントの管理者(rootユーザ)しか確認できない。
`aws s3api get-bucket-acl --bucket <bucket-name>` で確認したほうが手っ取り早いかも。
[AWS アカウント ID](http://docs.aws.amazon.com/ja_jp/general/latest/gr/acct-identifiers.html)

# ACL

ACLは設定でdisableに出来るようになった。
disableにした場合、ポリシーだけでアクセス制御を行うことになる。

[Controlling ownership of objects and disabling ACLs for your bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html)
※ 2022-01-31時点で日本語訳はない

アクセスコントロールリスト(ACL)は、各バケット/オブジェクトごとに付与され対象のアクセス権を制御できる。
バケット/オブジェクト作成時に、デフォルトで自分自身への `FULL_CONTROL` が付与される。
最大100個まで権限を付与できる。

バケットごと公開したい時 AWS Management Console からマウス操作だけで簡単に設定できるし、オブジェクトごとに細かくアクセス制限したい時などに使えそう。

別アカウントのバケットにオブジェクトをアップロードする場合、そのアカウントの管理者に `FULL_CONTROL` を与えたりする(CloudTrailなど)。
この場合ACL以外で許可する方法はない。バケットポリシーだけ設定したとしても許可はされない。

また、バケットポリシーは20KBの制限があるので、ACLを併用したほうがいいこともある。
[アクセスポリシーのオプションを使用するためのガイドライン](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/access-policy-alternatives-guidelines.html)

## 被付与者(Grantee)

アクセス権を付与される人。
被付与者は、AWSアカウントに紐付いたメールアドレスor正規ユーザID(Canonical User ID)で指定する。
IAMユーザ単位のアクセス権は設定できない。たぶんIAMが実装される前からある機能だから。

S3には、あらかじめいくつかのグループが定義されており、グループに対してアクセス権を付与することもできる。

* **Authenticated Users**
	全AWSアカウント(自分以外のアカウントも含むので注意)。
	リクエストは署名(認証)されている必要がある。
	<br>

* **All Users**
	誰でも。リクエストの署名有無は問わない。
	AWS Management Console の Permissions で選択できる Everyone は多分これ。
	<br>

* **Log Delivery**
	S3のアクセスログ書き込み権限。アクセスログについては↓。
	[サーバーアクセスのロギング](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/ServerLogs.html)

## アクセス許可(Permissions)

`READ`、`WRITE`、`READ_ACP`、`WRITE_ACP`、`FULL_CONTROL` の５つのアクセス権がある。
*_ACP はアクセスコントロールリストへのアクセス権をあらわす。
[アクセスコントロールリスト（ACL）の概要 - 付与できるアクセス許可](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/acl-overview.html#permissions)

## 規定ACL(Canned ACL)

S3には、あらかじめ被付与者とアクセス許可のセットが定義されており、APIのパラメータとして使用することができる。
[アクセスコントロールリスト（ACL）の概要 - 既定 ACL](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/acl-overview.html#canned-acl)

* **private** (バケット/オブジェクト)

	デフォルトACL。所有者に `FULL_CONTROL` が付与される。

* **public-read** (バケット/オブジェクト)

	所有者に `FULL_CONTROL` 、All Users に `READ` が付与される。

* **public-read-write** (バケット/オブジェクト)

	所有者に `FULL_CONTROL` 、All Users に `READ`/`WRITE` が付与される。

* **authenticated-read** (バケット/オブジェクト)

	所有者に `FULL_CONTROL` 、Authenticated Users に `READ` が付与される。

* **bucket-owner-read** (オブジェクト)

	オブジェクト所有者に `FULL_CONTROL` 、バケット所有者に `READ` が付与される。
	バケット作成時に指定しても無視される。

* **bucket-owner-full-control** (オブジェクト)

	バケット所有者とオブジェクト所有者に `FULL_CONTROL` が付与される。
	バケット作成時に指定しても無視される。

* **log-delivery-write** (バケット)

	Log Delivery グループに `WRITE`/`READ_ACP` が付与される。

# バケットポリシー

JSONの定義は、IAMポリシーの項を参照。
設定の参考例は↓。
[バケットポリシーの例](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-bucket-policies.html)

* ポリシーを定義できるのはバケット所有者のみ
* バケット/オブジェクトレベルのアクセス権を設定できる
* オブジェクトに対するアクセス権設定は、バケット所有者とオブジェクト所有者が同一の場合のみ可能
* ACLと違い、IAMユーザレベルのアクセス権設定ができる
	* IAMグループレベルのアクセス設定はできない。IAMグループのIAMポリシーで設定することはできる。
* 他AWSアカウントの管理者に許可を付与した場合、他AWSアカウントの管理者はそのIAMユーザに権限を譲渡することができる
* バケットポリシーのサイズは20KBまで

# IAMポリシー

IAMポリシーとバケットポリシーはどちらでも同じようなアクセス権を設定できるが、
ユーザにアクセス権を設定したい場合はIAMポリシーを使い、
AWSアカウントやバケットにアクセス権を設定したい場合はバケットポリシーを使う、
という決まりを作ったほうが運用時に幸せになれそう。

IAMポリシーを使ったアクセス制御のチュートリアルが↓にあるので参考に。
[チュートリアル例: ユーザーポリシーを使用したバケットへのアクセスの制御](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/walkthrough1.html)

## リソース作成者のアクセス許可

AWSアカウントの管理者がバケット/オブジェクトを作成した場合、デフォルトで `FULL_CONTROL` が付与される。
しかし、IAMユーザが作成した場合、デフォルトではアクセス権は設定されない。
なので、IAMポリシーでアクセス権を設定する必要がある。

## ポリシーの要素

ポリシーの要素で気になったものだけ紹介。
要素の中には値としてポリシー変数(後述)を指定できるものもある。
[IAM ポリシーエレメントの参照](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/AccessPolicyLanguage_ElementDescriptions.html)

### Version

ポリシー言語のバージョンを表す。

ポリシー内にポリシー変数を含む場合は `Version` 要素を必ず指定する必要がある。
`Version` 要素のデフォルト値は `2008-10-17` なので、ポリシー変数がリテラル文字列として扱われるから。
`2012-10-17` を指定することでポリシー変数として扱われるようになる。

```javascript:設定例
"Version":"2012-10-17"
```

### Principal

> Principal とは、ポリシーにしたがってアクセス権限を付与または拒否される 1 人以上のユーザーです。
> 
> [IAM ポリシーエレメントの参照 - プリンシパル](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/AccessPolicyLanguage_ElementDescriptions.html#Principal)

`Principal` 要素にはAWSアカウントID、正規ユーザID、IAMユーザのARNなどが指定できる。
`NotPrincipal` 要素を使うことで、特定アカウント/IAMユーザを明示的に拒否することもできる。

```javascript:設定例
// AWSアカウント単位
"Principal":{"AWS":"123456789012"}
"Principal":{"AWS":"arn:aws:iam::123456789012:root"}

// IAMユーザ
"Principal":{"AWS":[
	"arn:aws:iam::123456789012:user/Isono",
	"arn:aws:iam::123456789012:user/Nakajima"
]}

// Webフェデレーションユーザ
"Principal":{"Federated":"www.amazon.com"}
```

### Action

`Action` 要素にもワイルドカード(* or ?)が使える。
`NotAction` 要素を使うことで、特定アクション以外を明示的に拒否することもできる。

```javascript:設定例
"Action":"iam:*AccessKey*"
```

S3の Action 一覧↓。
[ポリシーでのアクセス許可の指定](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/using-with-s3-actions.html)

### Resource

取り扱う対象のオブジェクトをARNで指定する。
[IAM をサポートする AWS サービス](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/Using_SpecificProducts.html)

```javascript:設定例
"Resource":[
    "arn:aws:dynamodb:ap-northeast-1:123456789012:table/table1",
    "arn:aws:dynamodb:ap-northeast-1:123456789012:table/table2"
]
```

### Condition

オプショナルな要素。ポリシーの実行条件を細かく指定できる。

色々できるので詳細は↓を参照のこと。
[IAM ポリシーエレメントの参照 - 条件](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/AccessPolicyLanguage_ElementDescriptions.html#Condition)

## ポリシー変数

ポリシー変数を使う場合は `"Version":"2012-10-17"` をポリシーに含めること。
未指定の場合、デフォルトで旧バージョン(2008-10-17)が指定されたことになるのでポリシー変数は使えない。
その場合、ポリシー変数はリテラル文字列として扱われる。
[IAM ポリシー変数の概要](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/PolicyVariables.html)

### 変数の使用箇所

`Resource` 要素と `Condition` 要素で使える。

```javascript:設定例
// Resource要素
"Resource":["arn:aws:s3:::bucket/home/${aws:username}/*"]

// Condition要素
"Condition": {
	"StringLike": {
		"sns:endpoint": "https://example.com/${aws:username}/*"
	}
}
```

### キーの種類

#### リクエストから取得されるキー

`Condition` のキーとして指定する場合、大文字/小文字を区別するので注意。

* **aws:CurrentTime**

	日時条件

* **aws:EpochTime**

	日付をエポックつまり UNIX 時間で表したもの。日時条件で使用

* **aws:principaltype** (※)

	プリンシパルがアカウント、ユーザー、フェデレーション、または引き受けられたロールかどうかを示す値

* **aws:SecureTransport**

	リクエストが SSL を使用して送信されたかどうかを示すブール値

* **aws:SourceIp**

	IP アドレス条件で使用するための、リクエスタの IP アドレス

* **aws:UserAgent**

	文字列条件で使用するための、リクエスタのクライアントアプリケーションに関する情報

* **aws:userid** (※)

	現在のユーザーの一意の ID

* **aws:username** (※)

	現在のユーザーのフレンドリ名

※ プリンシパルごとの `aws:username` `aws:userid` `aws:principaltype` の値を表にしたもの。

| プリンシパル | aws:username | aws:userid | aws:principaltype |
|--------|:------:|:------:|:-------|
| AWSアカウント | - | AWSアカウントID | Account |
| IAMユーザ | IAMユーザ名 | 一意のID | User |
| 委任ユーザ | - | AWSアカウントID:発信者が指定した名前 | FederatedUser |
| Webフェデレーションユーザ | - | - | AssumedRole |
| SAMLフェデレーションユーザー| - | - | AssumedRole |
| 委任されたロール | - | ロールID:発信者が指定したロール名 | AssumedRole |
| Amazon EC2 インスタンスに割り当てられたロール | - | ロールID:インスタンスID | AssumedRole |
| 匿名(SQS/SNS/S3のみ) | - | - | Anonymous |

[IAM ポリシー変数の概要 - ポリシー変数に使用可能なリクエスト情報](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/PolicyVariables.html#policy-vars-infotouse)

#### Webフェデレーションユーザに関するキー

どのプロバイダでログインしたかは `aws:FederatedUser` で取得できる。

* **aws:FederatedUser**

	* Amazonでのログイン認証時に `www.amazon.com` を返す
	* Facebookでのログイン認証時に `graph.facebook.com` を返す
	* Googleでのログイン認証時に `accounts.google.com` を返す

アプリケーションIDとユーザIDも取得できる。
プロバイダごとのキーは下記の通り。

| プロバイダ | アプリケーションIDのキー      | ユーザIDのキー            |
|----------|---------------------------|-------------------------|
| Amazon   | www.amazon.com:app_id     | www.amazon.com:user_id  |
| Facebook | graph.facebook.com:app_id | graph.facebook.com:id   |
| Google   | accounts.google.com:aud   | accounts.google.com:sub |

#### SAMLベースのフェデレーションユーザに関するキー

下のほうに一覧が書いてある。
[IAM ポリシーエレメントの参照 - 条件](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/AccessPolicyLanguage_ElementDescriptions.html#Condition)

#### S3固有のキー

サービス固有のキーもある。S3で利用可能なキーは↓。
[ポリシーでの条件の指定](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/amazon-s3-policy-keys.html)

# クロスアカウント

このチュートリアルは順を追って関係者が増えて複雑になっていくので理解しやすい。
(例１はクロスアカウントではない)
[チュートリアル例: Amazon S3 リソースへのアクセスの管理](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-walkthroughs-managing-access.html)

* [例 1: バケット所有者がユーザーにバケットのアクセス許可を付与](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-walkthroughs-managing-access-example1.html)
![例 1: バケット所有者がユーザーにバケットのアクセス許可を付与](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/images/access-policy-ex1.png)

* [例 2: バケット所有者がクロスアカウントのバケットのアクセス許可を付与](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-walkthroughs-managing-access-example2.html)
![例 2: バケット所有者がクロスアカウントのバケットのアクセス許可を付与](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/images/access-policy-ex2.png)

* [例 3: バケット所有者が自分の所有していないオブジェクトに対するアクセス許可をユーザーに付与](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-walkthroughs-managing-access-example3.html)
![例 3: バケット所有者が自分の所有していないオブジェクトに対するアクセス許可をユーザーに付与](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/images/access-policy-ex3.png)

* [例 4: バケット所有者が自分が所有しないオブジェクトへのクロスアカウントアクセス許可を付与](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-walkthroughs-managing-access-example4.html)
![例 4: バケット所有者が自分が所有しないオブジェクトへのクロスアカウントアクセス許可を付与](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/images/access-policy-ex4.png)

とにかくIAMユーザ(子)はAWSアカウントの管理者であるrootユーザ(親)の許可がないとダメ。
クロスアカウントの場合、親であるAWSアカウントの管理者が2人になるので、子はそれぞれに親に許可を得る必要がある。