BLOG

2018/11/02

Lambda@Edgeを使ってPOSTリクエストボディを取得してみる

【概要】
AWS WAFには、サンプルログ・フルログというログ機能がありますが、どちらの機能もPOSTリクエストボディは出力されません。
そこで、今回はLambda@Edgeを使用して、CloudFront経由のPOSTリクエストボディを取得する方法を紹介します。

以下のViewer requestのタイミングでLambda@Edgeを実行させて、POSTリクエストボディを取得します。
Vierwer requestのタイミングで実行させる理由は、誤検知調査において、取得したPOSTリクエストボディとフルログを紐付けしながら調査することになるのですが、その紐付けをするための共通キーとして”requestId”が含まれるためです。

【CloudFront】
CloudFront とはグローバルに広がるコンテンツ配信ネットワークです。世界各地にあるサーバにキャッシュを保存することで、様々な国からのアクセスにおいても遅延を少なくすることができます。

【Lambda】
AWS Lambda (以降、Lambda) は サーバーの管理をすることなく、コードを実行することができるサービスです。イメージとしてはPaaSに近いです。インフラ環境の構築などは不要でコードを作成し、処理を実行する条件を決めるだけで条件に合致するイベントが発生した際に処理が実行されるようなサービスです。

例) S3に新しく画像ファイルが配置されたら、サムネイル用の画像を作成する etc…

他にもトリガーはCloudWatchを用いて定期的な処理、いわゆるバッチの様な処理を行う様に設定することもできます。

【Lambda@Edge】
Lambda@Edge はCloudFrontでのリクエスト、またはCloudFrontからのレスポンスをトリガーにしてLambdaの関数を実行させることができる機能です。
具体的には以下の4種類になります。
・ViewerRequest
・OriginRequest
・OriginResponse
・ViewerResponse

【ご利用頂く前に】

・AWS WAFによってBLOCKされたPOSTリクエストボディは取得できません。
・本番環境においては恒久的な設定ではなく、一時的な設定として利用します。
・初めの導入は本番環境ではなく、検証環境で問題ないことを確認した後、導入してください。
-> 各リクエストに対してLambda@Edgeを実行するため、Webサービスのリクエスト数なとによって遅延が発生する可能性があります。

https://aws.amazon.com/jp/blogs/news/lambdaedge-design-best-practices/

【作業の流れ】
1. CloudFrontの設定確認
2. Kinesis Data Firehoseの作成
3. Lambda@Edgeを実行するためのロール作成
4. Lambda@Edgeの設定
5. Lambda@Edge稼動確認
6. S3にPOSTデータを含むログが出力されていることを確認
7. 機能の停止方法

【1. CloudFrontの設定確認】
まず、CloudFrontの設定でPOSTリクエストを受け付ける設定をします。

対象の CloudFront にチェックを入れて、[Distribution Settings] をクリックします。
[Behaviors] をクリックします。

対象の ”Behaviors” にチェックを入れて、[Edit] をクリックします。

“Allowed HTTP Methods” が一番下の “POST” を含むになっていることを確認します。

【2. Kinesis Data Firehoseの作成】
続いて Kinesis Data Firehose を作成します。

[配信ストリームを作成する] をクリックします。

“Delivery stream name” にお好きな名前を入力して、[Next] をクリックします。
※0 あとでこの名前を使用します

次のページは何も設定せず、デフォルトのまま [Next] をクリックします。
※デフォルト設定
“Record transformation Disabled” : Disabled
“Record format conversion” : Disabled


“Destination” を “Amazon S3” に設定し、”S3 bucket” と “Prefix” を指定して [Next] をクリックします。
例) S3://csc-waftest/postdata/<ログ出力先>

“Buffer interval” と “S3 compression” は お好きな設定で構いません。例としては “Buffer interval” は 60 、”S3 compression” は “GZIP” を設定しています。
続いて ”IAM role” の “Create new or choose” をクリックします。

既存のロールを使用、もしくは新しくロールを作成します。
ロール名は自動で ”firehose_delivery_role” と記入されますが、お好きな名前で構いません。
入力が完了しましたら、”許可” をクリックします。

設定したロールが正しく反映されていることを確認し、[Next]をクリックします。

内容を確認し、問題なければ[Create delivery stream]をクリックします。

作成が完了すると,”Successfully Created delivery stream XXXX(※0
で作成したKinesis Data Firehose名)” と表示されます。

【3. Lambda@Edgeを実行するためのロール作成】
IAMのロールを作成します。

[ロール]をクリックします。

[ロールの作成]をクリックします。

“Lambda” を選択し、[次のステップへ:アクセス権限]をクリックします。

ポリシーのフィルタにキーワードを入力して以下のポリシーを選択します。

・AmazonKinesisFirehoseFullAccess
・AWSLambdaExecute
・CloudFrontFullAccess

選択が完了しましたら、[次のステップ:確認]をクリックします。

“ロール名” / ”ロールの説明”にはお好きな名前 / 説明を入力します。
※1 あとでこのロールを使用します。
前述の3つのポリシーが追加されていることを確認し、問題なければ[ロールの作成]をクリックします。

作成したロールの概要をから”信頼関係”のタブをクリックします。

[信頼関係の編集]をクリックします。

“Service” の部分を以下のように編集し、[信頼ポリシーの更新]をクリックします。

(編集前)

(編集後)

(編集前)
“Service”: “lambda.amazonaws.com”

 ↓

(編集後)
“Service”: [
“edgelambda.amazonaws.com”,
“lambda.amazonaws.com”
]

“信頼されたエンティティ”に追加されていることを確認します。

【4. Lambda@Edgeの設定】
Lambdaのサービスページに移動します。

リージョンを “バージニア北部リージョン” にした状態で[関数の作成]をクリックし
ます。

以下の内容で関数を作成します。

名前 : お好きな名前で構いません。※2 あとでこの名前を使用します
ランタイム : Node.js 8.10
ロール : 既存のロール
既存のロール : ※1で作成したロールを設定します。

“関数コード” には弊社が用意しているコードを使用します。
※”作成したKinesis Data Firehose(Delivery Stream Dame)名”には、今回の手順だと”postdata”という名前を入力しますが、適宜Kinesis Data Firehose名に合わせて変更してください。

'use strict';

var AWS = require('aws-sdk');
var kinesis = new AWS.Firehose({region: 'ap-northeast-1'});
var StreamName = '作成したKinesis Data Firehose(Delivery Stream Dame)名';

exports.handler = (event, context, callback) => {

    // Get request and request headers
    const request = event.Records[0].cf.request;
    const method = request.method;
    
    if (method != 'POST') {
        // passing
        callback(null, request);
        return;
    }
    
    let params = {
        DeliveryStreamName: StreamName
        , Record: {
            Data: JSON.stringify(event.Records[0].cf)
        }
    };
    
    kinesis.putRecord(params, (err, data) => {
        if (err) {
            console.log(err);
            return;
        }
        // passing
        callback(null, request);
    });
};

“実行ロール” の設定をします。”既存のロール”を選択して、※1で作成したロールを設定し、[保存]をクリックします。

続いて ”アクション” から[新しいバージョンを発行]をクリックします。※3 あとでこのバージョンを使用します。

“バージョンの説明”はお好きな内容で構いません。入力が完了しましたら[発行]をクリックします。

バージョンが発行されましたら、トリガーを設定します。
“トリガーの追加”から”CloudFront”を追加します。

“ディストリビューション” にて該当のCloudFrontのディストリビューションを選択します。
“CloudFrontイベント” はビューアーリクエストにして、”ボディを含める” にチェックを入れます。
“Lambda@Edgeへのデプロイを確認”にチェックを入れて、[追加]をクリックします。

[保存]をクリックします。

CloudFrontのDistribution一覧画面にて、”Deployed”になるまで待ちましょう。


(Deployed)

【5. Lambda@Edge稼働確認】
Lambda@Edgeのログは、CloudFrontのエッジサーバが稼働している
リージョンのCloudWatchに出力されます。
日本国内からのアクセス結果は東京リージョンにて確認することができます。
/aws/lambda/us-east-1.[Lambda@Edge関数名] ※2で設定した名前になります。

該当バージョンのログが出力されていることを確認できましたら、Lambda@Edgeの稼働確認は完了です。
※3 で設定したバージョンになります。

【6. S3にPOSTデータを含むログが出力されていることを確認】
Kinesis Data Firehoseで指定した出力先にログが出力されているか確認します。
存在が確認できましたら、ファイルをダウンロードして、ログの内容を確認することができます。

【7. 機能の停止方法】
機能を止めるには2通りの方法があります。
1. 一時的に機能を止める場合
2. 機能自体を削除する場合

1.の場合
CloudFrontのサービスページにてBehaviorを編集します。該当のCloudFrontを選択し、[Distribution Settings]をクリックします。

[Behaviors] をクリックします。

対象の ”Behaviors” にチェックを入れて、[Edit] をクリックします。

Behaviorページの最下部にある”CloudFront Event”を削除し、[Yes, Edit]をクリックします。

”Deployed”になるまで待ちましょう。
これで一時的に機能を止めることができます。

(Deployed)

2.の場合
1.の手順を全て行い、”Deployed”になった翌日にLambda@Edgeの設定を削除することで機能の削除が完了します。

【まとめ】
今回はLambda@Edgeを使用して、CloudFront経由のPOSTリクエストボディを取得する方法を紹介しました。全てのリクエストにおいてLambda@Edgeを実行する設定になっているので、Webサービスのレスポンス遅延に繋がる可能性がございます。そのため、検証環境のみの実施を推奨しております。本番環境での実施はWebサービスへの影響が発生する恐れがございますので非推奨です。