🔨

aws_wafv2_web_acl 用のイデオムについて

2022/12/26に公開

問題

terraformのWAF v2用リソースは、ルール定義がnested blockとして実装されている。
これにより再利用性がかなり低い。これは aws_cloudfront_distribution と同じ欠点である。

aws_wafv2_web_acl の場合はCloudFrontよりはまだ状況はマシで、ルールグループを使えば最低限の再利用性は確保できる。
ただし、ルールグループを使用しても aws_wafv2_web_acl 内にかなりの記述をすることには変わりがない。

これらの問題を解決し、 WAF v2の再利用性を最大限確保しつつ冗長な記述をなくしたい。

解決策

resource "aws_wafv2_web_acl" "cloudfront" {
  provider = aws.us_east_1
  scope    = "CLOUDFRONT"

  name = "cloudfront"

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "cloudfront"
    sampled_requests_enabled   = true
  }

  default_action {
    allow {}
  }


  #####################################################
  # マネージドルールグループのdynamic blockループ
  #####################################################

  dynamic "rule" {
    for_each = [
      {
        name                    = "aws-managed-rules-amazon-ip-reputation-list"
        priority                = 21
        managed_rule_group_name = "AWSManagedRulesAmazonIpReputationList"
        excluded_rule_names     = []
      },
      # https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-ip-rep.html
      {
        name                    = "aws-managed-rules-anonymous-ip-list"
        priority                = 22
        managed_rule_group_name = "AWSManagedRulesAnonymousIpList"
        excluded_rule_names     = ["HostingProviderIPList"]
      },
    ]


    content {
      name     = rule.value["name"]
      priority = rule.value["priority"]

      visibility_config {
        cloudwatch_metrics_enabled = true
        metric_name                = rule.value["name"]
        sampled_requests_enabled   = true
      }

      override_action {
        none {}
      }

      statement {
        managed_rule_group_statement {
          name        = rule.value["managed_rule_group_name"]
          vendor_name = "AWS"

          dynamic "excluded_rule" {
            for_each = rule.value["excluded_rule_names"]
            content {
              name = excluded_rule.value
            }
          }
        }
      }
    }
  }


  #####################################################
  # 非マネージドなルールグループのdynamic blockループ
  #####################################################

  dynamic "rule" {
    for_each = [
        {
          name     = module.waf_rule_group_allow_pagespeed_insights_cloudfront.name
          priority = 50
          arn      = module.waf_rule_group_allow_pagespeed_insights_cloudfront.arn
        },
      ],
      [
        {
          name     = module.waf_rule_group_allow_privileged_ip_addresses_cloudfront.name
          priority = 131
          arn      = module.waf_rule_group_allow_privileged_ip_addresses_cloudfront.arn
        },
      ],
    ]

    content {
      name     = rule.value["name"]
      priority = rule.value["priority"]

      visibility_config {
        cloudwatch_metrics_enabled = true
        metric_name                = rule.value["name"]
        sampled_requests_enabled   = true
      }

      override_action {
        none {}
      }

      statement {
        rule_group_reference_statement {
          arn = rule.value["arn"]
        }
      }
    }
  }


  #####################################################
  # ルールグループではない直接ACLに書かれたルール
  #####################################################

  # 以下通常の直書きルール
}

キモは マネージドルールグループのdynamic blockループ非マネージドなルールグループのdynamic blockループ にある。
これらを書く場合、 override_actionvisibility_config を細かくチューンしたいときは少ない。そこで、これらにデフォルト値を与えつつdynamic blocksでfor_eachにすることでルールグループの宣言を以下の5行に集約している。

{
  name     = module.waf_rule_group_allow_privileged_ip_addresses_cloudfront.name
  priority = 131
  arn      = module.waf_rule_group_allow_privileged_ip_addresses_cloudfront.arn
},

実際やってみた感じ

3ヶ月ぐらいこのイデオムを複数のシステムで使ってみたが、ほとんどのユースケースをカバーでき非常に汎用性が高い。

スポンサー

この記事はSpeeeでの業務中に書きました。

Discussion