AWS WAF で OPTIONS Request を捌く...!

これは [グロービス Advent Calendar 2023](https://qiita.com/advent-calendar/2023/globis) の7日目の記事です。

## お気持ち表明
「AWS WAF のルールで Block した上で 200 で返す」と言われてパッとユースケースが思い浮かぶ方はどのくらいいるでしょうか？、割と発想がトリッキーなのでは！？と、
検索しても類似ブログが見当たらないので、他者と被っていないのであれば！と思った次第です。

## 前提構成
下記のような構成のプロダクトがあるかもしれません（？）
SPA 用ドメイン、とは別に API 用ドメインが用意されている場合、何もしなければ CORS 制限に引っかかります。
実際には Application （Framework）等で CORS 関連の Access Control Header を 付与して Return する事でオリジン間リソース利用可能としている構成を想定してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629892/69da8120-9220-42ad-764e-cda0a2eb678e.png)

## どういう時に Block しつつ 200 を返したいか？

### 稀に見る WAF を使った Maintenance Mode で、下記のような時
WAF で社外アクセスを Block した際に Application まで到達せず、普段 Application が付与してる Access Control header が付与されないため、 SPA が Preflight Request 送信時に CORS Error となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629892/8bf03fc1-904c-23b1-d91b-e634d2278512.png)

### Cloudfront に Alias で複数ドメイン設定を行っている時
Cloudfront Alias に複数 Domain 付与している時、それぞれの Domain 向けに OPTIONS Request に Return する時に Access-Control-Allow-Origin を * などで一括で大きく許可したくない時です。
[MDN <origin> 「オリジンを指定します。1 つのオリジンだけを指定することができます。」](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Access-Control-Allow-Origin)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629892/7dab6950-00b2-e853-e1fd-6e09dc755a44.png)

## 設定
- WAF rule 優先度上位で Request Origin && Method で判定し、Custom Response を行う
- Request Origin を条件に入れ、必要ドメイン分 Rule を追加する事で Origin 事の許可ドメインを指定する事が可能です。

```hcl
resource "aws_wafv2_web_acl" "example" {
  name        = "example-acl"
  description = "example-acl"
  scope       = "REGIONAL"
  default_action {
    allow {}
  }

  # Preflight を 200 return する
  rule {
    name     = "preflight-return"
    priority = "0"
    action {
      block {
        custom_response {
          response_code = 200

          response_header {
            name  = "Access-Control-Max-Age"
            value = "3600"
          }
          response_header {
            name  = "Access-Control-Allow-Credentials"
            value = "true"
          }
          response_header {
            name  = "Access-Control-Allow-Headers"
            value = "Origin,Authorization,Accept,Content-Type"
          }
          response_header {
            name  = "Access-Control-Allow-Methods"
            value = "GET,POST,PUT,PATCH,DELETE,OPTIONS,HEAD"
          }
          response_header {
            name  = "Access-Control-Allow-Origin"
            value = "https://${SUB}.example.com"
          }
        }
      }
    }
    statement {
      and_statement {
        statement {
          byte_match_statement {
            positional_constraint = "EXACTLY"
            search_string         = "OPTIONS"

            field_to_match {
              method {}
            }

            text_transformation {
              priority = 0
              type     = "NONE"
            }
          }
        }
        statement {
          byte_match_statement {
            positional_constraint = "CONTAINS"
            search_string         = "${SUB}.example.com"

            field_to_match {
              single_header {
                name = "origin"
              }
            }

            text_transformation {
              priority = 0
              type     = "NONE"
            }
          }
        }
      }
    }
  }
}
```

### 確認
違和感がすごい（ネーミングと合わせて Preflight Requset を Block するルールの様に見える）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629892/c53d80c2-085e-ced1-aea0-ca6d3bf60483.png)

めげずに設定したホストにブラウザアクセスを行うと OPTIONS Request を Server awselb/2.0（正確にはELB Attache WAF） が応答しているのが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629892/e28a4ea1-1e88-071d-2dc4-6f84bbe4d064.png)

## さいごに
どうにか Origin が存在しない時でも AWS Managed Service 内で OPTIONS Request を捌けないかを考えてる時に閃いた設定です。
技術的には可能です（ただし色々大変）ですとか、技術的には可能です（ただしやって良いとは言ってない）などがあるかもです。
WAF Block request / Allow request 割合監視などを導入している場合、アラート発生となる可能性があります。
ALB 固定レスポンスで同様の事が出来ると良いのですが、現時点(2023.12)では Return Header のカスタマイズが出来ませんでした。
個人的には WAF Block 200 return...! の響き、割と気に入ってます。