どうも、Tです。
前回の記事で、AWSのS3+CloudFrontを使って静的Webサイトを公開することができました。
正式な公開時期までWebを公開しない方法を探したところ、AWSサービスではIPアドレスによる制限、もしくはBasic認証(ユーザー・パスワードによる認証)ができるようです。
今回は、よく使われるであろうBasic認証を試してみました。なお、Basic認証は、CloudFrontだけではできないので、Lambada@Edgeを使っての実装になります。
目次
やりたいこと
やることは非常にシンプルです。アクセスしたユーザーのWebブラウザにユーザー名とパスワード認証のダイアログボックスを表示させて認証できたら、Webサイトが表示させたいです。
使うサービス
Lambda@Edgeとは
Lambda@Edgeという名前のサービスがあるわけでなく、サービスとしてはLambdaです。
LambdaとCloudFrontエッジのイベントによってトリガーして使う形態をLambda@Edgeの位置づけになるようです。
Node.js および Python の Lambda 関数を実行して CloudFront が発信するコンテンツをカスタマイズし、ビューワーに近い AWS 地域でこの関数を実行できます。この関数は、プロビジョニングや管理の必要なく、CloudFront イベントに応答を実行します。Lambda 関数を使用して、次の時点で CloudFront リクエストとレスポンスを変更できます。
Lambda@Edgeのできること
Lambda@Edgeは、CloudFrontと連携して動くLambdaです。そのタイミング(トリガー)として下記があります。
下記にわかりやすいく書いてくれてました。
Viewer Request
クライアントがCloudFrontにHTTP(S)でアクセスし、そのリクエストがエッジロケーションに到達した時に発生するイベントViewer Response
リクエストをしたクライアントへエッジロケーションからレスポンスを返す際に発生するイベントOrigin Request
エッジロケーションにキャッシュが残っていない状態で、クライアントからのリクエストがオリジンに送信される際に発生するイベントOrigin Response
オリジンからエッジロケーションにレスポンスがある場合に発生するイベント
Basic認証の際は、クライアント(Webブラウザ)がアクセスしてエッジロケーション(CloudFront)に届いたときなので、Viewer Requestを利用します。
Lambda@Edgの制限
Lambda@Edgeを利用する上ので、制限事項が色々ありました。
数年前の記事など見比べると、内容は多少変わっているようですが今回の構成でハマった部分を書き出しておきます。(2020年7月現在)
トリガーを追加できるのは番号付きバージョンのみです。$LATEST やエイリアスには追加できません。
Lambdaで関数を作成したときに、バージョンを付けないとエラーができました・・・。
トリガーを追加できるのは、米国東部(バージニア北部) リージョンの関数のみです。
Lambdaのリージョンをバージニア北部にする必要があります。
トリガーを追加するには、Lambda 関数に関連付けられている IAM 実行ロールをサービスプリンシパルの lambda.amazonaws.com および edgelambda.amazonaws.com が引き受けられる必要があります。
ロール作成時に手動でプリシンパルを追加する必要がありました。
他にもLambda@Edgeを使う場合に注意が色々書かれているので目を通しておくことをお勧めします。
Lambda@Edgeの料金
サービスとしてはLambdaになるので、領域んはLambdaの料金になります。
Lambdaは、無料枠として1 か月ごとに 100 万件の無料リクエスト、および 40 万 GB-秒のコンピューティング時間があります。
これは1年間の無料枠ではなく無期限の無料枠になります。
超過分は、下記になります。
事前準備
S3+CloudFrontの静的Webサイトホスティングは行ている前提で記載しています。
設定方法
設定の流れ
以下の流れで進めていきます。
- Lambda@Edge用のロール作成
- Lambadaの設定
- CloudFrontにLambda関数の関連付け
Lambda@Edge用のロール作成
Lambdaを実行するためのロールを作成します。
サービスからIAMをクリックします。
「アクセス管理」->「ロール」->「ロールの作成」をクリックします。
AWSサービスから「Lambda」をクリックします。
「次のステック:アクセス権限」をクリックします。
ポリシーのフィルタにAWSLambdaを入力して、「AWSLambdaBasicExecutionRole」にチェックを入れて、「次のステップ:タグ」をクリックします。
「次のステップ:確認」をクリックします。
「ロール名」に任意の名前を付けて「ロールの作成」をクリックします。
Lambda実行用のロールが作成されました。作成したロールをクリックします。
Lambda@Edgeを利用するためのサービスプリシンパルの追加します。
「信頼関係」タブの「信頼関係の編集」をクリックします。
下は編集前の状態です。Serviceエレメントを編集していきます。
下は、編集後の状態です。「edgelambda.amazonaws.com」を追加しています。
Serviceエレメントの中に[]をつけ足しているので注意してください。
編集後、「信頼ポリシーの更新」をクリックします。
信頼されたエンティティに「edgelambda.amazonaws.com」が追加されていることを確認します。
Lambda関数の準備
Lambdaで実行するBasic認証を行うための関数を準備します。
色々みなさん作られているので、今回は下記を利用させていただきました。
アクセスして「Donwload ZIP」をクリックしてダウンロードします。
ZIPファイルを展開して「lambda-basic-auth.js」ファイルがあることを確認します。
Lambdaの設定
Lambdaの関数を作成していきます。
Lambdaサービスを開いて、リージョンをバージニア北部にします。
「一から作成」から基本的な情報を設定し、右下の「関数の作成」をクリックします。
関数名:任意の関数名を設定
ランタイム:Node.js12.x
実行ロール:既存のロールを使用する
既存のロール:Role_Lambda@Edge(作成したロールを選択)
関数が作成されました。スクロールして画面を下に移動していきます。
関数コードのindex.jsがデフォルトで作成されています。中身を全部消します。
ダウンロードした「lambda-basic-auth.js」をテキストエディタで開きすべてコピーして、index.jsの中にペーストします。authUserの「user」とauthPassの「pass」をBasic認証するユーザー名とパスワードになるので修正して、「Save」をクリックします。
さらにスクロールすると基本設定があります。index.jsを名前を変えている場合、ハンドラの設定を<変更した名前>.handlerに変更してください。
「保存」をクリックします。
Lambda関数をCloudFront関連付け
Lambdaから紐づけ操作
引き続きLambdaの画面から設定を続けます。作ったLambda関数をCloudFrontにデプロイしていきます。
アクションから「Lambda@Edgeへのデプロイ」をクリックします。
下記の設定を行って、「デプロイ」をクリックします。
ディストリビューション:デプロイするCloudFrontのディストリビューション
CloudFrontイベント:ビューアーリクエスト
Lambda@Edgeへのデプロイを確認:チェックする
デプロイされるとデザイナー画面で作成したLambda関数にCloudFrontが紐づきました。
また、関数に:1が付与されました。Lambda@Edge利用時は、番号付きバージョンが必要という要件がありましたが、自動で番号を付けてくれています。
右上のARNは、この後の確認に使うためコピーしておきます。
Lambda関数のバージョン管理は、バージョンのリストから参照することができます。
CloudFrontで紐づけの確認
CloudFron側からLambda@Edgeの設定を確認しておきます。
CloudFrontサービスを開き、設定したディストリビューションをクリックします。
「Behaiviors」タブを開き、対象のOriginにチェックを付けて「Edit」をクリックします。
画面したのLambda Function AssociationsのViwer Requestで先ほどLambdaで確認したARNがLambda Function ARNに設定されていることが確認できます。
ここの設定は手動でもできますが、LambdaでバージョンのついていないARNを設定すると下記のようなエラーが発生します。
com.amazonaws.services.cloudfront.model.InvalidLambdaFunctionAssociationException: The function ARN must reference a specific function version. (The ARN must end with the version number.) ARN: arn:aws:lambda:us-east-1:xxxxxxxxxxxxxxxxxxxxxxxxxxxx:function:lambda_CloudFront_Basic-auth (Service: AmazonCloudFront; Status Code: 400; Error Code: InvalidLambdaFunctionAssociation; Request ID: xxxxxxxxxxxxxxxxxxxxxxxxxxxx; Proxy: null)
動作確認
設定が完了したので、CroudFrontで設定しているURLにアクセスすると下記のようにBasic認証のダイアログボックスが表示され、設定したユーザー名・パスワードを入力するとWebサイトが表示されます。
ちょっとはまったところ
Lambda@Edgeの関数が削除できない
作成したLambda関数は、CroudFrontから設定を消すと削除できるはずですが、下記のエラーが発生しました。
これは、Lambda@Edgeの仕様のようで、CloudFrontから設定を外した後も数時間は削除できないようです。CloudFrontから紐づけ設定を削除後に、1時間ほど放置した後にLambda関数を手動で削除することができました。
すべての CloudFront ディストリビューションから関数の最後の関連付けを削除した後。複数のディストリビューションで関数が使用されている場合、最後のディストリビューションから関数の関連付けを削除した後にのみ、レプリカが削除されます。
関数が関連付けられた最後のディストリビューションを削除した後。
レプリカは通常、数時間以内に削除されます。Lambda@Edge 関数のレプリカを手動で削除することはできません。これにより、まだ使用中のレプリカが削除され、エラーが発生する状況を防ぐことができます。
Lambada@Edgeを適用した直後のWebページ参照でエラーがでる
CloudFrontと紐づけた後に、すぐにアクセスすると50xのエラーで接続できませんでした。
※細かいステータス番号取得し忘れた・・・・。
テスト時に1回だけ発生したのですが、Lambda@Edge関数のレプリカ配置に時間がかかっているかと思うので、CloudFront紐づけ後、数分程度経過後に再度試すと正常に接続されてBasic認証のダイアログボックスが表示されました。
参考
まとめ
やっぱりAWSのサービスは、最初の理解までに時間を要しますなぁ・・・・。