Azure App Service で Application Gateway WAF v2 のレートリミットルールを設定してみた

2023.11.12

いわさです。

Microsoft Azure では L7 ロードバランサーとして Application Gateway というサービスがあります。
AWS でいう Application Load Balancer です。

様々なバックエンドの前段に配置することが出来るのですが、オプションで Azure マネージドな WAF (Web Application Firewall) を有効化することが出来ます。
先日のアップデートで、この WAF のカスタムルール内のレートリミットオプションが GA になったようです。

バックエンドに ASP.NET Core な App Service のホスティングしている場合に、攻撃緩和やスロットリングの目的でレートリミットが欲しい場合があって、最新の .NET 7 だとMicrosoft.AspNetCore.RateLimitingミドルウェアを使ってアプリケーション上で実装する方法があります。
あるいはクライアントサーバー型のアプリケーションの場合はクライアント側でSystem.Threading.RateLimitingを使って独自に実装する場合もあります。

Application Gateway の WAF ポリシーでコントロール出来ると、検出とブロックの挙動を切り替えたり、あるいはサーバーサイドで検出ログを分析したり、運用しやすい利点があり、アプリケーション上で実装する以外のアプローチとして Azure インフラレイヤーでレートリミットを有効化出来るのは非常に魅力的です。

作りたいもの

本日は App Service をバックエンドプールに構成した Application Gateway でレートリミットを有効化してみましたのでその様子を紹介します。

今回は次のように単純に App Serivce の前段に Application Gateway を配置し、WAF のカスタムルールで 1 分あたりの同一クライアント IP アドレスからのアクセスを 10 回までに制限してみます。

前提として、次のような適当な App Serice が既に存在している状態です。

Application Gateway を構成する

まずは Application Gateway を作成し、App Service と統合させます。

前提として、今回のレートリミット機能を使うには WAF V2 である必要があります。

Azure Application Gateway には V1 と V2 があります。
V1 は古い形式でロードバランサーインスタンス数などが静的ですが、新しい V2 の場合は自動スケーリングがサポートされています。
V1 は 2023 年 4 月 28 日から非推奨となっており、2026 年 4 月 28 日以降に廃止されます。今後は原則 V2 を使用するべきでしょう。

さらに V2 の中でも Standard と WAF が存在しており料金単価が異なっています。
WAF 機能が必要な場合は WAF V2 を選択します。

後ほど WAF ポリシーでレートリミットを構成しますが、この時点では新規に適当なものを作成しておきます。

今回は Application Gatewya の後ろが単独の App Service になりますので、バックエンドプールに App Service を指定しました。
複数のバックエンドを構成してパスベースルーティングさせることなども可能です。

今回は最低限の検証環境ということで、Application Gateway まではパブリック IP アドレス指定での HTTP アクセス、Applicatin Gateway からバックエンドの App Service へはデフォルトドメインでの HTTPS アクセスを行います。

フロントエンドは HTTP を、バックエンドは HTTPS を指定します。
ポイントとして、Application Gateway が App Serivce に送信するヘッダーは App Service デフォルトドメインである必要があるので、「well-known CA」を有効化し、ホスト名のオーバーライドを有効化しました。
カスタムドメインの実装を行う場合などはまたこのあたりの設定が異なってくると思います。

ヘルスチェックのエラーが発生していなければ Application Gateway に割り当てられたパブリック IP アドレスにアクセスしてみましょう。ヘルスチェックのエラーが発生している場合はバックエンドの構成を見直してみてください。

App Service でホスティングしているサービスに Application Gateway 経由でアクセスすることが出来ましたね。

WAF を構成する

レートリミットを有効化するにあって、前提として最新の WAF エンジンを使用する必要があります。
デフォルトのルールセットとして OWASP CRS 3.2 を選択します。

ではレートリミットを設定します。
レートリミットの実体はカスタムルールのルールタイプです。

次のように新規カスタムルールを追加してルールタイプに Rate limit を選択すると、レートリミット関連の設定を行うことが出来ます。
まずはレートリミットの計上する期間として 1 分間か 5 分間かを選択することが出来ます。
1 分を選択出来るのは結構こまかく制御出来て良いですね。

また、レートカウントをまとめるグループを選択することが出来ます。
グループを構成せずに一括でカウントすることも出来ますが、オプションとしてクライアント IP アドレスあるいはクライアント IP アドレスから判断される地理情報から選択ができます。
このあたりはグループ条件をもう少し細かく構成出来るとより使い勝手があがりそうなのでフィードバックしたいところです。

今回は次のように同一クライアント IP アドレスから、1 分間で 10 回までのレートリミットを設定し、アクションとして Deny (リクエストの拒否) を設定してみました。
Conditions が必須なのでご注意ください。ここでは公式ドキュメントを参考に IP アドレスが255.255.255.255/32以外という条件を設定しています。

最後に、ルールの Enable rule が ON であることを確認しましょう。

もうひとつ注意点があって、Azure Web Application Firewall のポリシーには 2 つのモードがあって、ポリシーに該当する場合に検出するかブロックするかポリシーとして構成することが出来ます。
デフォルトでは検出モード(detection)になっており、今回はブロックしたいので、以下から Prevention にスイッチします。

今回は検証用なので最初から Prevention モードに変更しましたが、実際の運用環境では Detection モードで一定期間評価し、誤検出などがなさそうであれば Prevention モードに移行することが推奨されています。

ブロックされるか確認

1 分間のうちに 10 回以上リクエストを送信してみましょう。
尚、今回はクライアントに cURL を使い、次の記事を参考にさせていただきステータスコードのみ表示してみました。

cURLでHTTPステータスコードだけを取得する #HTTP - Qiita

% curl -LI http://20.188.24.86/ -o /dev/null -w '%{http_code}\n' -s
200
% curl -LI http://20.188.24.86/ -o /dev/null -w '%{http_code}\n' -s
200

:

% curl -LI http://20.188.24.86/ -o /dev/null -w '%{http_code}\n' -s
403

一定回数を超えると 403 エラーとなりました。期待したとおりブロックされていますね。
ブラウザでアクセスしたところ、次のようなデフォルトの 403 画面が表示されました。

レートリミットの期間が 1 分間での評価なので、少しペースを落としてアクセスを継続してみると、レートを下回ったタイミングで正常なレスポンスを取得することが出来るようになりました。

% curl -LI http://20.188.24.86/ -o /dev/null -w '%{http_code}\n' -s
403

:

% curl -LI http://20.188.24.86/ -o /dev/null -w '%{http_code}\n' -s
200

ブラウザでアクセスした場合も次のように正常にアクセス出来ることが確認出来ています。

さいごに

本日は Azure App Service で Application Gateway WAF v2 のレートリミットルールを設定してみました。

カスタムルールで非常に簡単に設定することが出来ましたね。
Application Gateway が既に適用されている環境であれば簡単に導入出来ると思います。レートリミット関係で気になっていた方は今回の GA を機にぜひ使ってみてください。