簡単だった!CloudFront + S3 に BASIC認証を入れる方法

「AWSのS3にhtmlをアップしたけど、これにBASIC認証付けられないかな?」

こんにちは、タカフです。

CloudFront + S3、いいですよね。

ただのhtml + css + js だけなら、CloudFront + S3 + ACM(SSL) の組み合わせで、httpsのセキュアなサイトが最安で最強なサーバーを構築することが出来ます。

さて、そんなS3によるWebサイトでもBASIC認証をつけたいことがあります。

例えば、サイトのリリース前とか、社内確認用、とかのケースですね。

これ、AWSのLambda@Edgeを使えば、わりと簡単に実現できます。

lambda@edgeとは、簡単に言うとCloudFrontでリクエストを受けたりレスポンスを返したりする時に、独自処理を入れられるサービスの事です。ここにBASIC認証を入れるというわけです。

今日はその方法を記事にしていきます。

事前にCloudFront + S3 でサイト構築準備が整っていることを前提とします。

今回Qiitaの記事を参考にしてますが、lambda@edgeの事は書いてなかったので、インスパイア記事です。

CloudfrontとS3でBasic認証をかける

CloudFrontの事前準備

BASIC認証を導入するということは、HTTPヘッダーとして Authorization を通す必要があるのと、キャッシュ時間を 0 にする必要があります。

他のユーザーのBASIC認証をキャッシュしては意味がないからですね。

CloudFrontのBehaviorを以下のように設定しておきましょう。

Cache Based on Selected Request HeadersWhitelistにして、
Whitelist HeadersにてAuthorizationをAddしておきます。

そしてTTLを全て0にしておきましょう。

lambda関数の作成

リージョン切り替え

lambda@edgeを使うには、リージョンが 米国東部(バージニア北部)のみなのでリージョン移動します。

そしてlambdaのサービスに遷移します。

関数の作成

関数名を任意の名前で「BasicAuth」(任意の名前。BASIC認証であることをわかるようにしておく)

ランタイムを「Node.js 8.10

実行ロールを「AWSポリシーテンプレートから新しいロールを作成

ロール名に「lambda_edge_exection」(任意の名前)

ポリシーテンプレートに「基本的な Lambda@Edgeのアクセス権限(CloudFrontトリガーの場合)

にしておきます

選択したら「関数の作成」を押しましょう。

BASIC認証コードの貼り付け

ここは参照記事の完全にコピペです。

以下のコードをページ中ほどの関数コードのところに貼り付けます。

userとpassのところ、ご自身の任意の値に変えてください!これがBASIC認証のユーザー名とパスワードになります。

index.js
'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);
};

こんな感じになります。

一旦、ここまでで右上の「保存」を押しておきましょう。

Lambda@Edgeへのデプロイ

次にページ上部のアクションの「Lambda@Edgeへのデプロイ」をクリックします。

次の画面で、

ディストリビューションに対象のCloudFrontのディストリビューションを選択

CloudFrontのイベントに「ビューアーリクエスト」を選択

デプロイ時に、この関数の新しいバージョンが〜のチェックボックスをONにします。

CloudFrontイベントが「ビューアリクエスト」を選択するのは、エンドユーザーからリクエストが来たときに、このLambda関数が発動してBASIC認証を動作させたいからです。

AWSのドキュメントページをお借りすると、まさしく以下の画像のところになりますね。

さて、話を戻して「デプロイ」ボタンを押します。

もしここで以下のようなエラーが表示される場合はロールに問題があります。

下のエラーを修正し、もう一度お試しください。

関数の実行ロールは、edgelambda.amazonaws.com サービスプリンシパルによって引き受け可能である必要があります。

このLambda関数に割り当てているロールのページに遷移します。

実は以下のリンクから遷移できます。

対象のロールの「信頼関係」タブにて、「信頼関係の編集」を押下します。

Serviceのところに<strong>lambda.amazonaws.com</strong><strong>edgelambda.amazonaws.com</strong> が配列の文字列で並ぶようにして、「信頼ポリシーの更新」を押下します。

信頼関係の編集
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
            "lambda.amazonaws.com",
            "edgelambda.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

これでデプロイ出来るはずです。

CloudFrontのデプロイ確認後、BASIC認証の確認

対象のCloudFrontのデストリビューションをのぞくと、デプロイ中のステータスになっていて、Behaviorも見てみると、Viewer Request のところに先ほど作成したラムダ関数がセットされているかと思います。

CloudFrontのデプロイが完了して、そのURLにアクセスしてみるとBASIC認証が効いているのがわかります。

(おまけ)サブディレクトリのデフォルトファイルはindex.htmlを返すようにする

CloudFrontへの、/ によるアクセスの場合に、オリジンサーバーに対してどのファイルを取得しにいくかを設定出来ますが、これはドキュメントルートだけの設定なんですね。

CloudFrontへの、/subdir/ などのサブディレクトリへのアクセスの場合、403エラーとなってしまいます。

詳しくは、AWSでおなじみのクラスメソッドさんの記事にあるのですが、これはLambda@Edgeで解決出来る話なので、必要であれば一緒に設定するのもアリです。

方法はクラスメソッドさんの記事を見てもわかるのですが、上記の要領で同じようにLambda@Edgeを作ります。

簡単に言うと、同じロールを使って以下のコードでlambda関数を作って、

index.js
'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);
 
};

CloudFrontのところに「オリジンリクエスト」のところで設定します。つまりCloudFrontからオリジンサーバーに対してのところで処理を挟むわけですね。

まとめ

静的サイトで展開する場合、AWSのCloudFront+S3は最強の組み合わせです。

無料でhttpsにも出来るし、何よりYahoo砲など様々なアクセス砲が来てもサーバーが落ちることはありません。

サーバー負荷は心配しなくていいからコンテンツ制作に力を入れていきましょう!Webサイトはとにかく中身です!

現場からは以上です!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です