2019. 12. 9
Lambda@Edge で CloudFront へのアクセス数を制限してみる
この記事は Fusic その 2 Advent Calendar 2019 9 日目の記事です。
はじめに
案件で、CloudFront をキャッシュサーバーとして使った API を公開するにあたり、アクセス制限を設けることができないか打診がありました。
Lambda@Edge を使えばそれが可能にではないかと言う話になり、Lambda@Edge を用いたアクセス制限について PoC を作成したので、手順を備忘録がてらに書いておきたいと思います。
手順
0. (お試しなので)Web ホスティング S3 を作る
CloudFront を作るにしても、まずはオリジンが必要になってくるので、テスト用にS3を使ってWEBサイトを作ります。
バケットを作成
適当な名前をつけたバケットを作成します。
静的WEBホスティングの設定
WEBホスティングの設定を有効化します。
おそらくパブリックアクセス権限が必要になるので、作成する方のポリシーにしたがって調整してください。
これでオリジンができたので、適当なHTMLを置いて動作確認をします。
はい、表示されましたね。
1. CloudFront を作る
メインディッシュを作っていきます。
とは言っても、CloudFront は特別な設定をしません。
先ほど作成した S3 をオリジンに設定するだけです。
キャッシュ等はポリシーにしたがって設定してください。
2. Lambda 関数を作る
次は、Lambda関数を作っていきます。 Lambda@Edgeとして使う関数はバージニア北部に作成する必要があるので、コンソールを切り替えて作業をします。
また、 Lambda@Edge として設定できる関数は Node.js
か Python
のみなので注意しましょう。今回は Python3.7 で作成します。
コードは下記のコードを使用します。
テストなので、アカウントIDは決め打ちです。
import base64
import boto3
import json
from pprint import pprint
dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
def lambda_handler(event, context):
accountId = '000000001'
# DynamoDB 基本設定
tableName = 'lambda-egde-test-12345678'
dynamoTable = dynamodb.Table(tableName)
primary_key = {
'account_id': accountId
}
# カウント取得
dynamoGetResponse = dynamoTable.get_item(Key=primary_key)
# アカウントが登録されていない場合は 404
if 'Item' not in dynamoGetResponse:
body = 'Not Found'
response = {
'status': '404',
'statusDescription': 'Not Found',
'body': body,
}
return response
# カウントが上限を超えていたら 403
if dynamoGetResponse['Item']['request_count'] >= dynamoGetResponse['Item']['request_limit']:
body = 'Forbidden'
response = {
'status': '403',
'statusDescription': 'Forbidden',
'body': body,
}
return response
# カウントアップ
dynamoPutResponse = dynamoTable.update_item(
Key=primary_key,
UpdateExpression=\"set request_count = :c\",
ExpressionAttributeValues={
':c': dynamoGetResponse['Item']['request_count'] + 1
},
ReturnValues=\"UPDATED_NEW\"
)
return request
アカウント管理にDynamoDBを使うので、Lambdaの実行ロールにDynamoDBへのアクセス権限を付与します。
今回はお試しなので、DynamoDBのフルアクセス権限をつけちゃいます。良い子は真似しないでね。
3. DynamoDB テーブルを作る
アカウント管理用のDynamoDBテーブルを作成します。
こちらも特別なことはしないので、細かい説明は割愛します。
テーブルを作成したら、Lambdaに決め打ちで書いているアカウントの情報を追加します。
今回は10回アクセスしたら制限に引っかかるようにします。
4. Lambda と CloudFront を紐付ける
Lambda の設定画面から、トリガーを追加
をクリックして、CloudFront トリガーを追加します。
ディストリビューションは先ほど作成したCloudFront、CloudFront イベントは「ビューワーリクエスト」を選択します。
「デプロイ」をクリックしたら、CloudFront に反映されるのを待ちましょう(反映に結構時間がかかります)。
結果
最初のうちは問題なくS3に設置したHTMLを表示することができますが、
何度かリロードをすると、無事に 403 になりました(文字が小さくて読みにくいですが「Forbidden」と書かれています。
無事にCloudFrontに対してアクセス制限をかけることができましたね。
おわりに
今回構築した仕組みでは、固定のアカウントしか制御できませんが、カスタムヘッダなどを用いてアカウントを識別してやれば、問題なくCloudFrontにアクセス制限をかけることができそうですね。
本番で運用する際には、Lambda のスロットリングや、DynamoDB のキャパシティ等も気にかけないといけなさそうです。