AWS CLIで Web サイトを構築、管理、運用する(7日目)

6日目では、独自ドメインと証明書を割り当てて、Web サイトにアクセスできるようにしました。
7日目は、静的Webサイト設定になっている S3 の設定を見直します。

# 7日目の要約
設定を見直すよ！

# AWS CLI の準備
このあたりをみて、好きなバージョンとお使いのOSにあった環境設定をしてくださいね。
なんなら、 AWS CloudShell で実行するのも楽でよいと思います。
この記事シリーズは、AWS CloudShell で実行し、実行例を載せています。

バージョン１
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv1.html

バージョン２
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2.html

# 概要
CloudFrontを導入したので、 S3 で静的 Web ホスティングを行う必要がなくなりました。
S3 の静的 Web ホスティングをやめ、CloudFront からのアクセスのみを許可するように設定しなおします。

# さあ、やってみよう！
## Origin Access Identity(OAI)を作成する
まずは、CloudFront から S3 にアクセスするための認証情報（OAI）を作成します。
cloudfront create-cloudfront-origin-access-identity コマンドを実行します。

```sh
uuid=`uuidgen`
aws cloudfront create-cloud-front-origin-access-identity \
    --cloud-front-origin-access-identity-config \
        CallerReference="${uuid}",Comment="OAI"
```

コマンドが正常に実行でき、OAI の作成ができると、以下のような json が返ります。
このうち、CloudFrontOriginAccessIdentity 内の Id の値がこの後必要になるので確認しておいてください。

```json
{
    "Location": "https://cloudfront.amazonaws.com/2019-03-26/origin-access-identity/cloudfront/***************",
    "ETag": "**************",
    "CloudFrontOriginAccessIdentity": {
        "Id": "**************",
        "S3CanonicalUserId": "<S3CanonicalUserId>",
        "CloudFrontOriginAccessIdentityConfig": {
            "CallerReference": "<${uuid}の値>",
            "Comment": "OAI"
        }
    }
}
```

## S3 バケットポリシーを編集する
OAI からのアクセスのみに許可するため、バケットポリシーを編集します。
一旦、現状の設定をダウンロードし、編集用の元にします。
まずは、 s3api get-bucket-policy コマンドで取得します。

```sh
aws s3api get-bucket-policy --bucket <バケット名> > bucketpolicy_tmp.json
```

実行に成功すると以下のように出力されます。
エスケープされていて若干読みづらいので、jq を使って、皮剥きをします。

```sh
cat bucketpolicy_tmp.json | jq '.Policy | fromjson' > bucketpolicy_now.json
```

```json:bucketpolicy_now.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicWebHosting",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::<バケット名>/*"
    }
  ]
}
```

この取得した json をもとに、Principal の値を書き換え、下記のようにOAIのものにします。

```json:bucketpolicy_oai.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicWebHosting",
      "Effect": "Allow",
      "Principal": {
            "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <OAI IDの値>"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::<バケット名>/*"
    }
  ]
}
```

書き換えが行えたら、 s3api put-bucket-policy を実行します。
実行の仕方は [3日目](https://qiita.com/hirosys/items/29a4ba8bd076cfc95b93#%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B)と同様です。

```sh
aws s3api put-bucket-policy --bucket <BUCKETNAME> --policy file://bucketpolicy_oai.json
```

やはり、このコマンドが正常終了しても特に何も返しません。
これで、OAIからのアクセスのみ許可されるバケットポリシーができました。

## S3 のパブリック公開設定を解除する
Web サイトホスティング機能を有効にする際にオフにしていた、ブロックパブリックアクセスの設定をオンにします。
これで、S3 バケット自体がパブリック公開状態ではなくなります。

```sh
aws s3api put-public-access-block --bucket <バケット名> \
--public-access-block-configuration \
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
```

特に応答はありません。

## CloudFront のオリジン設定を変更する

基本的には、[6日目](https://qiita.com/hirosys/private/c339bf0d85d4a8ac74e5#cloudfront-%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B%E6%BA%96%E5%82%99)で実施したものと似た内容です。

1. cloudfront get-distribution-config コマンドで現状の設定を取得する
1. ETag の値を確認する
1. 取得した json ファイルに対して、設定変更を加える
1. cloudfront update-distribution コマンドを実行して更新する

この記事では、3と4についてのみ記載します。１と２の実施内容については、[6日目](https://qiita.com/hirosys/private/c339bf0d85d4a8ac74e5#cloudfront-%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B%E6%BA%96%E5%82%99)の記事をご確認ください。

### 取得した json ファイルに対して設定変更を加える
Origins > Items 内にある、 S3 Web サイトホスティングの設定に関する情報を一旦全て削除します。

```json:削除箇所
      {
        "Id": "<S3 Webサイトホスティングの FQDN>",
        "DomainName": "<S3 Webサイトホスティングの FQDN>",
        "OriginPath": "",
        "CustomHeaders": {
          "Quantity": 0
        },
        "CustomOriginConfig": {
          "HTTPPort": 80,
          "HTTPSPort": 443,
          "OriginProtocolPolicy": "http-only",
          "OriginSslProtocols": {
            "Quantity": 3,
            "Items": [
              "TLSv1",
              "TLSv1.1",
              "TLSv1.2"
            ]
          },
          "OriginReadTimeout": 30,
          "OriginKeepaliveTimeout": 5
        },
        "ConnectionAttempts": 3,
        "ConnectionTimeout": 10,
        "OriginShield": {
          "Enabled": false
        }
      }
```

削除したところに以下を編集して、貼り付けます。

```json:貼り付け内容
      {
        "Id": "<S3 バケット名>.s3.ap-northeast-1.amazonaws.com",
        "DomainName": "<S3 バケット名>.s3.ap-northeast-1.amazonaws.com",
        "OriginPath": "",
        "CustomHeaders": {
          "Quantity": 0
        },
        "S3OriginConfig": {
          "OriginAccessIdentity": "origin-access-identity/cloudfront/<OAI ID>"
        },
        "ConnectionAttempts": 3,
        "ConnectionTimeout": 10,
        "OriginShield": {
          "Enabled": false
        }
      }
```

次に、DefaultCacheBehavior　内の TargetOriginId を S3 バケットの FQDN にします。

```json:編集前
（省略）
        "DefaultCacheBehavior": {
            "TargetOriginId": "<S3バケット名>.s3-website-ap-northeast-1.amazonaws.com",
            "TrustedSigners": {
                "Enabled": false,
                "Quantity": 0
（省略）
```

```json:編集後
（省略）
        "DefaultCacheBehavior": {
            "TargetOriginId": "<S3バケット名>.s3.ap-northeast-1.amazonaws.com",
            "TrustedSigners": {
                "Enabled": false,
                "Quantity": 0
（省略）
```

あと、忘れてはならないのが、 DefaultRouteObject の指定です。
hogefuga.cf 的な指定でリクエストを飛ばした際、Web サイトホスティングが有効であればインデックスドキュメントに指定したファイルが返りますが、S3 バケットをオリジンとして使う場合には DefaultRouteObject の設定が必要です。

というわけで、以下のように、 DefaultRouteObject の設定を行います。

```json
"DefaultRootObject": "index.html"
```

### cloudfront update-distribution コマンドを実行して更新する
準備ができたら、6日目と同様に cloudfront update-distribution コマンドを実行して、設定変更を行います。

```sh
aws cloudfront update-distribution --id <CloudFront Distribution ID> ¥
--cli-input-json file://distributionconfig.json ¥
--if-match 確認したETag の値

```

コマンド実行に成功すると、Distribution の内容が記された json が返されます。
やはり長いので割愛します。

## S3 の静的 Web ホスティングの設定を解除する

CloudFront 側の設定が終わったので、S3 の静的 Web ホスティングの設定を解除します。
s3api delete-bucket-website コマンドを実行します。

```sh
aws s3api delete-bucket-website --bucket <バケット名>
```

正常に実行できた場合には、特にメッセージを返しません。

## 動作確認
名前解決ができ、各種設定に問題がなければ3日目に作成した HTML が表示されるはずです。

``sh
curl https://<独自ドメイン名>/
```


```HTML
<!DOCTYPE html>
<html lang="ja">
<head>
  <title>Advent calendar 2021</title>
</head>
<body>
  <h1>Hello world!!</h1>
  <h2>Advent calendar 2021 DAY 3</h2>
</body>
</html>
```


# まとめ
CloudFront が前面にでたので、 S3 の Web サイトホスティング機能を無効化しました。
併せて、S3 へのアクセスはOAIという仕組みを使って、CloudFrontのみからに絞るようにしました。
そして、ブロックパブリックアクセスをオンにして、S3 バケット自体はパブリック公開されないようにしました。

* 今回使ったコマンド
 * cloudfront create-cloud-front-origin-access-identity
 * s3api get-bucket-policy
 * s3api put-bucket-policy
 * s3api put-public-access-block
 * (cloudfront get-distribution-config)
 * cloudfront update-distribution
 * s3api delete-bucket-website