AWS IAM User と Policy で S3 にアップロードしか出来ないユーザを作る

ユーザ権限の設定はちょっと AWS を触るだけでも必須の機能ですが、すぐ忘れるのでメモします。例えば自動的に S3 にファイルをアップロードしたいという場合を考えてみます。この時、アップロードするスクリプトは特定の S3 にだけアクセス出来て、他の余計な事は出来ないようにしたいと思います。できるだけ awscli を使い、awscli では面倒臭い操作だけ AWS Console を使います。

## やりたい事

* S3 バケットを作成して、ローカルディレクトリの内容をコピーしてブラウザから見える状態にする。
* ファイルのアップロード用に最低限の権限だけを持つユーザを作成する。

## AWS の権限とは?

AWS では、「誰が」、「何処に」、「何を」、「出来る/出来ない」。という形式で権限を設定し
ます。それぞれ、AWS 用語では Principal, Resource, Action, Effect と呼
びます。例えば今からやろうとしている設定はこんな風になります。

* Principal (誰が)
    * 例: `hoge-user` (hoge-user という名前のユーザが)
* Resource (何処に)
    * 例: `s3://hoge-web-server` (`hoge-web-server` という名前の S3 バケットに)
* Action (何を)
    * 例: `s3:PutObject` (ファイルのアップロードを)
* Effect (出来る / 出来ない)
    * 例: `Allow` (出来る)

これを実現するために、次の二つの物を作り、後で関連付けます。

* IAM User: 「誰が」を表す。
    * Access Key と Secret Access Key を発行してプログラムが権限を持てる。
* IAM Policy: 「何処に」「何を」「出来る/出来ない」を表す。

## S3 バケットの作成

まず、アップロード先の S3 バケットを作成して、現在のディレクトリの内容をコピーします。

```
$ export AWS_PROFILE=my-user # 自分の AWS ユーザに切り替える。
$ aws s3 mb s3://hoge-web-server # bucket を作る
$ aws s3 sync . s3://hoge-web-server --delete --acl public-read # カレントディレクトリの内容を S3 にアップロード
$ aws s3 website s3://hoge-web-server --index-document index.html --error-document error.html # bucket を website として公開
```

`http://hoge-web-server.s3-website-(region).amazonaws.com/pages/` にアクセスして公開出来た事を確認

## IAM User の作成

次に、IAM User を作成します。この時 ARN を控えておきます。ARN は AWS 上の物を表す記号です。

```
$ aws iam create-user --user-name hoge-user
...
        "Arn": "arn:aws:iam::01234:user/hoge-user"
```

次に、Access Key を取得します。

```
aws iam create-access-key --user-name hoge-user
...
        "SecretAccessKey": "XXXX",
        "AccessKeyId": "YYYY"
```

別のターミナルを開け、取得した Access Key で早速 S3 へのアップロードを試みてみます。

```
$ export AWS_ACCESS_KEY_ID=YYYY
$ export AWS_SECRET_ACCESS_KEY=XXXX
$ export AWS_DEFAULT_REGION=us-west-2

$ aws s3 sync . s3://hoge-web-server --delete --acl public-read
fatal error: An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
```

と、当然ながらエラーが出ます。このエラーが出なくなるような Policy を作成しましょう。

## IAM Policy の作成

Policy の作成は AWS Console でやった方が楽です。 **AWS Console > IAM > Policies > Create policy ボタン > JSON タブ** のようにアクセスして以下のような JSON を入力します。

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:PutObjectAcl",
                "s3:ListBucket",
                "s3:DeleteObject",
                "s3:GetBucketLocation"
            ],
            "Resource": [
                "arn:aws:s3:::hoge-web-server",
                "arn:aws:s3:::hoge-web-server/*"
            ]
        }
    ]
}
```

初見では一体これは何なんだ?! と面食らいますが、これが権限を表す Policy という物です。この例では 「何処に(Resource)」「何を(Action)」「出来る/出来ない(Effect)」が書かれてます。

この Policy の作成が一番むずかしい所です。AWS のドキュメントをくまなく探してもピッタリの物は出てこないので、トライアンドエラーで試す事になります。S3 の場合一応以下が参考になります。

* [ポリシーでのアクセス許可の指定](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/using-with-s3-actions.html)

Policy を入力して次の画面で `hoge-policy` という名前で保存すると、`hoge-policy` へのリンクが出てくるので、Policy ARN を控えておきます。

### IAM Policy 引っかかりやすい所。

* エラーメッセージからではどの Action が足りてないのか分からない事があります。stackoverflow とか見ながら調べるしか無いような気がします。
    * この場合だと、`aws s3 sync` の `--acl` オプションでファイルを一般公開しますが、このオプションには `s3:PutObjectAcl` 権限が必要です。ただドキュメントには書いてないので勘で探しました。
* 上記の例では Resource として `arn:aws:s3:::hoge-web-server` と `arn:aws:s3:::hoge-web-server/*` の2つが指定されています。この2つは似ているようで意味合いが違います。
    * `arn:aws:s3:::hoge-web-server`: S3 Bucket (S3 の入れ物全体) を表します。`ListBucket` などの指定に必要。
    * `arn:aws:s3:::hoge-web-server/*`: S3 Object (それぞれのファイル) 表します。`s3:PutObject` などの指定に必要。

## IAM User と IAM Policy を関連付ける

作成した user name と policy ARN を使って、User と Policy を関連付けます。最初のコンソールで以下を入力します。

```
aws iam  attach-user-policy --user-name hoge-user --policy-arn arn:aws:iam::01234:policy/hoge-policy
```

## テスト

二番目のコンソールで試験

```
aws s3 sync . s3://hoge-web-server --delete --acl public-read
```

## 片付け

S3 Bucket の削除。

```
aws s3 rb s3://hoge-web-server --force
```

IAM User と Policy の削除。これは AWS Console でやった方が断然楽です。

* AWS Console > IAM > Users > hoge-user > Delete User
* AWS Console > IAM > Policies > hoge-policy > Delete Policy


## 他に参考にしたもの

* [AWS permissions required for sync](https://stackoverflow.com/questions/48894886/aws-permissions-required-for-sync)
* [Policy simulator](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_testing-policies.html)