S3 Cloudfront でのサブディレクトリへアクセス Lambda@Edge

S3にて静的コンテンツをweb公開するとき、cloudfrontを噛ませると色々なメリットがるため、一般的になっていますが、その時の問題が、s3をstatic web hostingしなければ サブディレクトリがファイル指定しないと参照できない ということがあります。

https://example.com/ → https://example.com/index.html を参照してくれる(cloudfrontのDefault Root Objectの設定にて)
https://example.com/dir/ → https://example.com/dir/index.html を返してくれず403が帰ってくる

この時によくある設定方法としては、
s3でstatic web hostingをONにして、cloudfrontの向き先をS3のエンドポイントに設定するという方法がありますが、
個人的には、static web hostingをするのは、なんのために、cloudfront経由のみでしかs3にアクセスできなくしているのかという感じなので、避けたいところでした。

lambda@Edge を使う

ここで、

lambda@Edge を使うということで、どういうことかというと、
CloudFront ディストリビューションに Lambda 関数に関連付け、CloudFront エッジロケーションでリクエストとレスポンスが傍受することができ、次の CloudFront イベントの発生時に Lambda 関数を実行できます。

ここを参考にさせていただきました。ありがとうございます。

要するに、ざっくりとした流れは

  1. リクエスト
  2. Cloudfrontへのアクセス時にLambda@Edgeが呼ばれ
  3. Lambda@Edgeにて必要な処理をし、Cloudfrontに伝達
  4. Cloudfrontからs3参照
  5. レスポンス

ということで、

https://example.com/dir/

このようなアクセスが来た時に、index.htmlを付加しcloudfrontに通達してくれれば、403になることはありませんね。

Lambda関数を作成

  • AWSコンソールからlambdaを選択します
  • リージョンはバージニア北部 にします(このリージョンしかサポートしてません(2019年1月現在))
  • 必要項目を下記のように入力、選択

キャプチャです

キャプチャです

これで面倒なロールやアクセス設定はOKです。今は簡単ですね。
(Lambda@EdgeよりCloudfrontへアクセスするようのロールを作成して付与したことになります)

関数のコード

'use strict';
exports.handler = (event, context, callback) => {
    //
    // Extract the request from the CloudFront event that is sent to Lambda@Edge
    var request = event.Records[0].cf.request;
    //
    // Extract the URI from the request
    var olduri = request.uri;
    //
    // Match any '/' that occurs at the end of a URI. Replace it with a default index
    var newuri = olduri.replace(/\/$/, '\/index.html');
    //
    // Log the URI as received by CloudFront and the new URI to be used to fetch from origin
    console.log("Old URI: " + olduri);
    console.log("New URI: " + newuri);
    //
    // Replace the received URI with the URI that includes the index page
    request.uri = newuri;
    //
    // Return to CloudFront
    return callback(null, request);
    //
};

こちらをコピーして貼り付けます。

トリガーの設定

  1. 画面上部、アクションのドロップダウン
  2. 新しいバージョンを発行
  3. 表示されたダイアログにバージョンを記入(未記入でもOK)
  4. 左のトリガーからcloudfrontを選択
  5. 表示されるトリガーの設定にで、cloudfrontのdistribution IDを入れ、cloudfrontのイベントをオリジンリクエスト とします。

これで設定は完了になります。

この後Cloudfrontの指定したdistributionのstatusが In Progressとなります。

まとめ

意外と簡単に設定でき、問題を解決できたかと思います。
同じ悩みの方は是非お試しください。間違っている部分等ありましたらご指摘ください。