CloudFront + S3 での IP アドレスベースのアクセス制限を設定する静的なサイトを構築

　この記事は、[オールアバウト Advent Calendar 2017](https://qiita.com/advent-calendar/2017/allabout)の21日目の記事です。

　この前、弊社のデザインチームのために、社内環境のみでサイトのデザインが確認できる静的コンテンツサイトを構築しました。ここで構築手順をご紹介したいと思います。

# 実現したいこと
- 独自ドメインを利用する
- HTTPS通信できる
- 社内環境のみアクセスできる
- S3 のエンドポイントへの直アクセスはさせたくない

# 使用するAWSのサービス
- S3：静的なコンテンツの置き場所
- IAM：S3へコンテンツをアプロードするアカウントを作成
- Cloudfront：独自ドメインを利用するため
- Certificate Manager：HTTPS通信できるため
- WAF：IP 制限を設定する

# 設定詳細
## S3 バケット作成
　S3 のコンソールで「+ バケットを作成する」をクリックして、新しい S3 バケットを作成します。
　バケット名を入力して、「作成」ボタンをクリックして作成します。
![1.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/b47b4511-9030-ec15-34f5-37425f9007d7.jpeg)
　
　他の設定は一旦そのままにします。
　S3 のエンドポイントへの直アクセスはさせないため、「静的ウェブホスティング」が **<font color="red">無効</font>** のままにします。

## IAM作成
　S3 バケットが作成された後、コンテンツをアップロード用のアカウントを作成します。
　「ユーザーを追加」をクリックして、作業が開始します。
![2.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/cc06f26d-7150-72e4-da0e-57f08aee620b.jpeg)
　
　アクセスキーとシークレットアクセスキーを使いますので、「プログラムによるアクセス」をチェックして、「次のステップ：アクセス権限」をクリックします。
　
![3.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/b3bc366d-3d10-c4b1-e818-8d77feae22ad.jpeg)
　ここで、特に設定しなくても大丈夫ですので、「次のステップ：確認」をクリックします。
　
![4.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/a34e78dc-1d59-6f08-c6e9-fb9c7756a49c.jpeg)
　「ユーザーの作成」をクリックして、アカウントを作成します。
　次のページでアクセスキーとシークレットアクセスキーが表示されますので、メモを取ってください。
　**シークレットアクセスキーがユーザー作成のみ表示されますので、必ず保存してください。**

　最後、IAMの権限を設定します。
　ユーザー一覧でユーザー名をクリックして、「アクセス権限」タブの「インラインポリシーの追加」をクリックします。
![5.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/f918d759-621b-8c37-65ce-192bcf8b3845.jpeg)

　「カスタムポリシー」を選択して、「選択」ボタンをクリックします。
　ポリシー名を任意に記入して、下記のポリシードキュメントを記入して、「ポリシーの適用」をクリックします。

```json
{
    "Statement": [
        {
            "Action": [
                "s3:ListAllMyBuckets"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::*"
            ]
        },
        {
            "Action": [
                "s3:ListBucket",
                "s3:GetBucketLocation"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::＜S3バケット名＞"
            ]
        },
        {
            "Action": [
                "s3:ListBucket",
                "s3:GetBucketLocation",
                "s3:DeleteObject",
                "s3:GetObject",
                "s3:GetObjectAcl",
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:s3:::＜S3バケット名＞/*"
        },
        {
            "Action": [
                "s3:DeleteObject",
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Effect": "Deny",
            "NotResource": "arn:aws:s3:::＜S3バケット名＞/*"
        }
    ]
}
```
　
![6.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/5636e580-f426-6739-5d55-6052addba5b3.jpeg)
　これで、S3 バケット用アカウントが作成完了です。

## SSL証明書インポート
　Cloudfront 経由で HTTPS 通信するため、SSL証明書が必要です。自社で取った証明書を使うか、AWSが発行された証明書を使う2つの方法があります。
　すでにワイルドカードのSSL証明書を持っていますので、このまま使います。

　Cloudfront 経由で HTTPS 通信するのは**バージニア北部**リージョンのみ使えますので、SSL証明書を必ず**バージニア北部**リージョンにインポートするようにします。

　「証明書のインポート」をクリックします。
![7.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/9d03b84a-d2d0-7b4c-b3ff-c6c8364dbfeb.jpeg)

　証明書本文、プライベートキー、証明書チェーンを記入して、「レビューとインポート」をクリックします。
![8.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/75387d19-7d02-47f9-f130-951906bc6990.jpeg)

　レビューして、問題がなければ、インポートします。
![9.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/26b5afc9-7425-4d6f-27de-5f647cf1adda.jpeg)

## WAF設定
　Cloudfront のアクセス制限は WAF で設定します。
　WAF とは、ウェブアプリケーションファイアウォール です。

　左側の「Go to AWS WAF」をクリックして、作業開始します。
![10.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/18922be2-6836-8472-7e0d-a9ce250d8a9a.jpeg)

　「Configure web Acl」をクリックします。
![11.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/c2ad06b4-d800-aefd-76cc-3913556b90f0.jpeg)

　「Next」ボタンをクリックします。
![12.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/1b3bea34-019c-7358-4700-c51515a27f27.jpeg)

　ACL 名を任意に記入して、Region は「Global(CloudFront)」を選択して、「Next」ボタンをクリックします。
![13.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/72d44dc9-3c7c-3ff9-77bc-b3810f62a509.jpeg)

　「IP match conditions」の「Create condition」をクリックします。
![14.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/394d6ab8-447b-ca12-1818-da789059d5d7.jpeg)

　「Name」に任意の名前を記入して、許可する IP アドレスを記入して、「Add IP address range」をクリックして、「Create」ボタンをクリックします。
![15.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/3669122d-7687-231d-dc09-6f1ec42830f5.jpeg)

　IP アドレスが設定された後、「Next」ボタンをクリックします。
![16.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/ce39abdb-6721-6577-e415-a044401e2393.jpeg)

　「Create rule」をクリックして、制限ルールを作成します。
![17.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/c1ea7758-9044-1815-9939-f30b1a9b369e.jpeg)

　任意の Name を記入して、「Regular rule」を選択しします。
　「Add condition」で「originate from an IP address in」を選択して、先程作成した IP match conditions を選択します。
　「Create」ボタンをクリックします。
![18.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/39ea9490-d11e-13c9-9d93-85abc018157a.jpeg)

　上で作成されたルールを選択して、「Add another rule」をクリックして、「Allow」をチェック入れます。
　「Default action」は「Block all requests that don't match any rules」をチェック入れます。
　「Review and create」ボタンをクリックします。
![19.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/ca78d691-0479-34a5-7b89-14b74e7679e1.jpeg)

　最後レビューして、問題がなければ「Create」ボタンをクリックして、作成します。

## Cloudfront作成
　以上の作業が完了した後、最後に CloudFront を作成します。

　「Create Distribution」をクリックして、Web の「Get Started」ボタンをクリックします。
![20.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/15a0dabf-b7a9-b5bf-bac3-b78c68335732.jpeg)

### Origin Settings
- Origin Domain Name：最初に作成されたS3を選択します。
- Restrict Bucket Access：「Yes」 をチェックします。
- Origin Access Identity：「Create a New Identity」をチェックします。
- Grant Read Permissions on Bucket：「Yes, Update Bucket Policy」をチェックします。
![21.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/f717276e-7772-376b-e8a2-f328ebf5e5fe.jpeg)

### Default Cache Behavior Settings
　基本デフォルトのままでいいです。キャッシュのTTLを設定したい場合、「Object Caching」で「Customize」を選択して、TTL の時間（秒）を設定します。
![22.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/fae69d74-3a46-c1b5-a4d0-e7a1cbb9703e.jpeg)

### Distribution Settings
- AWS WAF Web ACL：先程作成した Web ACL を選択します。
- Alternate Domain Names(CNAMEs)：静的なサイトのドメインを記入します。
- SSL Certificate：「Custom SSL Certificate (example.com):」をチェックして、SSL証明書を選択します。
- Default Root Object：「index.html」を記入します。
![23.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/359d1606-7316-813f-0177-a15f5b6ce0e9.jpeg)

　「Create Distribution」をクリックして、CloudFront の Distribution を作成します。
　20〜30分程待つと CloudFront の Distribution の作成が完了します（Status は Deployed になる）。

## S3 バケットのアクセス制限確認
　CloudFront を作成する時、Origin Settings の設定るる所で、「Restrict Bucket Access」を「Yes」 とチェックしまたので、S3 バケットのアクセス制限のバケットポリシーが下記のように自動に生成されます。

　************ は Origin Access Identity の ID となります。

```json
{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ************"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::＜S3バケット名＞/*"
        }
    ]
}
```

　これまで、設定作業が全部完了となります。
　最後、動作確認を行います。

# 動作確認
## S3 のエンドポイントへの直アクセスはさせない
![24.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/1dc65956-570f-c79f-23db-a0bb90f0f17d.jpeg)

## Cloudfront のドメイン名でアクセスできる
![25.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/04f5ae79-d0b6-ecda-25f2-359f2db2bbc7.jpeg)

## 独自ドメインでアクセスできる
　CloudFront のドメイン名を nslookup して、いずれの IP をローカルホストに設定して、ブラウザで確認します。

```bash
$ nslookup **********.cloudfront.net
Server:		192.168.1.1
Address:	192.168.1.1#53

Non-authoritative answer:
Name:	**********.cloudfront.net
Address: 54.192.***.***
・・・・・・
```
　HTTPS通信も問題がありません。
![26.jpg](https://qiita-image-store.s3.amazonaws.com/0/57618/e3ff9972-3a7a-a659-b3e9-5bc8eaa1dc23.jpeg)

## 社内環境がアクセスできて、社外環境がアクセスできない
　モバイル通信の場合、アクセスできないことが確認できました。
<img width="414" alt="27.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/57618/7dbda3b3-4817-1e6c-9837-0f5f193f9954.jpeg">

　全部確認して問題がありませんので、最後 DNS を登録して、全部の作業が完了となります。