AWS WAFのWebACLを複数リソースで共有している場合、レートリミットはリソース単位で効きますか?ルール単位で効きますか?
AWS WAFのWebACLは複数のリソースに関連付けて共有することが可能です。
WebACLあたり$5/月
の料金がかかりますので、コストを意識してWebACLを複数のリソースで共有されている環境も少なくないかと思います。
今回、お客様より「1つのWebACLを共有しているとき、レートリミットは関連付けた各リソース単位なのか、WebACL単位なのか?」というご質問をいただきました。
さっとAWSドキュメントを確認してみましたが、私の日本語読解力ではWebACLを共有している場合の考え方について明確に記載されている箇所を見つけられなかったので検証してみました。
先に3行まとめ
- レートリミットはWebACLのルール単位で効く
- 各リソース毎にレートリミットを効かせたい場合は、ルールを分け、スコープダウンステートメントでHostヘッダーなどの条件で分岐させる
- 2行でまとまった
確認したいこと
今回確認したいことを図にすると以下のとおりです。
- WebACLは1つ、レートリミットのルールも1つ
- 2つのCloudFrontが関連づけられている
- 特定のクライアントからそれぞれのリソースにリクエストを送るとき、レートリミットは合算されるか?
やってみる
検証1: 1つのRate-based ruleを共有する
今回は2つのCloudFrontに対して1つのWebACLを関連づけています。
また、当該WebACLにはRate limit:100
でBLOCKするルールを1つだけを作成しました。
まず、一方のCloudFrontにレートリミットを越えるリクエストを送ります。レートリミットはしきい値を越えても即時にブロックされるわけではありませんので、110回のリクエストを送ってもこのタイミングではすべて正常に処理されます。
$ for i in `seq 110` > do > curl -s https://d357oxdfq7yglz.cloudfront.net/index.html -o /dev/null -w '%{http_code}\n' > done 200 200 200 … 200 $
先のリクエストでレートリミットの100
を越えていますのでブロックが適用されるまで1~2分ほど待ったのち、もう一方のCloudFrontにリクエストを送ります。
$ curl -I https://dmicdmyajdzr0.cloudfront.net/index.html HTTP/2 403 server: CloudFront date: Sat, 16 Apr 2022 07:07:03 GMT content-type: text/html content-length: 919 x-cache: Error from cloudfront via: 1.1 c7b74e12b97e13a0b0116342d7b1e06c.cloudfront.net (CloudFront) x-amz-cf-pop: NRT51-C3 x-amz-cf-id: bQPv59jXwjnm4dPxUdOjcdL03sNii8QJOXSMaM15KetzncJhmHEu7Q==
dmicdmyajdzr0.cloudfront.net
へのリクエストは1回目にも関わらずブロックされました。このことからリクエスト先のリソースでの区別はされておらずWebACLのルール単位でレートリミットが判定されていることが解りました。
検証2: Rate-based ruleで分岐する
では次に、もし共有WebACL内で各リソース毎にレートリミットを持ちたい場合の検証をやってみましょう。
この場合は、以下のようにレートベースのルールを複数に分けて登録します。各ルール内のスコープダウンステートメントでHostヘッダーなどを条件にルールごとでカウントされるリクエストを分けることで実現します。
先ほどと同様にまず一方にレートリミットを超えるリクエストを送ります。
$ for i in `seq 110` > do > curl -s https://d357oxdfq7yglz.cloudfront.net/index.html -o /dev/null -w '%{http_code}\n' > done 200 200 200 .. 200 $
1〜2分経過した後、リクエストを送りレートリミット超過が反映されてブロックされる状態であることを確認しました。
$ curl -I https://d357oxdfq7yglz.cloudfront.net/index.html HTTP/2 403 server: CloudFront date: Sat, 16 Apr 2022 08:02:38 GMT content-type: text/html content-length: 919 x-cache: Error from cloudfront via: 1.1 56eaaf829f4d1d68353f81d5f1643e7c.cloudfront.net (CloudFront) x-amz-cf-pop: NRT51-C2 x-amz-cf-id: vpwwcCaWqsjxtYlaUoBNx-35_6VNTAoLuZ8XsVE0rLs6l_eoDcVKDg==
この状態でもう一方のCloudFrontにリクエストを送ってみると、正常にリクエストが通ることが確認できました。
$ curl -I https://dmicdmyajdzr0.cloudfront.net/index.html HTTP/2 200 content-type: text/html content-length: 103 date: Sat, 16 Apr 2022 08:02:49 GMT last-modified: Sat, 16 Apr 2022 05:52:18 GMT etag: "e33ba3242ce535006082aa67863b25ab" accept-ranges: bytes server: AmazonS3 x-cache: Miss from cloudfront via: 1.1 eae246afe964f7c3bcdcd6a113b3570e.cloudfront.net (CloudFront) x-amz-cf-pop: NRT51-C3 x-amz-cf-id: e1Vyoo0Y33um72vmk1lA6VTWjch3lq96_LuUwuJRjEk2SR3ZZbvwWg==
試しにコチラでもレートリミットを超えるリクエストを送り、1〜2分経過した後に再度リクエストを送るとブロックされることが確認できました。
$ for i in `seq 110` > do > curl -s https://dmicdmyajdzr0.cloudfront.net/index.html -o /dev/null -w '%{http_code}\n' > done 200 200 200 ... 200 $ $ curl -I https://dmicdmyajdzr0.cloudfront.net/index.html HTTP/2 403 server: CloudFront date: Sat, 16 Apr 2022 08:04:46 GMT content-type: text/html content-length: 919 x-cache: Error from cloudfront via: 1.1 c0de8e3a0a5bd76a28840b4643d652c8.cloudfront.net (CloudFront) x-amz-cf-pop: NRT51-C3 x-amz-cf-id: W_-5RHUZQt01cDx6BTfsr0-2Klon8FVwLdYtKCrtu0p77gwfMwRKQw==
AWS WAFのログを確認してみるといずれもRATE_BASED
でBLOCK
されていますがterminatingRuleId
が異なっています。このことから、それぞれのルール単位でレートリミットが効いていることが解りますね。
検証は以上です!
さいごに
コストを節約するためにWebACLを共有している環境も少なくないと思います。レートリミットを1つのルールで利用される場合、スコープダウンステートメントで絞り込みをしていない限りは、共有しているすべてのリソースへのリクエストがレートリミットのカウント対象になっていることに気を付けてください。
リソース毎にレートリミットを持ちたい場合は、個別にルールを作り、スコープダウンステートメントでHostヘッダー等を条件指定するようにしてください。