こんにちは、インフラエンジニアの小林です。
前回は、CloudWatch Metric Mathを利用してAWS WAFのマネージドルールのブロック数を可視化する方法を解説いたしました。
今回はそれに引続き、CloudWatch Alarmを使用して、ブロック数が一定数を超えた時にSlackに通知する方法と、それをTerraformで設定するためのサンプルコードをご紹介します。
はじめに
LCLではではAWS WAFのマネージドルールを使用して、悪意のあるリクエストからサービスを保護しています。マネージドルールは簡単に設定でき、非常に強力な保護を提供しますが、ルールの更新や特定のユースケースにおいては誤検知のリスクが伴います。
そのため、CloudWatch Alarmを使用し、一定期間内にブロックされたリクエスト数がしきい値を超えた場合、Slackに通知する仕組みを導入しました。この仕組みによって、誤検知が発生していないか迅速に確認し、必要な対応が取れるようになります。
CloudWatch Alarmとは?
CloudWatchメトリクスに設定したしきい値を超えた時に、SNSなどを使ってアクションを実行できる仕組みです。アクションでは Amazon SNS など様々な処理を実行できます。今回は、AWS WAFのマネージドルールのブロック数が2時間で1000件を超えた場合に、Amazon SNS を使用してSlack通知を送る設定を行います。
以下にTerraformのサンプルコードを示しつつ、詳細を解説します
Terraformのサンプルコード
################################################################################ # アラーム ################################################################################ locals { period = 3600 #1H毎に監視とチェック # マネージドルールを適用したルール名の一覧 rule_names = [ "ip", "common", "known_bad_inputs", "sql_i", "linux", "unix" ] } resource "aws_cloudwatch_metric_alarm" "managed_rule_block_count" { alarm_name = "${local.name_prefix}-managed-rule-block-count" alarm_description = "対応方法: https://example.com/notes/xxxx" # `period` * `evaluation_periods` のあいだに `datapoints_to_alarm` 回だけ `threshold` を、 # `comparison_operator` で指定する比較方法で比較し、TRUE である場合、アラーム状態へ遷移 # evaluation_periods = 2 datapoints_to_alarm = 2 threshold = 1000 comparison_operator = "GreaterThanThreshold" alarm_actions = [ aws_sns_topic.managed_rule_block_count.arn ] ok_actions = [ aws_sns_topic.managed_rule_block_count.arn ] metric_query { id = "total_blocked_requests" expression = "SUM([${join(",", local.rule_names)}])" return_data = true } dynamic "metric_query" { for_each = toset(local.rule_names) content { id = metric_query.value period = 0 metric { namespace = "AWS/WAFV2" period = local.period metric_name = "BlockedRequests" stat = "Sum" # メトリクスを取得するWAFのルールの名前やACL、リージョンを指定 dimensions = { Rule = "aws-common-${replace(metric_query.value, "-", "_")}" WebACL = "web-default-acl-v2" Region = data.aws_region.current.name } } } } }
metric_query
では、アラームに使う計算式やメトリクスの値を定義できます。今回は、複数のマネージドルールのブロック数の合計をしきい値に使用したいので、Terraformの dynamic
を使用して、各マネージドルールの metric_query
を作成しています。
dynamic "metric_query" { for_each = toset(local.rule_names) content { id = metric_query.value period = 0 metric { namespace = "AWS/WAFV2" period = local.period metric_name = "BlockedRequests" stat = "Sum" # メトリクスを取得するWAFのルールの名前やACL、リージョンを指定 dimensions = { Rule = "aws-common-${replace(metric_query.value, "-", "_")}" WebACL = "web-default-acl-v2" Region = data.aws_region.current.name } } } }
その後、各マネージドルールのブロック数をこちらの metric_query
で合計しています。なお、return_data=true
を定義すると、この metric_query
がCloudWatch Alarmのしきい値チェックの対象となります。
metric_query { id = "total_blocked_requests" expression = "SUM([${join(",", local.rule_names)}])" return_data = true }
そして、以下の部分でしきい値を定義しています。計算方法はコメントの通りです。
# `period` * `evaluation_periods` のあいだに `datapoints_to_alarm` 回だけ `threshold` を、 # `comparison_operator` で指定する比較方法で比較し、TRUE である場合、アラーム状態へ遷移 # evaluation_periods = 2 datapoints_to_alarm = 2 threshold = 1000 comparison_operator = "GreaterThanThreshold"
最後に、しきい値を超えた場合と、回復した場合のアクションとして、SNSへ通知を設定しています。 (SNSの内容は省略いたしますが、Slackへの通知が設定されています。)
alarm_actions = [ aws_sns_topic.managed_rule_block_count.arn ] ok_actions = [ aws_sns_topic.managed_rule_block_count.arn ]
以上の設定をすることで、マネージドルールのブロック数がしきい値を超えた時に、Slackに通知が飛ぶようになります。
まとめ
今回の記事では、AWS WAFのマネージドルールのブロック数が一定数を超えた際に、CloudWatch Alarmを利用してSlackに通知する仕組みについて解説しました。マネージドルールは強力な保護手段ですが、誤検知のリスクもあるため、常に状況を監視し、迅速に対応できるようにしておくことが重要です。これを実現するため、Terraformを使った設定方法を紹介しました。 今後も引き続き、WAF監視の自動化と効率化を進めていきたいと考えています。この記事が、同じような課題を抱えている方々の参考になれば幸いです。