前回に引き続き、AWS初心者向けハンズオンから
Amazon CloudFrontおよびAWS WAFを用いて エッジサービスの活用方法を学ぼうを実際に構築してみましたので、内容をかいつまんでお伝えします。
アーキテクチャ図
ハンズオンで利用するアーキテクチャは次の通りです。
ユーザはWAFで通信を許可するか否かの判定を行い、許可する場合はURLのパスを基にCloudFrontにて接続先の振り分けを行います。静的コンテンツであればS3に、動的コンテンツであればAPI Gatewayに遷移させます。
利用しているAWSサービス
AWS WAF
境界型のウェブアプリケーションファイアウォールです。
CloudFront、Application Load Balancer、AppSync、API Gatewayなどに紐づけることができ、通信内容を検査して不正なアクセスを遮断することができます。
Amazon CloudFront
CDS(Contents Delivery Service)を提供するサービスで、エッジロケーションで動作します。
ユーザからのリクエストを受け付け、リクエストパスによって接続先のサーバ(オリジンサーバ)への向き先を設定することができます。また、コンテンツの内容をキャッシュするか否かもパスによって指定可能です。
設定項目は大きく以下の3つに分けられる。
- Distribution 1つのWebアプリケーションを表す。BehaviorとOriginの設定をまとめたもの
- Behavior パスの設定
- Origin バックエンドとなるオリジンサーバの設定
なお、CloudFrontの設定画面ではリージョンが グローバル となっており変更ができません。
このようなサービスは、設定する中でリージョンを指定するか、あらかじめ使用するリージョンが固定で定められているということを意味します。
Amazon S3
静的コンテンツを格納します。
Amazon API Gateway
動的コンテンツ用のオリジンサーバとして作成します。内部でLambda関数を呼び出します。
AWS Lambda
リクエストのクエリストリングで受け取った日本語を英語に訳して返却する処理を実装します。
構築(ポイントとなる箇所のみ)
S3
ユーザからのリクエストに従って返却される静的コンテンツをバケットに格納する。
ここでは20241231-handson-edgeという名前でバケットを作成しました。
CloudFrontからバケットへの読み取りアクセスを許可するため、バケットのアクセス許可設定にてバケットポリシーを以下の通り設定します。
{
"Version": "2008-10-17",
"Id": "PolicyForCloudFrontPrivateContent",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipal",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::20241231-handson-edge/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::[アカウントID]:distribution/E1P98BAVG7N8WE"
}
}
}
]
}
CloudFront
静的コンテンツ用の設定
Origin :上で作成したS3のバケットを設定します。バケットのリージョンをバージニア北部以外で作成した場合はした画像のように明示的にオリジンの指定先にリージョン名(ここでは東京リージョンを指定)を含める必要があるので注意
Behavior:パスパターンをデフォルト(*) とし、キャッシュポリシーをCachingDisabled(キャッシュしない)に設定します。
Distribution:ウェブページにアクセスする際のURLにてindex.htmlという入力を省略可能とするため、Default Root Objectにindex.htmlと入力します。
動的コンテンツ用の設定
デフォルトの設定ではCloudFrontはユーザリクエストに設定されたヘッダー、クッキー、クエリストリングをバックエンド側には送らないようになっています。
今回はinput_textというクエリストリングを設定し、それをlambdaで処理したいので、オリジンリクエストポリシーを新規作成します。
Origin:作成したAPI Gatewayを設定します。
Behavior:パスパターンをapiとし、オリジンリクエストポリシーで上で作成したオリジンリクエストポリシーを設定します。
Lambda
API Gatewayから呼び出される処理を設定します。
import boto3
def lambda_handler(event, context):
translate = boto3.client(service_name='translate', use_ssl=True)
result = translate.translate_text(Text=event['queryStringParameters']['input_text'], SourceLanguageCode="ja", TargetLanguageCode="en").get('TranslatedText')
return {
'statusCode': 200,
'body': result
}
処理の中でAmazon Translateを利用したいので、Lambdaのロールに対してTranslateReadOnly権限を追加します。
API Gateway
API GatewayではリソースとしてHTTPのGETメソッドを受け付けて上記のLambda関数を呼び出すように設定します。その際、メソッドリクエストに含まれるinput_textを渡すLambdaに渡すように設定します。
WAF
WAFではサービスとの紐づけ、ルールの設定の2つの設定を行います。
紐づけるサービスは上で作成したCloudFrontを設定します。
ルールにはOriginates from a country in Japanの場合にBlockを設定することで日本からのアクセスをWAFでブロックし、CloudFrontへのアクセスを遮断します。
なお、WAFではAWSや3rdPartyが作成したマネージドルールを設定することも可能で、OWASP Top 10をメインに対応を実施するようなルールもあります。
動作確認
静的コンテンツ
CloudFrontの設定画面にディストリビューションドメイン名が表示されているので、そちらにアクセスするとデフォルトで設定したindex.htmlが表示されます。
こちらはオリジンをS3に設定し、Behaviorでキャッシュしないように設定しましたので、下の画像の赤枠の通り
x-cache:Miss from cloudfront
となっており、キャッシュがされていないことがわかります。
ここでBehaviorとしてパスパターンをstatic/* とし、キャッシュするように自作したキャッシュポリシーを設定してからstatic配下の画像ファイルのヘッダを確認すると、キャッシュの結果は
x-cache:Hit from cloudfront
となり、今度はキャッシュされるようになったことがわかります。
動的コンテンツ
まずWAFでアクセスを遮断する前(リクエストをCloudFrontが直で受ける)の状態で挙動を確認します。
Input Messageで入力した内容がinput_textに格納されてAPI Gatewayに渡され、Lambdaで翻訳された結果がOutput Messageに表示されることが確認できました。
WAFで日本からのリクエストを遮断する設定を行った後は画像の通り403 ERRORとなります。
感想
今回はWAFを使ったルールベースでのファイヤウォールの設定、および、CloudFrontでのパスの振り分けやキャッシュの設定などにフォーカスしました。
WAFでは様々なルールが事前提供されており、セキュリティもマネージドにすることができる点は利用する側にとって(特にセキュリティ面にあまり知見がない場合には)有用であると感じました。
今後は他にどのような種類のルールが利用できるかも調べていきたいと思います。
それではまた。