AWS S3で公開している静的サイトにBasic認証をかける

AWS S3で公開している静的サイトにBasic認証をかける方法です。
S3の他に、LambdaとCloudFrontの設定が必要になります。

# S3バケットの作成
まずはBasic認証をかけたいS3のバケットを用意します。
パブリックアクセスの許可や、Static website hostingの設定は必要ありません。

# CloudFrontの設定

Create distribution -> Web側のGet Startedを選択し、CloudFrontの設定に進みます。

![CloudFront Get Started](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/2eed0ac0-2f77-2f3b-99bb-7a2a9041a4d5.png)

`Origin Domain Name` にBasic認証をかけたいS3のバケットを指定します。
`Origin ID` は自動的に設定されます。
`Restrict Bucket Access` を `Yes` にします。
`Origin Access Identity` は `Create a New Identity` にします。
これで `Comment` 部分は自動で設定されます。
`Grant Read Permissions on Bucket` は `Yes, Update Bucket Policy` を選択してください。

![CF設定1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/480718f7-63c2-abd1-eabd-d29382da582c.png)

キャッシュは無効にするので、 `Cache Policy` は `Managed-CachingDisabled` を選択してください。

![CF設定2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/acca235d-8302-6223-f13e-ffc4d2a0a0a8.png)

`Default Root Object` を `index.html` 等に設定すれば、ルートにアクセスした際のデフォルトファイルを指定可能です。

![CF設定3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/99e9f684-3468-0852-6f39-cf679003ce8a.png)

その他はデフォルト設定でOKです。
ここまで設定したら、Create Distributionを押して作成しましょう。

デプロイが完了したら、指定したS3のバケットポリシーに設定が追加されていることがわかります。
![S3バケットポリシー](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/07c30e4d-a080-6fa0-cbee-9a879c428947.png)

また、 CloudFrontの `Error Pages` の設定で、 `403` `404` エラーの際は `/index.html` へリダイレクトするように設定しておきましょう。
この設定がなければ、SPAのサイトではルート以外のURL（例えば `/about` など）で画面を更新した際にエラーになってしまいます。

![スクリーンショット 2020-10-22 1.37.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/fb50b777-2b03-5b14-8140-a6100d9680fe.png)


# Lambdaの設定

Basic認証を行うためのLambda関数を作成します。
`バージニア北部(us-east-1)` から作成を行ってください。

`関数名` は `basicAuthentication` としています。
`ランタイム` は `Node.js` です。
`デフォルトの実行ロールの変更` では、 `AWSポリシーテンプレートから新しいロールを作成` を選択し、
`ロール名` に `lambda_edge_exec` （任意の名前）、 `ポリシーテンプレート - オプション` に `基本的な Lambda@Edge のアクセス権限 (CloudFront トリガーの場合` を設定します。

![Lambda作成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/be76f696-027f-4c18-b8d5-825200468ec0.png)

関数コードの `index.js` に下記のコードを貼り付けます。
`const authUser = 'user';` と `const authPass = 'pass';` の部分で、Basic認証のuserとpassを設定します。

その後 `Deploy` ボタンを押してデプロイします。

```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);
};
```

![関数コード](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/6ec32071-8b3f-1d54-2d3c-c6b39296df34.png)

アクション -> Lambda@Edgeへのデプロイ を選択します。

![Lambda@Edgeへのデプロイ1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/ed5b5b8e-cf3b-ab64-31a3-bdbd730742df.png)

`ディストリビューション` に先程作成したCloudFrontを指定します。
`CloudFrontイベント` は `ビューアーリクエスト` を選択します。
`Lambda@Edgeへのデプロイを確認` にチェックして、デプロイを行います。

![Lambda@Edgeへのデプロイ2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/bb9ed243-2562-3ba2-7db5-8e7961c21e27.png)

CloudFrontを確認すると、 `status` が `In Progress` になっていることが確認できます。
デプロイが完了すると、CloudFrontのドメインネームにアクセスした際、Basic認証がかけられていることが確認できます。

![basic認証](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/e92a6fad-1846-8380-a030-dc3d7ed221fd.png)

ちなみに、 `バージニア北部(us-east-1)` 以外で作ったばかりのS3のバケットにCloudFront経由でアクセスしても、リダイレクトされてしまいます。
しばらく待つか、下記の解決法を参考にしてください。
[S3+CloudFrontでS3のURLにリダイレクトされてしまう場合の対処法](https://dev.classmethod.jp/articles/s3-cloudfront-redirect/)