S3バケットに配置しているオブジェクトファイルに対してBasic認証をかけたい場合があると思います。しかし、S3自体にBasic認証機能は提供されていません。そのため、今まではリバースプロキシとしてEC2を前段に置くことで実現していました。
最近、Lambda@Edgeという機能がGAリリースされました。この機能を使うことで、サーバレスにBasic認証をかけることができるため、その方法について紹介します。
[toc]
Lambda@Edge とは
CloudFrontのイベントに対してLambda関数を実行できる機能です。Lambda@Edgeが実行できるCloudFrontのイベントは、以下の4つがあります。
- CloudFrontがエンドユーザからHTTPリクエストを受信(ビューワーリクエスト)
- CloudFrontが受け取ったHTTPリクエストをオリジンサーバに転送する前(オリジンリクエスト)
- CloudFrontがオリジンサーバからレスポンスを受信(オリジンレスポンス)
- CloudFrontがエンドユーザにレスポンスを返す前(ビューワーレスポンス)
Lambda@Edgeのユースケースとして以下の用途がドキュメントに書かれていました(一部抜粋)。
AWS Lambda@Edge - AWS Lambda
- A/B テスト用に、異なるバージョンのサイトに URL を書き換えるために Cookie を検査します
- リクエストを送信したデバイスに関する情報が含まれている、User-Agent ヘッダーに基づいてユーザーにさまざまなオブジェクトを送信します。たとえば、ユーザーのデバイスに応じて、さまざまな解像度のイメージをユーザーに送信できます。
- ヘッダーまたは認証トークンを検査し、対応するヘッダーを挿入して、リクエストをオリジンに転送する前にアクセス制御を有効にします。
- ヘッダーの追加、削除、変更、および URL パスの書き換えを行い、キャッシュの異なるオブジェクトにユーザーをダイレクトします。
- 未認証ユーザーをログインページにリダイレクトしたり、エッジから直に静的ウェブページを作成して配信したりといったことを行う新しい HTTP レスポンスを生成します。
構成
以下のサービスを組み合わせて実現させます。
- S3
- 取りに行きたいファイルを置く
- CloudFront
- Lambda@Edgeとの連携
- キャッシュ、TTLは無効化する
- Lambda@Edge
- Basic認証を設定(Authorizationヘッダを追加する)
S3
S3バケットの作成を行います。今回はマネジメントコンソールから行いました。 CloudFrontとの連携のためにWebホスティングを有効化します。
IAM
Lambda@Edge用のIAMロールを作成します。 lambda_edge_exection という名前で作成します。
信頼関係(Trust Relationship)は(元々の)LambdaとLambda@Edgeの両方を追加します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com",
"edgelambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
インラインポリシーは以下のようにします。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"lambda:GetFunction",
"lambda:EnableReplication*",
"iam:CreateServiceLinkedRole",
"cloudfront:CreateDistribution",
"cloudfront:UpdateDistribution"
],
"Resource": "*"
}
]
}
Lambda
Lambda@Edgeの関数はバージニアリージョンで行う必要があります。 Lambdaのページに飛んだら右上のリージョン一覧から「米国頭部(バージニア北部)」を選択します。
関数の作成
「関数の作成」->「一から作成」を選択、以下の図のように作成します。 関数名を「S3BasicAuthentication」にし、ロールは先ほど作成したもの(lambda_edge_execution)を選択します。
関数のコードは以下を参考にしました。ランタイムは「Node.js 6.10」にします。
Basic HTTP Authentication for CloudFront with Lambda@Edge - Gist
注意点として、「基本設定」でメモリ(MB)を「128(MB)」、タイムアウトを「1(秒)」にします。
CloudFront
CloudFrontのページから「Distributions」->「Create Distributions」をクリックします。 「Select a delivery method for your content.」で「Web」を選択します。
以下の図のように設定していきます。
「Create Distribution」をクリックすると、作成されます。
エラーページのキャッシュ無効化
先ほど作成したDistributionを選択し、「Error Pages」タブをクリック、「Create Custom Error Response」をクリックします。
- 「HTTP Error Code」はTTLを変更したいHTTPステータスコードを選択
- 「Error Caching Minumum TTL」を1にする
今回は400、403、404で設定してみました。
CloudFrontとLambda@Edgeの連携
Lambdaの画面に戻ります。
Lambda@Edgeでは指定されたバージョンに対して有効にする必要があります($LATEST
は指定できない)。
「アクション」から「新しいバージョンを発行」をクリックします。
「バージョンの説明」には任意の説明(空でも良い)を入力し、「発行」をクリックします。
作成したバージョンにおいて、「トリガー」タブをクリックし、「トリガーを追加」をクリックします。
トリガーとしてCloudFrontを選択し、先ほど作成したDistribution IDを指定します。 CloudFrontイベントを「ビューアーリクエスト(Viewer Request)」を選択し、「トリガーとレプリケートの有効化」をチェックします。
送信ボタンを押すと、CloudFrontの設定変更も行われます。Statusが「Deployed」になれば完了です。
確認
CloudFrontのURLをブラウザからアクセスするとBasic認証ダイアログが表示されるはずです。
ログ
Lambda関数が実行したログはCloudWach Logsに格納されます。
ただ、他のLambda関数と異なる点として、「関数が実行される場所に最も近いCloudWatch Logsリージョン」でCloudWatch Logs ログストリームを作成します。各ログストリームの名前の形式は、 /aws/lambda/us-east-1.function-name
です」 (参考: Lambda 関数の CloudWatch メトリクスと CloudWatch Logs - Amazon CloudFront)
日本国内からアクセスすると、東京リージョンのCloudWatch Logsで以下の図のようにログストリームが作成されます。
最後に
Lambda@Edgeを活用することでS3バケットのファイルにBasic認証をかけることができました。今回はCloudFrontのデフォルトURL(https://xxxxxxx.cloudfront.net)でしたが、独自ドメインで設定することも可能です。また、ユーザのIDとパスワードをLambda関数内に直接書いてしまっているので、KMSで暗号化したり、DynamoDBなどで管理することでより実践的な仕組みを構築することができます。 Lambdaを使いこなしてサーバレスなAWSライフを送りましょう。