S3 で ホスティングしたウェブサイトをSSL化し、かつ特定の IP のみに開示する方法

#はじめに
この記事は2020年の RevComm アドベントカレンダー25日目の記事です。クリスマス当日ですね！

前日は @qii-purine さんの「pythonでのアーキテクチャを考える」でした。


今回は最後となりますが、S3 で ホスティングしたウェブサイトをSSL化し、かつ特定の IP のみに開示する方法について紹介します。

# やりたいこと

aws 使っている会社で、自分の作ったサイト（例えばデモサイト）を社内で共有するとき、みなさまどのように共有しますか。

典型的なやり方だと EC2 や Fargate でフロントエンドのサーバを立てるのではないかと思います。また、複数の静的ウェブページであれば、サーバーレスも検討するのではないかと思います。

そんな中、A) 頻繁にアクセスしない、B) 静的ファイルでも良い といった場合、 **S3でのホスティング** はコスト的に有効です。

しかし A) 社内のVPN のみで共有したい、B) サイトを転送時に暗号化したい と言う条件が加わると、**SSL化** と **特定のIPのみに開示** する必要があります。そのためには SSL 証明書発行 と ファイヤーウォール を用意する必要があります。

今回 Route 53, WAF, CloudFront, Certificate Manager, S3 を使って, どのようにSSL化して S3 でホスティングするかを紹介します。


また、今回 CloudFront を使いますが、CloudFront の場合はデータがキャッシュされるため、TTL (Time to Live) を意図的に設定しない限り変更が即時反映されません。TTL を短くすればいい話ですが、今回 S3 にあるサイトを変更したときにどのようにサイトを更新し、変更を即反映させるかについて紹介します。



( 内容は[こちらのリンク](https://qiita.com/jasbulilit/items/73d70a01a5d3b520450f)とほぼ被りますので、もし、本記事でわからないことがあればそちらを読んでいただけると幸いです)



# 全体構成

全体構成はこんな感じです。

![diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/767ed321-f57c-8957-29fd-c7a635fae28e.png)



# 作成手順

## 取り組む前の準備

- Route 53 でメインドメインをまだ作成していない場合は作成してください。
- Route 53 に追加するレコード名を予め、検討してください。( revcomm-christmas-demo.example.com　とします。)


## 素材の準備
まず、画像を用意します。(頑張ってコピー等でダウンロードしてください。)

![christmas_tree.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/7d039b09-1954-eb98-6568-4a6177416f99.png)

## コードの準備


```index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8'>
    <title>Merry Christmas</title>
    <link rel='stylesheet' type='text/css' media='screen' href='main.css'>
</head>
<body>
    <div class="content">
        <img class="tree" src="./christmas_tree.png">
    </div>
</body>
</html>
```

```main.css
.content{
    text-align:center;
    position: relative;
    width: 100%;
    height: 100%;
}

.content .text{
    position: absolute;
    text-align:center;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-family: 'Charm', cursive;
    font-size: 5em; 
}

html {
    width: 100%;
    height: 100%;
}

body{
    width: 100%;
    height: 100%;
    background: radial-gradient(#ffffff 50%, #91bee5 100%);
}


.ball{
    position: absolute;
    padding: 0px;
    margin: 0px;
    width: 20px;
    height: 20px;
    border-radius: 10px;
    background-color: rgb(218, 233, 247);
}
```




## S3 の設定

S3 にアクセスし、バケットを新規作成してください。(revcomm-christmas-demo にします)

![s00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/f0309595-f82c-bc36-4d14-5830a85b069c.png)
![s01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/31a62b7c-20c7-c7bc-393f-2b6e85d28aa7.png)


次にコンテンツをアップロードしてください。（ドラッグアンドドロップで可能）

![s03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/967e4d8d-8271-87e3-af38-b5f2da6f99ae.png)

これで S3 にコンテンツが追加されました。


## Certificate Manager の設定

SSL 証明書の作成です。
Certificate Manager にアクセスし、**Region を N.Virginia** にした状態で SSL 証明書の新規作成をクリックしてください。

![](image/cm00.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/d286503d-e20d-b766-d800-670ad8d17b70.png)

予め検討した URL を入力してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/3b3ebe90-6e23-68eb-4868-33eebbb1afe1.png)

DNS検証 にしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/627bfc53-1c27-7fa0-a549-1ecf7f21fe8b.png)

レコード作成を選択してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/64f0149d-b4a0-8c56-b479-3b5f33180081.png)

レコードを追加すると、 Route 53 に SSL 証明書用のレコードが追加されます。

## CloudFront の設定

CloudFront にアクセスし、CloudFront Distribution の新規作成をクリックしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/427baa57-1698-3bb2-07fd-938c99b894b3.png)

以下のようにパラメータを修正してください。(英語のままですが、ご容赦願います。)

- Enable Origin Shield: No   
- Restrict Bucket Access: Yes   
- Origin Access Identity: Create New Identity
- Generate Read Permissions on Bucket: Yes   
- Viewer Protocol Policy: Redirect   
- Price class: Use All Edge   
- Default Root Object: index.html   
- Alternate Domain Names: 予め検討した URL (revcomm-christmas-demo.example.com)
- SSL Certificate: Custom SSL Certificate   
- Custom SSL: 予め検討した URL をタイプしてみてください。対応する SSL が出ます。(revcomm-christmas-demo と書けば出るはず)

![cf01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/3cbae832-f406-8494-a798-8bb6e8394cb1.png)
![cf02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/9c7ab020-bd61-753e-064d-70270cd2fa2c.png)


しばらくすると、デプロイが完了します。（15分以上かかる可能性あり）
完了したら、 CloudFront のドメイン名をメモってください。

![cf03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/7fdaecbd-bf95-2455-e8ff-96155bcc6340.png)

**補足**

- S3 のポリシーも自動的に更新します。
- OAI (Origin Access Identity) を使用することで、 S3 をpublic化をしない状態でホスティングすることができます。

## Route 53 のレコード作成

Route 53 にアクセスし、ドメイン名 ＞　レコードを追加　をクリックしてください。

![](image/r00.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/65d9b12e-ed39-31f4-e093-50dfd2766827.png)

レコードを作成してください。
Route ポリシーを Simple Route、 レコード名を予め検討した URL のサブドメイン名(revcomm-christmas-demo)、 レコードタイプをCNAME、 Value を先ほどメモった CloudFront を URL 入力してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/d1e1027a-683e-9939-01f7-693ed4e89c20.png)

## WAF の設定

WAF にアクセスし、IP Sets で **Region を Global (CloudFront)** にした状態で新規 IP Set を作成してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/616c7edc-91af-3002-c227-1b038f1bb305.png)

IP を設定してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/89f4fa93-60c5-7a29-d08f-940313e49ee7.png)


次にファイヤウィールの設定です。Web ACLs (Access Control List) で **Region を Global (CloudFront)** にした状態で 新規 ACL を作成してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/269c1483-efec-74b8-6a52-b8b9f0907792.png)

任意の名前を設定して、関連リースの追加をクリックしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/9f135a4c-78a7-496b-7e89-4e117bca9821.png)

WAF に関連するリソースを追加してください。(CloudFrontのID番号が表示する)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/3b0833a0-e19a-c0c5-cec4-23f0987536ff.png)

次へをクリックし、Add rules > Add my own rules ... をクリックしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/ad108863-c3ef-beee-091b-311cd7efa2e7.png)

ルールタイプ IP Set を選択し、 ルール名（任意）を好きな名前にした状態で先ほど作成した IP set を入力し、作成してください。Default Action を Allow にしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/72ff0564-4360-d631-7341-5283e4200e63.png)

デフォルトを Block にしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/f79efa16-831b-193b-813b-ace531023700.png)

あとは ACL を作成して完成です。



## 結果

予め検討した URL (revcomm-christmas-demo.example.com) にアクセスすると、以下のようになります。

![f00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/ae5e6421-1766-8bba-623a-294119e1231b.png)

ちなみに IP アドレスを変えると、以下のようになります。

![f01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/5493c523-bd85-cba1-d3ce-b16c49251419.png)


# 更新手順

次に更新方法です。

## コードの修正

以下のようにコードを修正してください。

```index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset='utf-8'>
        <title>Merry Christmas</title>
        <link rel='stylesheet' type='text/css' media='screen' href='main.css'>
        <link rel="preconnect" href="https://fonts.gstatic.com">
        <link href="https://fonts.googleapis.com/css2?family=Charm:wght@700&family=Pacifico&display=swap" rel="stylesheet">
    </head>
    <body>
        <div class="content">
            <img class="tree" src="./christmas_tree.png">
            <div class="text">
                We wish you a Merry Christmas <br />
                and a Happy New Year
            </div>
        </div>
    </body>

    <script>
        function drop_snow(){

            var snow_ball = document.createElement("div")
            snow_ball.className = "ball"
            snow_ball.style.top = 0 + 'px'
            snow_ball.style.left = Math.random() * document.body.clientWidth  + 'px'

            var content = document.getElementsByClassName('content')[0]
            content.appendChild(snow_ball);

            var pos = 0
            var refreshIntervalId = setInterval(frame, 10);

            function frame() {
                if (pos > document.body.clientHeight) {
                    content.removeChild(snow_ball);
                    clearInterval(refreshIntervalId);
                } else {
                    pos+= 1;
                    snow_ball.style.top = pos + 'px';
                    snow_ball.style.opacity = (1 - pos/document.body.clientHeight)
                }
            }
        }

        for (let i=0; i < 10; i ++){
            drop_snow()
        }
        setInterval(drop_snow, 500);
    </script>

</html>
```


```main.css
.content{
    text-align:center;
    position: relative;
    width: 100%;
    height: 100%;
}

.content .text{
    position: absolute;
    text-align:center;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-family: 'Charm', cursive;
    font-size: 5em; 
}

html {
    width: 100%;
    height: 100%;
}

body{
    width: 100%;
    height: 100%;
    background: radial-gradient(#ffffff 50%, #91bee5 100%);
}


.ball{
    position: absolute;
    padding: 0px;
    margin: 0px;
    width: 20px;
    height: 20px;
    border-radius: 10px;
    background-color: rgb(218, 233, 247);
}
```

## S3に再度アップロード

再度作成した S3 バケットにアップロードしてください。


## CloudFront のキャッシュ無効化

CloudFront を通して一度サイトにアクセスすると、作成したウェブページがキャッシュされます。
そこで、キャッシュを無効化する必要があります。キャッシュを無効化するためにはまず CloudFront にアクセスし、作成した CloudFront Distribution をクリックしてください。

![cf10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/e1a8cbed-5bdf-b46b-335f-3c6f9a89dbe6.png)

Invalidations で 無効化の作成 をクリックしてください。

![cf11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650642/f7ec180f-a2e2-04b6-ee28-313aac417512.png)

あとは index.html 入力し、無効化を実行してください。

結果、こんな感じになります。(codepen です。画像だとつまらないので。)

<p class="codepen" data-height="900" data-theme-id="light" data-default-tab="result" data-user="zomaphone" data-slug-hash="GRjMmNp" style="height: 900; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="aws_s3_ssl_example1">
  <span>See the Pen <a href="https://codepen.io/zomaphone/pen/GRjMmNp">
  aws_s3_ssl_example1</a> by zomaphone1 (<a href="https://codepen.io/zomaphone">@zomaphone</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>

# まとめ

以上で、S3 で ホスティングしたウェブサイトをSSL化し、かつ特定の IP のみに開示する方法について一つ紹介しました。

通常のホスティングであれば S3 で設定が済みますが、SSL化 と 特定の IP に対して公開したい場合、上記の手段は有効です。

もちろん他の手段として、S3 だけで解決する手段もあります。

[こちらの記事](https://qiita.com/Yuhkih/items/1754ac9caf3f12d75946)のようにアクセスポイントを変えるだけ https で公開することもできます。IP と https のみを許容する場合は bucket policy を変えるだけで済みます。

```
Before:
https://revcomm-christmas-demo.s3-website-ap-northeast-1.amazonaws.com/
After: 
https://s3-ap-northeast-1.amazonaws.com/revcomm-christmas-demo/index.html
```
ただし、このやり方で注意していただきたいのが、`https` と `index.html` を明示的に示さないといけないことです。そのため、共有するときに URL の扱いに注意しないといけなくなります。

もし、リダイレクト等も含めたい、index.html まで記述させたくない場合、ぜひこちらの記事を参考に実施していただけると幸いです。

# 最後に

いかがでしたでしょうか。

今回の Qiita advent calendar は RevComm として初の試みではありましたが、有益な情報は得られましたでしょうか。

RevComm では「コミュニケーションを再発明し、人が人を想う社会を創る」というミッションを基に、電話営業をディープラーニングの技術で支援するプロダクト( [Miitel](https://miitel.revcomm.co.jp/) ) をはじめ、コミュニケーションに関わる様々なプロダクト開発を行っており、日頃からコミュニケーションの在り方を再定義するという難しい課題に取り組んでいます。

弊社ではテックに関わらず成長に貪欲な人がたくさんいます。会社としてまだまだ若いところもありますが、新しい技術等に積極的に取り入れる会社ではあるので、エンジニアとしてテックスタックを広げたいという人にとってはいい会社です。

もし、弊社で一緒に働きたいという想いがあれば、あるいは少しでも興味があればぜひぜひ弊社の[採用ページ](https://hrmos.co/pages/revcomm/jobs)に応募してみてください。

ちなみに働き方について興味があれば、ぜひ CTO が書いた[こちらの記事](https://qiita.com/hiratake55/items/c37585d93138781e940a)を読んでください。



では良いクリスマスを！！