🤖

Cloud Armor の細切れルールによる課金を節約する

2022/09/22に公開

こんにちは、クラウドエースの吉崎です。

Cloud Armor の料金体系をご存知でしょうか。
公式ドキュメントを見ると、以下の項目に対して課金されます。

項目 課金
WAF requests $0.75 per million requests
WAF security policies $5 per policy per month
WAF rules $1 per rule per month

最も課金額が大きいのは WAF security policies ですが、本稿で節約の対象とするのは WAF rules です。
出来るだけ作成されるルールの数を減らし、さらには管理の手間の削減に貢献するのが本稿の内容です。
※そうです。良い意味でケチです。

節約していない例

まずは会社ごとに許可する IP アドレスを愚直にルールに書いていった例をご覧ください。

before.tf
resource "google_compute_security_policy" "default" {
  name = "default"

  rule {
    description = "company1"
    action      = "allow"
    priority    = 100

    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        src_ip_ranges = [
          "1.1.1.1/32", "2.2.2.2/32", "3.3.3.3/32"
        ]
      }
    }
  }

  rule {
    description = "company2"
    action      = "allow"
    priority    = 101

    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        src_ip_ranges = [
          "4.4.4.4/32", "5.5.5.5/32", "6.6.6.6/32",
          "7.7.7.7/32", "8.8.8.8/32", "9.9.9.9/32",
        ]
      }
    }
  }

  rule {
    description = "company3"
    action      = "allow"
    priority    = 102

    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        src_ip_ranges = [
          "11.11.11.11/32", "12.12.12.12/32", "13.13.13.13/32",
          "14.14.14.14/32", "15.15.15.15/32", "16.16.16.16/32",
          "17.17.17.17/32",
        ]
      }
    }
  }

  rule {
    description = "company4"
    action      = "allow"
    priority    = 103

    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        src_ip_ranges = [
          "18.18.18.18/32", "19.19.19.19/32", "20.20.20.20/32",
          "21.21.21.21/32", "22.22.22.22/32", "23.23.23.23/32",
          "24.24.24.24/32", "25.25.25.25/32", "26.26.26.26/32",
          "27.27.27.27/32",
        ]
      }
    }
  }

  rule {
    description = "company4_2"
    action      = "allow"
    priority    = 104

    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        src_ip_ranges = [
          "28.28.28.28/32", "29.29.29.29/32", "30.30.30.30/32",
          "31.31.31.31/32", "32.32.32.32/32", "33.33.33.33/32",
          "34.34.34.34/32",
        ]
      }
    }
  }
}

description を見ればどの会社にどの IP アドレスが許可されているかぱっと見で分かるシンプルな構成ですが、以下のことが気になります。

  • company1 と company2 のルールを統合すれば課金を抑えられそう(でも違う会社だしなぁ)
  • 許可する IP アドレスが増えたとき _2 とかつけていくのなんか嫌
  • 許可する会社が増えたときどんどんルールが増えてコードが縦に伸びていきそう

節約している例

続いて、作成されるルールの数を減らし、管理の手間を削減した例をご覧ください。

after.tf
locals {
  company1 = [
    "1.1.1.1/32", "2.2.2.2/32", "3.3.3.3/32",
  ]
  company2 = [
    "4.4.4.4/32", "5.5.5.5/32", "6.6.6.6/32",
    "7.7.7.7/32", "8.8.8.8/32", "9.9.9.9/32",
  ]
  company3 = [
    "11.11.11.11/32", "12.12.12.12/32", "13.13.13.13/32",
    "14.14.14.14/32", "15.15.15.15/32", "16.16.16.16/32",
    "17.17.17.17/32",
  ]
  company4 = [
    "18.18.18.18/32", "19.19.19.19/32", "20.20.20.20/32",
    "21.21.21.21/32", "22.22.22.22/32", "23.23.23.23/32",
    "24.24.24.24/32", "25.25.25.25/32", "26.26.26.26/32",
    "27.27.27.27/32", "28.28.28.28/32", "29.29.29.29/32",
    "30.30.30.30/32", "31.31.31.31/32", "32.32.32.32/32",
    "33.33.33.33/32", "34.34.34.34/32",
  ]
}

resource "google_compute_security_policy" "default" {
  name = "default"

  dynamic "rule" {
    for_each = chunklist(concat(
      local.company1,
      local.company2,
      local.company3,
      local.company4,
    ), 10)

    content {
      description = format("rule%s", rule.key)
      action      = "allow"
      priority    = rule.key + 1

      match {
        versioned_expr = "SRC_IPS_V1"

        config {
          src_ip_ranges = rule.value
        }
      }
    }
  }
}

ポイントは、

を使っている点です。
なんとなく想像がつくかもしれませんが、concatで引数のリストを結合し、chunklistで 10 個ずつに分割しています。
これにより、以下の良いことがあります。

  • 各ルールに 10 個ずつ IP アドレスが設定されており、無駄がない状態になる(=節約)
  • company5 を追加するとき、ルールを足すのではなく locals に足すだけで良い(=可読性向上)
  • 許可する IP アドレスが一社につき 10 個を超えた場合、自動で 10 個ずつに分割される(=利便性向上)

terraform plan をした結果は以下をご覧ください。

terraform plan
$ terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # google_compute_security_policy.after will be created
  + resource "google_compute_security_policy" "after" {
      + fingerprint = (known after apply)
      + id          = (known after apply)
      + name        = "after"
      + project     = (known after apply)
      + self_link   = (known after apply)
      + type        = (known after apply)

      + advanced_options_config {
          + json_parsing = (known after apply)
          + log_level    = (known after apply)
        }

      + rule {
          + action   = "allow"
          + preview  = (known after apply)
          + priority = 1

          + match {
              + versioned_expr = "SRC_IPS_V1"

              + config {
                  + src_ip_ranges = [
                      + "1.1.1.1/32",
                      + "11.11.11.11/32",
                      + "2.2.2.2/32",
                      + "3.3.3.3/32",
                      + "4.4.4.4/32",
                      + "5.5.5.5/32",
                      + "6.6.6.6/32",
                      + "7.7.7.7/32",
                      + "8.8.8.8/32",
                      + "9.9.9.9/32",
                    ]
                }
            }
        }
      + rule {
          + action   = "allow"
          + preview  = (known after apply)
          + priority = 2

          + match {
              + versioned_expr = "SRC_IPS_V1"

              + config {
                  + src_ip_ranges = [
                      + "12.12.12.12/32",
                      + "13.13.13.13/32",
                      + "14.14.14.14/32",
                      + "15.15.15.15/32",
                      + "16.16.16.16/32",
                      + "17.17.17.17/32",
                      + "18.18.18.18/32",
                      + "19.19.19.19/32",
                      + "20.20.20.20/32",
                      + "21.21.21.21/32",
                    ]
                }
            }
        }
      + rule {
          + action   = "allow"
          + preview  = (known after apply)
          + priority = 3

          + match {
              + versioned_expr = "SRC_IPS_V1"

              + config {
                  + src_ip_ranges = [
                      + "22.22.22.22/32",
                      + "23.23.23.23/32",
                      + "24.24.24.24/32",
                      + "25.25.25.25/32",
                      + "26.26.26.26/32",
                      + "27.27.27.27/32",
                      + "28.28.28.28/32",
                      + "29.29.29.29/32",
                      + "30.30.30.30/32",
                      + "31.31.31.31/32",
                    ]
                }
            }
        }
      + rule {
          + action   = "allow"
          + preview  = (known after apply)
          + priority = 4

          + match {
              + versioned_expr = "SRC_IPS_V1"

              + config {
                  + src_ip_ranges = [
                      + "32.32.32.32/32",
                      + "33.33.33.33/32",
                      + "34.34.34.34/32",
                    ]
                }
            }
        }
      + rule {
          + action      = "allow"
          + description = "default rule"
          + preview     = (known after apply)
          + priority    = 2147483647

          + match {
              + versioned_expr = "SRC_IPS_V1"

              + config {
                  + src_ip_ranges = [
                      + "*",
                    ]
                }
            }
        }
    }
Plan: 1 to add, 0 to change, 0 to destroy.

───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

デメリット

この構成に変えたとき、コンソールから見たときにどの IP アドレスがどの会社のものか分からなくなるというデメリットがあります。

画像は文字が小さいですが、節約のためルールが会社ごとではなくなったため、説明を見てもどの会社の IP アドレスかは分かりません。

console_before
before(company1~4)

console_after
after(rule1~4)

まとめ

コンソールからルールと説明の関係を確認する必要がある場合を除いては、節約と管理の手間の削減から本稿の構成を取ると良いでしょう。

Discussion