S3にログを出力するAWS WAFの構築

ALB/WAF環境がほしかったのでCFnテンプレートを作りました。WAFはS3にロギングします。
2020.06.07

ALBでAWS WAFv2(以下WAF)を利用する環境を構築します。以下のような構成です。

00

S3にWAFのログを出力する際は、Kinesis Data Firehoseが必要になります。ここでは、Kinesis Data Firehoseで配信(S3への出力)エラーが発生した際に、CloudWatch Logsへロギングされる構成にしました。

ALBなどVPCリソースが構築済みであることを前提に、赤枠の箇所を中心に構築(説明)していきたいと思います。

前提

  • VPCリソースが構築済みであること

WAFと周辺リソース構築

赤枠の箇所を構築するCFnテンプレートを作成しました。前提の環境が構築されていれば、利用できるようになっています。

CFnテンプレートを用意しましたので、マネジメントコンソールからの構築については割愛しています。 構築自体は上記CFnテンプレートにてスタックを作成すれば済みますので、ここからは作成されるリソースについて説明していきたいと思います。

S3

WAFログの出力先となるS3バケットです。Kinesis Data Firehoseを作成するリージョンと、同一リージョンに作成する必要があります。ここでは、S3のライフサイクルールにてログの保存期限を設定しました。

Kinesis Data Firehose

配信ストリーム名

WAFのログ出力に利用する際は制約があり、配信ストリーム名はaws-waf-logs-ではじめる必要があります。

圧縮設定

S3保存量、ログの確認(Athena、S3 Select..)を考慮し、コストの抑制のためGZIP圧縮を有効にしました。

エラーログ設定

配信(S3への出力)エラーを考慮して、Kinesis Data FirehoseのエラーログをCloudWatch Logsに出力する設定にしています。配信エラーの詳細については、以下を確認ください。

プレフィックス設定

Athenaで解析がしやすいように、カスタムパーティションを設定しました。

サービスロール

今回の構成では、S3とCloudWatch Logsに対する権限が必要になります。

設定については以上となります。

東京リージョンのKinesis Data Firehoseでは、1,000 レコード/秒、1,000 トランザクション/秒、1 MiB/秒の上限が設定されています。これに抵触する可能性がある場合は、上限緩和申請を実施してください。

CloudWatch Logs

Kinesis Data Firehoseにてエラーログの記録を有効にする場合かつ、マネジメントコンソール以外で配信ストリームを作成する場合は、ロググループ、ログストリームを事前に作成する必要があります。

ここでは、ログデータを90日で失効するようにしました。今回は設定していませんが、該当のロググループにメトリクスフィルターを設定して、エラー発生時にアラートを通知させることも可能です。

WAF

今回はWeb ACLにルールを設定していませんので、実際に利用する際は何らかのルールを追加してください。Kinesis Data Firehoseなど上記に記載したリソース作成後、WAFのコンソールよりロギングを設定してください。(CFnにてWAFのログ設定が見当たりませんでした。)

なお、特定の項目をログから除外することも可能ですので、Cookieなど除外したい項目がある場合は、以下を参考にしてください。

ログに出力される項目など、詳細については以下を確認ください。

確認

今回は、WAFにルールを追加していませんので、WAFを利用しているALBにアクセスし、WAFのログが出力されることを確認したいと思います。

$ curl http://ALBのDNS名
<html><body>Hello world</body></html>

WAFのログが出力されていることを確認します。

$ BUCKET_NAME=バケット名
$ aws s3 ls s3://${BUCKET_NAME} --recursive
2020-06-06 19:09:28       2599 year=2020/month=06/day=06/hour=10/aws-waf-logs-kof-dev-delivery-stream-2-2020-06-06-10-08-25-1e9ba238-37df-4d72-ba98-12ed648b7f4c.gz
2020-06-06 19:10:29       2588 year=2020/month=06/day=06/hour=10/aws-waf-logs-kof-dev-delivery-stream-2-2020-06-06-10-09-26-ea76bd9d-0981-4904-b9d1-1e3670e46346.gz

ログの内容を確認しました。(一部マスクしています。)

$ aws s3 cp s3://${BUCKET_NAME}/year=2020/month=06/day=06/hour=10/aws-waf-logs-kof-dev-delivery-stream-2-2020-06-06-10-08-25-1e9ba238-37df-4d72-ba98-12ed648b7f4c.gz ./log.gz
download: s3://XXXXXXXXXXXX/year=2020/month=06/day=06/hour=10/aws-waf-logs-kof-dev-delivery-stream-2-2020-06-06-10-08-25-1e9ba238-37df-4d72-ba98-12ed648b7f4c.gz to ./log.gz
$ gzcat log.gz
{"timestamp":1591438087848,"formatVersion":1,"webaclId":"arn:aws:wafv2:ap-northeast-1:XXXXXXXXXXXX:regional/webacl/kof-dev-web-acl/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","terminatingRuleId":"Default_Action","terminatingRuleType":"REGULAR","action":"ALLOW","terminatingRuleMatchDetails":[],"httpSourceName":"ALB","httpSourceId":"XXXXXXXXXXXX-app/test-dev-alb/XXXXXXXXXXXXXXXX","ruleGroupList":[],"rateBasedRuleList":[],"nonTerminatingMatchingRules":[],"httpRequest":{"clientIp":"XX.XX.XX.XX","country":"CN","headers":[{"name":"Host","value":"XX.XX.XX.XX"},{"name":"Content-Length","value":"25"},{"name":"Content-Type","value":"application/x-www-form-urlencoded"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0"},{"name":"Cache-Control","value":"no-cache"}],"uri":"/aotu7.php","args":"","httpVersion":"HTTP/1.1","httpMethod":"POST","requestId":null}}
{"timestamp":1591438094790,"formatVersion":1,"webaclId":"arn:aws:wafv2:ap-northeast-1:XXXXXXXXXXXX:regional/webacl/kof-dev-web-acl/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","terminatingRuleId":"Default_Action","terminatingRuleType":"REGULAR","action":"ALLOW","terminatingRuleMatchDetails":[],"httpSourceName":"ALB","httpSourceId":"XXXXXXXXXXXX-app/test-dev-alb/XXXXXXXXXXXXXXXX","ruleGroupList":[],"rateBasedRuleList":[],"nonTerminatingMatchingRules":[],"httpRequest":{"clientIp":"XX.XX.XX.XX","country":"CN","headers":[{"name":"Host","value":"XX.XX.XX.XX"},{"name":"Content-Length","value":"19"},{"name":"Content-Type","value":"application/x-www-form-urlencoded"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0"},{"name":"Cache-Control","value":"no-cache"}],"uri":"/q.php","args":"","httpVersion":"HTTP/1.1","httpMethod":"POST","requestId":null}}
{"timestamp":1591438092515,"formatVersion":1,"webaclId":"arn:aws:wafv2:ap-northeast-1:XXXXXXXXXXXX:regional/webacl/kof-dev-web-acl/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","terminatingRuleId":"Default_Action","terminatingRuleType":"REGULAR","action":"ALLOW","terminatingRuleMatchDetails":[],"httpSourceName":"ALB","httpSourceId":"XXXXXXXXXXXX-app/test-dev-alb/XXXXXXXXXXXXXXXX","ruleGroupList":[],"rateBasedRuleList":[],"nonTerminatingMatchingRules":[],"httpRequest":{"clientIp":"XX.XX.XX.XX","country":"CN","headers":[{"name":"Host","value":"XX.XX.XX.XX"},{"name":"Content-Length","value":"20"},{"name":"Content-Type","value":"application/x-www-form-urlencoded"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0"},{"name":"Cache-Control","value":"no-cache"}],"uri":"/l7.php","args":"","httpVersion":"HTTP/1.1","httpMethod":"POST","requestId":null}}
・
省略
・
・

さいごに

マネジメントコンソールでの構築については割愛していますが、ALB/WAF環境を構築する際の参考になれば幸いです。 S3にログが出力されていれば、AthenaやS3 Selectなどで解析することも可能ですので、以下も参考にしてください。