🐟

AWS WAFのAWSマネージドルールをTerraformで一括追加する方法

2023/06/11に公開

皆さんは、TerraformでAWS WAFを導入する際に「とりあえずAWS Managedなルールを一括追加してCOUNTモードで挙動を確認したい!」ということはないでしょうか?
本記事はそのような方に向けて、AWS WAFのマネージドルールをシンプルな記述で一括追加できるTerraformコードをご紹介します。

対象読者

  • AWS WAFのマネージドルールをCOUNTモードにて、Terraformで一括追加したい方
  • Terraformのmap, object, dynamics blockの使い方を、コードを交えて理解したい方

前提

実行環境

  • OS
    macOS Monterey version 13.4
  • CPU
    Apple M1(Macbook Pro 2020)
  • MEM
    16GB
  • Program
    Terraform 1.4.6

はじめに

Terraformコードを求めている方が多いかと思いますので、はじめにコードを載せます。
コードをコピペしてinit, plan, applyすればwafルールが作成されます。
以降はコードをポイントごとに解説していきます。
また、実際に使う際には適宜Module化して利用するのが良いと思います。

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.0.0"
    }
  }
  required_version = ">= 1.4.0"
}

provider "aws" {
  profile = "default"
  region  = "ap-northeast-1"
  default_tags {
    tags = {
      Terraform = true
    }
  }
}

variable "rules" {
  description = "Map of AWS WAF Managed-rules"
  type = map(object({
    name                       = string
    priority                   = number
    cloudwatch_metrics_enabled = bool
    sampled_requests_enabled   = bool
  }))
  default = {
    AWSManagedRulesCommonRuleSet = {
      name                       = "AWSManagedRulesCommonRuleSet"
      priority                   = 100
      cloudwatch_metrics_enabled = true
      sampled_requests_enabled   = true
    },
    AWSManagedRulesAdminProtectionRuleSet = {
      name                       = "AWSManagedRulesAdminProtectionRuleSet"
      priority                   = 200
      cloudwatch_metrics_enabled = true
      sampled_requests_enabled   = true
    },
    AWSManagedRulesKnownBadInputsRuleSet = {
      name                       = "AWSManagedRulesKnownBadInputsRuleSet"
      priority                   = 300
      cloudwatch_metrics_enabled = true
      sampled_requests_enabled   = true
    },
    AWSManagedRulesSQLiRuleSet = {
      name                       = "AWSManagedRulesSQLiRuleSet"
      priority                   = 400
      cloudwatch_metrics_enabled = true
      sampled_requests_enabled   = true
    },
    AWSManagedRulesLinuxRuleSet = {
      name                       = "AWSManagedRulesLinuxRuleSet"
      priority                   = 500
      cloudwatch_metrics_enabled = true
      sampled_requests_enabled   = true
    },
    AWSManagedRulesUnixRuleSet = {
      name                       = "AWSManagedRulesUnixRuleSet"
      priority                   = 600
      cloudwatch_metrics_enabled = true
      sampled_requests_enabled   = true
    },
    AWSManagedRulesWindowsRuleSet = {
      name                       = "AWSManagedRulesWindowsRuleSet"
      priority                   = 700
      cloudwatch_metrics_enabled = true
      sampled_requests_enabled   = true
    },
    AWSManagedRulesPHPRuleSet = {
      name                       = "AWSManagedRulesPHPRuleSet"
      priority                   = 800
      cloudwatch_metrics_enabled = true
      sampled_requests_enabled   = true
    },
    AWSManagedRulesWordPressRuleSet = {
      name                       = "AWSManagedRulesWordPressRuleSet"
      priority                   = 900
      cloudwatch_metrics_enabled = true
      sampled_requests_enabled   = true
    },
    AWSManagedRulesAmazonIpReputationList = {
      name                       = "AWSManagedRulesAmazonIpReputationList"
      priority                   = 1000
      cloudwatch_metrics_enabled = true
      sampled_requests_enabled   = true
    },
    AWSManagedRulesAnonymousIpList = {
      name                       = "AWSManagedRulesAnonymousIpList"
      priority                   = 1200
      cloudwatch_metrics_enabled = true
      sampled_requests_enabled   = true
    },
    AWSManagedRulesBotControlRuleSet = {
      name                       = "AWSManagedRulesBotControlRuleSet"
      priority                   = 1300
      cloudwatch_metrics_enabled = true
      sampled_requests_enabled   = true
    }
  }
}

resource "aws_wafv2_web_acl" "example" {
  name        = "TerraformWebACL"
  description = "Example of a managed rule created by Terraform."
  scope       = "REGIONAL"

  default_action {
    allow {}
  }

  # AWS Managed rulesを一括追加
  dynamic "rule" {
    for_each = var.rules

    content {
      name     = rule.value.name
      priority = rule.value.priority

      override_action {
        # 以下をnoneとすることでcountモードを解除可能
        count {}
      }

      statement {
        managed_rule_group_statement {
          name        = rule.value.name
          vendor_name = "AWS"
        }
      }

      visibility_config {
        cloudwatch_metrics_enabled = rule.value.cloudwatch_metrics_enabled
        metric_name                = "${rule.value.name}WafMetric"
        sampled_requests_enabled   = rule.value.sampled_requests_enabled
      }
    }
  }
  # ruleを追加する際には以下にrule{}を追加

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

コード解説

マネージドルール定義

Terraformのmapobjectを利用してマネージドルールのVariable定義を行なっています。
定義したVariableに従って、default = {}内にて変数を定義しています。変数についてはTerraform公式:aws_wafv2_web_aclを参照してください。
default = {}内のnameにはAWS公式:AWS マネージドルールのルールグループのリストに記載されている、ルールの名前を記述してください。
また、Module化する場合にはVariableをModule側のvariable.tfなどに記述を行い、呼び出す際にdefaultの部分をrulesなどに変更して渡してください。

variable "rules" {
  description = "Map of AWS WAF Managed-rules"
  type = map(object({
    name                       = string
    priority                   = number
    cloudwatch_metrics_enabled = bool
    sampled_requests_enabled   = bool
  }))
  default = {
    AWSManagedRulesCommonRuleSet = {
      name                       = "AWSManagedRulesCommonRuleSet"
      priority                   = 100
      cloudwatch_metrics_enabled = true
      sampled_requests_enabled   = true
    },
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 中略
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    AWSManagedRulesBotControlRuleSet = {
      name                       = "AWSManagedRulesBotControlRuleSet"
      priority                   = 1300
      cloudwatch_metrics_enabled = true
      sampled_requests_enabled   = true
    }
  }
}

マネージドルール適用

マネージドルールを適用する際にはTerraformのdynamic blocksを利用して繰り返しの記述を避けています。
dynamic blocksは若干ややこしいかと感じる方もいるかもしれないですが、今回のようなruleをぐるぐる回して追加して行く際にはとても有用です。
また、コメントにて記載しておりますが、count {} --> none {}とすることでAWSマネージドルールにて検知された通信をBlockする動作にすることができます。
※現在の記載だとCountモードでの動作となります。各モードについてはAWS公式:AWS WAF ルールアクションをご覧ください。

  # AWS Managed rulesを一括追加
  dynamic "rule" {
    for_each = var.rules

    content {
      name     = rule.value.name
      priority = rule.value.priority

      override_action {
        # 以下をnoneとすることでcountモードを解除可能
        count {}
      }

      statement {
        managed_rule_group_statement {
          name        = rule.value.name
          vendor_name = "AWS"
        }
      }

      visibility_config {
        cloudwatch_metrics_enabled = rule.value.cloudwatch_metrics_enabled
        metric_name                = "${rule.value.name}WafMetric"
        sampled_requests_enabled   = rule.value.sampled_requests_enabled
      }
    }
  }

その他Tips

  • AWS WAFのPriorityは0 ~ 2,147,483,647の範囲で設定可能
  • マネージドルールのPriorityを設定
    許可リストを追加する際に追加しやすいよう、各マネージドルール間の間隔を100から100刻みで設定しています。
    前述した通り、Priorityの設定可能範囲は広いため、10刻みにするなど、お好みに応じて変更してください。

終わりに

AWS WAFはAWSマネージドルール以外にも、AWS Marketplaceで提供される3rd Partyベンダーさんが提供するマネージドルールも複数ありますので、それぞれの要件に合ったものを適用していただければと思います。
本記事が誰かの助けとなれば幸いです。

Discussion