初学者のためのAWS入門(4) - S3でBasic認証を設定してリポジトリを公開

#### 初学者のためのAWS入門シリーズ
- [初学者のためのAWS入門(1)] (https://qiita.com/mshinoda88/items/e7557227f7060b52d8ae) 
- [初学者のためのAWS入門(2) - CloudFormation入門1](http://qiita.com/mshinoda88/items/c5b238212c2de850efdd) 
- [初学者のためのAWS入門(3) - CloudFormation入門2](https://qiita.com/mshinoda88/items/65e7e145c7ec943af403) 
- 初学者のためのAWS入門(4) - S3でBasic認証を設定してリポジトリを公開 -> この記事です。
- [初学者のためのAWS入門(5) - S3+Athena+QuickSightでデータ分析結果の可視化](https://qiita.com/mshinoda88/items/54d1d8c3b660e88e6198)
- [初学者のためのAWS入門(6) - Lambdaでサーバレス実行してみる](https://qiita.com/mshinoda88/items/1b44f2a19c0a0895ab60)

S3を外部にWeb公開する際に、認証設定してみます。
S3自体にBasic認証機能は提供されていませんので、Lambda@Edgeという機能を使うことで、サーバレスにBasic認証をかけることができます。

# 作業概要

## Lambda@Edge とは
![lambda_edge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/a62e0def-fca7-071a-7d81-af3a03e4977a.png)

CloudFrontのイベントに対してLambda関数を実行できる機能。Lambda@Edgeが実行できるCloudFrontのイベントは、以下の4つがあります。

1. CloudFrontがエンドユーザからHTTPリクエストを受信(ビューワーリクエスト)
2. CloudFrontが受け取ったHTTPリクエストをオリジンサーバに転送する前(オリジンリクエスト)
3. CloudFrontがオリジンサーバからレスポンスを受信(オリジンレスポンス)
4. CloudFrontがエンドユーザにレスポンスを返す前(ビューワーレスポンス)

Lambda@Edge のユースケースとして以下の用途がドキュメントに書かれていました(一部抜粋)。

```text

A/B テスト用に、異なるバージョンのサイトに URL を書き換えるために Cookie を検査します
リクエストを送信したデバイスに関する情報が含まれている、User-Agent ヘッダーに基づいてユーザーにさまざまなオブジェクトを送信します。たとえば、ユーザーのデバイスに応じて、さまざまな解像度のイメージをユーザーに送信できます。
ヘッダーまたは認証トークンを検査し、対応するヘッダーを挿入して、リクエストをオリジンに転送する前にアクセス制御を有効にします。
ヘッダーの追加、削除、変更、および URL パスの書き換えを行い、キャッシュの異なるオブジェクトにユーザーをダイレクトします。
未認証ユーザーをログインページにリダイレクトしたり、エッジから直に静的ウェブページを作成して配信したりといったことを行う新しい HTTP レスポンスを生成します。
```
[AWS Lambda@Edge - AWS Lambda] (http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-edge.html) 

## サービス構成
以下のサービスを組み合わせて実現させます。

#### S3
- 取りに行きたいファイルを置く

#### CloudFront
- Lambda@Edgeとの連携
- キャッシュ、TTLは無効化する

#### Lambda@Edge
- Basic認証を設定(Authorizationヘッダを追加する)

## 作業の流れ
1. 公開するS3のバケットを作成
2. Basic認証を処理するlambda関数を作成
3. Cloudfrontの作成
4. Cloudfrontの設定
5. 公開ページにアクセスして確認

# 1. 公開するS3のバケットを作成
S3のバケットを作成する。
<img width="843" alt="s3_03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/d553f62e-467f-71fc-5fb1-a9097e223cb7.png">


# 2. Basic認証を処理するlambda関数を作成
右上のリージョン設定で「バージニア北部」を選択してから「関数の作成」をすること。そうしないと、
<img width="343" alt="スクリーンショット 2019-09-30 4.16.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/92b2eb7c-de8d-d68d-8d4a-552ad86d7acf.png">
のようなエラーになり、失敗します。

「一から作成」を選択。関数名として任意の名前を入力。ここでは「BasicAuthentication_bw_data」

ランタイムは「Node.js 10.x」を選択。
<img width="917" alt="lambda_02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/2e318835-8298-b6d8-4ad4-b14c68cdda2d.png">

ロールは「テンプレートから新しいロールを作成」を選択
ロール名には任意の名前を入力。ここでは「role_s3_read」
ポリシーテンプレートは「基本的なLambda@Edgeのアクセス権限(CloudFrontトリガーの場合)」を選択

<img width="1020" alt="スクリーンショット 2019-09-30 4.19.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/684a4328-1eb4-8e70-315e-abe95545ee01.png">



「関数の作成」をクリックしてしばらくすると次の画面に切り替わります。
<img width="1185" alt="lambda_03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/9df27641-9467-385a-63a2-fddfed5df9f4.png">

関数入力欄に以下をコピー。
https://gist.github.com/lmakarov/e5984ec16a76548ff2b278c06027f1a4

```javascript

'use strict';
exports.handler = (event, context, callback) => {

    // Get request and request headers
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    // Configure authentication
    const authUser = 'user';
    const authPass = 'pass';

    // Construct the Basic Auth string
    const authString = 'Basic ' + new Buffer(authUser + ':' + authPass).toString('base64');

    // Require Basic authentication
    if (typeof headers.authorization == 'undefined' || headers.authorization[0].value != authString) {
        const body = 'Unauthorized';
        const response = {
            status: '401',
            statusDescription: 'Unauthorized',
            body: body,
            headers: {
                'www-authenticate': [{key: 'WWW-Authenticate', value:'Basic'}]
            },
        };
        callback(null, response);
    }

    // Continue request processing if authentication passed
    callback(null, request);
};
```

以下の部分がBasic認証のユーザとパスワード部になります。

```javascript

    // Configure authentication
    const authUser = 'user';
    const authPass = 'pass';
```

<img width="1195" alt="lambda_05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/d0611106-35f2-03a2-f07f-7bbb62e46ede.png">
入力後、右上の「保存」ボタンをクリックして保存する。

「アクション」→「新バージョンの発行」を選択。
<img width="777" alt="lambda_06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/27655e11-838c-f7ec-ac1f-28834ad9d30c.png">

任意の説明を記入して、「発行」→これでCloudFrontから参照できるようになった。
<img width="729" alt="lambda_07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/baac813f-cd14-9be2-3179-5817f2c4957a.png">

右上のARNをコピーして記録しておく。後ほどCloudFrontから参照する際にこのARNをペーストすることになる。
<img width="689" alt="lambda_08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/0609f55d-9636-867d-bd01-583d4b2c5ad3.png">

# 3. Cloudfrontの作成

「ClouｄFront」サービスを選択し、
<img width="630" alt="cf_01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/65714c1d-aa8d-df04-2496-994465771bdf.png">

「Web」の「Get Started」を押下。
<img width="781" alt="cf_02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/144ddc1b-9c8a-2a62-138d-b8fd2078501c.png">

### Origin Settins
#### Origin Domain Names
プルダウンから選択。
#### Restrict Bucket Access
「Yes」を選択
### Origin Access Identity
「Create a New Identity」を選択
#### Grant Read Permission on Bucket
「Yes, Update Bucket Policy」を選択

<img width="713" alt="cf_03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/23629f37-1b49-d5bb-0120-ebd54b760438.png">

### Default Cache Behavior Settings
#### Object Caching
「Customize」にして
各TTLを0にすることでキャッシュされないようになるので、テスト環境の構築時には便利。
<img width="527" alt="cf_04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/f34078e0-b0c8-3ddb-ef9e-bf5faf93c53d.png">

### Lambda Function Associations
「CloudFront Event」を「Viewer Request」を選択。
「Lambda Function ARN」に先ほどコピーしたARNをペーストする。

<img width="708" alt="cf_05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/a36127bc-5245-8c04-7798-63eb04d9ac07.png">

### Distribution Settings
#### Default Root Object
「index.html」と設定しておくことでroot URL（ドメイン+/で終わるURL）でアクセスした時のデフォルトファイルを設定できる。
<img width="1047" alt="cf_06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/ef17e147-09ab-8ac8-0b45-bcdda2ef36b8.png">

最後に右下の「Create Distoribution」をクリックして完了を待ちます。
CloudFrontの「Distribution」を選択し、状態を確認する。
<img width="1076" alt="スクリーンショット 2019-09-30 4.24.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/74f73468-82d0-ad88-5495-59e8c06c35a5.png">

状態が「In Progress」から「Deployed」になったら完了。
<img width="1147" alt="cf_ditribute_list02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/de383ce8-1160-9097-1970-148ebe7ad858.png">

# 4. Cloudfrontの設定

デフォルトの設定でエラーキャッシュが効いてしまうのでそれを無効にしておく。
作成したDistoributionを選択して、「Error Pages」のタブを選択。
「Create Custom Error Response」をクリック。
<img width="750" alt="cf_edit_01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/3a5343e2-5eca-fc53-91aa-b7c2635f6729.png">

403エラー時のTTLを0に設定
<img width="534" alt="cf_edit_02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/083ee4bc-df86-184e-6f27-c0d4a2d2e718.png">

404エラーはindex.htmlを開く、のようにしたい場合は
Custom Error ResponseをYesにすることで設定可能です。
（Response Page Pathは / から始まる必要があります。）
<img width="534" alt="cf_edit_03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/00381f02-406b-3dff-5f3e-3e3a0e0dd3cc.png">

エラーページの設定を確認。
<img width="915" alt="cf_07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/fa39a720-fae4-e118-34cb-f14629a3b6a6.png">

# 公開ページにアクセスして確認

公開されるドメインはここで確認できる。http://XXX.cloudfront.net
<img width="767" alt="cf_domain.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199265/76ea3b72-240d-d3e2-086a-8e1715ee52be.png">