Lambda@Edge で CloudFront に Basic 認証をかける

Lambda@Edge とは

CloudFront へのリクエスト時に Lambda 関数を実行して何かやるすごい仕組み。

  • A/B テスト
  • 認証、アクセス元IP制限
  • レスポンスヘッダ追加・書き換え (リダイレクトなど)
  • むしろ Lambda 関数で動的にレスポンスボディも作っちゃう
  • オリジンの動的切り替え
  • その他いろいろ

@Edge と謳っている通り、世界中の CloudFront エッジロケーションへレプリケーションされた Lambda 関数が、各ロケーション内で実行される。 もう、やりたい放題ですね。

実行できるタイミング

4 つのイベント時に関数を仕込める。

・ CloudFront がビューワーからリクエストを受信した後 (ビューワーリクエスト)
・ CloudFront がリクエストをオリジンサーバーに転送する前 (オリジンリクエスト)
・ CloudFront がオリジンからレスポンスを受信した後 (オリジンレスポンス)
・ CloudFront がビューワーにレスポンスを転送する前 (ビューワーレスポンス)
Lambda@Edge – AWS Lambda

制限

HTTP リクエスト時に同期的に Lambda 関数を実行するので、言うまでもなく、数分間処理をぶん回すなんてことはできない。 その他にもいろいろ制限があるので、要チェック。

インパクト大きいのは主に下記かと。

  • 環境変数: 使えません
  • ランタイム: Node.js 6.10 or 8.10 のみ
  • メモリ: 128 MB
  • 関数タイムアウト: 30 秒 (オリジンリクエスト、レスポンス) / 5 秒 (ビューワーリクエスト、レスポンス)

その他にもいろいろ制限がある。

Basic 認証をかけてみた

前提

CloudFront のディストリビューションはすでに設定済みであること。

CloudFront から Lambda 関数を実行するための IAM ロール設定

参考: サービスプリンシパルの関数実行ロール

他の AWS サービスにアクセスしない単純な関数の場合、Lambda と CloudFront のサービスが AWSLambdaBasicExecutionRole を引き受けられるように設定すれば良い。

マネジメントコンソールの IAM のロール作成画面より、下記の流れで作成。

AWSLambdaBasicExecutionRole ポリシーをアタッチ。

ロール名、説明は適宜設定し、一旦追加する。

追加したロールの信頼関係の編集画面にて、edgelambda.amazonaws.com を追加。

Lambda 関数の準備

関数作成

マネジメントコンソールの Lambda 関数作成画面から作成。
バージニア北部リージョン (us-east-1) に作成しないと駄目なので注意。

  • 名前: 適当に
  • ランタイム: 先に述べたとおり、Node.js 6.10 or 8.10 を選択
  • ロール: 先程追加したロールを選択

コードについて

CloudFront にはサクッと認証かける術がないので、Basic 認証をかける方法は需要がある。 そのため、ググればいくらでも先例が出てくるので、下記のコードを拝借して試してみた。

出典: Serverless: password protecting a static website in an AWS S3 bucket

認証情報がコード内にべた書きなので、AWS Systems Manager パラメータストア などに移したいところだが、後述の通り、ビューワーリクエストの関数タイムアウトが 5 秒しか無いのにネットワークアクセスが発生するのは少々悩ましいところ。

細かい設定

制限に沿うように設定。

  • タイムアウト: 5 秒
  • メモリ: 128 MB

バージョン発行

CloudFront には Lambda 関数の特定バージョンしか指定できないので、新しいバージョンを発行しておく。

CloudFront のビューワーリクエストイベントに Lambda 関数を関連付け

ブラウザからのアクセス時に必ず認証を行うために、ビューワーリクエストイベントをトリガーとして実行するように設定。

CloudFront が受信したディストリビューションのすべてのリクエストに対して関数を実行する場合は、ビューワーリクエストイベントかビューワーレスポンスイベントを使用します。
オリジンリクエストイベントとオリジンレスポンスイベントは、リクエストされたオブジェクトがエッジロケーションにキャッシュされていないためにリクエストがオリジンに転送される場合にのみ発生します。
Lambda 関数をトリガーするために使用する CloudFront イベントを決定する方法

CloudFront のビヘイビア設定編集画面の最下部で、先程の Lambda 関数 (の特定バージョン) ARN を Viewer Request と関連付け。

設定後、Lambda 関数の編集画面にて、CloudFront のトリガーが設定されていることを確認。

動作確認

ここまで設定を行えば、CloudFront の対象 URL へアクセス時に Basic 認証が有効になっているはず!

ちなみに、実行ログは各エッジロケーションがあるリージョンの CloudWatch ログに吐かれる。

東京からアクセスした際の Lambda のログは東京リージョンの CloudWatch ログに吐かれる。

備考

厳密にアクセス制限する必要がある場合、CloudFront を迂回したオリジンへの直アクセスを防がないとザルになるので要注意。