terraformで Cloudfront に WAF2 を使って特定のパスやAPIにIP制限をする

# terraformで Cloudfront に WAF2 を使って特定のパスやAPIにIP制限をする

terraformでWAF２を使ってやるやり方が割と最近対応したので備忘録的なあれ

`/admin/*` と `/api/admin/*` にIP制限をするサンプル

##### IP sets と Regex pattern setsを作成

```js
resource "aws_wafv2_regex_pattern_set" "admin-path" {
  name        = "admin-path-set"
  scope       = "CLOUDFRONT"
  provider = aws.east

  regular_expression {
    regex_string = "^\\/?admin.*$"
  }
}

resource "aws_wafv2_regex_pattern_set" "admin-api-path" {
  name        = "admin-api-path-set"
  scope       = "CLOUDFRONT"
  provider = aws.east

  regular_expression {
    regex_string = "^\\/?api\\/admin.*$"
  }
}

resource "aws_wafv2_ip_set" "admin-ips" {
  name = "admin-ip-set"
  scope = "CLOUDFRONT"
  provider = aws.east
  ip_address_version = "IPV4"
  addresses = ["192.168.1.1/32", "192.168.1.2/32"] // 指定したいIPをサブネット付きで書く
}
```

#####　ACL作成

```js
resource "aws_wafv2_web_acl" "cloudfront-waf" {
  name = "cloudfront-waf"
  scope = "CLOUDFRONT"
  provider = aws.east

  default_action {
    allow {}
  }

  // /admin/* /api/admin は指定IPのみ許可する
  rule {
    name     = "admin-path-only-allow-ips"
    priority = 5

    action {
      block {}
    }

    // URLが ("/admin/*" or "/api/admin/*") && ！指定IP だったらBlockみたいなこと書いてある
    statement {
      and_statement {
        statement {
          or_statement {
            statement {
              regex_pattern_set_reference_statement {
                arn = aws_wafv2_regex_pattern_set.admin-path.arn

                field_to_match {
                  uri_path {
                  }
                }

                text_transformation {
                  priority = 1
                  type     = "NONE"
                }
              }
            }

            statement {
              regex_pattern_set_reference_statement {
                arn = aws_wafv2_regex_pattern_set.admin-api-path.arn

                field_to_match {
                  uri_path {
                  }
                }

                text_transformation {
                  priority = 1
                  type     = "NONE"
                }
              }
            }
          }
        }

        statement {
          not_statement {
            statement {
              ip_set_reference_statement {
                arn = aws_wafv2_ip_set.admin-ips.arn
              }
            }
          }
        }

      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = false
      metric_name                = "admin-path-only-allow-ips"
      sampled_requests_enabled   = false
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = false
    metric_name                = "cloudfront-waf"
    sampled_requests_enabled   = false
  }
}
```

##### Cloudfrontに反映

```js
resource "aws_cloudfront_distribution" "cloudfront" {
  // ...省略
  web_acl_id = aws_wafv2_web_acl.cloudfront-waf.arn
```

#### 注意点

CloudFrontはリージョンがGlobalでしか作れないので、下記のように `us-east-1` のProviderでACL等のリソースを作成しないとCloudfrontに設定できない

```js

provider "aws" {
  region     = "us-east-1"
  version    = "= 2.68.0"
  access_key = "hoge"
  secret_key = "fuga"
  alias      = "east"
}

resource "aws_wafv2_regex_pattern_set" "admin-path" {
  name        = "admin-path-set"
  scope       = "CLOUDFRONT"
  // ここ！！
  provider = aws.east

  regular_expression {
    regex_string = "^\\/?admin.*$"
  }
}
```