App Service を利用したアーキテクチャでは、フロントエンドに Front Door や Application Gateway といった L7 のロードバランサーを追加する構成を組むことがそれなりにあります。
特に Front Door は CDN と L7 ロードバランサが組み合わされたサービスなので、静的コンテンツをキャッシュしつつ API などのリクエストはバックエンドプールに分散させることが簡単に実現出来ます。
実際に Front Door のチュートリアルでも地理分散された App Service の前に Front Door を 1 つ作成して、可用性を高める構成を取っています。DNS レベルでルーティングを行う Traffic Manager よりも、Front Door の方が制御しやすくバックエンド選択のパラメータが多いです。
設定が反映されるまで地味に時間がかかるのが難点ではあるのですが、その辺りは若干 Front Door Standard / Premium で改善されそうな気配があるので期待しています。
Front Door 自体の話はこれぐらいにして、本題である Front Door や Application Gateway といった L7 ロードバランサと App Service Authentication を組み合わせた時に発生する問題と、解決策の話をします。
Front Door を作成し App Service Authentication を設定
適当に Front Door を作成して、バックエンドには App Service を追加しておきます。App Service では予め App Service Authentication を使って Azure AD 認証を有効にしてあります。
今回は Front Door にカスタムドメインの設定までは行っていませんが、フロントに Front Door を追加すると当然ながらホスト名が App Service のデフォルトではなくなるので、Azure AD のアプリケーション登録からリダイレクト URI を変更します。
単純にホスト名を azurewebsites.net
から azurefd.net
に置き換えるだけで良いです。カスタムドメインを設定した場合は、それに置き換えれば問題ありません。
Front Door と Azure AD アプリケーションの設定はこれだけです。作業自体は難しくないはずです。
実際にアクセスしてみると、Azure AD のログイン画面にリダイレクトしたタイミングで、以下のようにリダイレクト URI のミスマッチエラーが出るようになります。
OpenID Connect に対応したプロバイダーを使っていると、たまに設定忘れで表示されるものにはなりますが、今回は正しいリダイレクト URI を設定しているのに表示されます。
原因は非常に単純で Azure AD のログイン画面にリダイレクトされる時のクエリパラメータを確認するとわかります。重要な redirect_uri
には見てわかる通り、アクセスに使用した azurefd.net
ではなくバックエンド App Service の azurewebsites.net
が渡されています。
この時点で詳しい方は原因が分かったと思いますが、App Service Authentication が X-Forwarded-Host
を正しく扱っていないことが原因です。
以下のエントリで書いたように、Front Door は一般的な L7 ロードバランサと同様に X-Forwarded-*
系ヘッダーをバックエンドへのリクエストに追加しますが、App Service Authentication は Host
ヘッダーのみを見てリダイレクト先を作成しているようです。
App Service Authentication の問題なので ASP.NET Core などで自前で実装すると、大体は X-Forwarded-Host
を正しく扱えるのでこのような問題は発生しないのですが、この手の認証を自前で実装するのは避けているのでコードを書かずに解決します。
これまでは解決しようがなかったのですが、App Service Authentiation の V2 からロードバランサー下にある場合の処理が改善され、設定の変更で対応できるようになりました。
新しい App Service Authentication V2 の設定を有効化
Azure Portal に新しい Authentication 設定が追加されたタイミングで、プラットフォームと ARM の両方で実装が新しくなり、今回のようなロードバランサー下のシナリオに対応できるようになりました。
リファレンスでは存在を知っていたのですが、ドキュメント化がまともにされていなかったので App Service Team Blog の記事によって、初めて動作と設定方法が公開されました。
具体的には forwardProxy
の中にある convention
プロパティの値を、デフォルトの NoProxy
から Standard
に変更すると、標準となっている X-Forwarded-Host
を扱うようになります。
App Service Authentication のランタイムは V1 と V2 で共通ですが、この設定を有効化するためには V2 に移行する必要があります。V2 への移行は Azure Portal からは簡単に行えますが、ARM Template や Terraform を使っている場合は手間がかかります。
今回は最初から Auth Settings V2 で作っているので、移行の手順に関しては省略します。
Auth Settings V2 のエンドポイントは PATCH
リクエストを受け付けてくれないので、以下のように ARM Explorer 上でサクッと編集するのが一番簡単です。
Application Gateway のように X-Forwarded-Host
以外のヘッダーを送ってくる場合は、Custom
に設定した上で customHostHeaderName
に利用するヘッダー名を設定すればよいです。関係ないですが apiPrefix
を変更すればエンドポイントを .auth
以外に変更することも出来ます。
設定を変更後に最初と同じように azurefd.net
にアクセスすると、今度は redirect_uri
が Front Door を指したままになっているので、Azure AD 認証も問題なく通るようになりました。
今回のようにバックエンドの App Service に対してアクセス制限を設定していない場合は、リダイレクト先が異なっていても Azure AD 側の設定漏れによってはログイン出来てしまうケースもあるので、アクセス制限込みで設定を忘れないようにしたいです。
Static Web Apps では現時点で問題あり
App Service Environment がバックエンドで使われている Static Web Apps でも、Custom Authentiation を有効化しつつ Front Door などの L7 ロードバランサーを組み合わせて利用すると、同様の問題が発生します。
残念ながら Static Web Apps では App Service のように設定変更が行えないため、現時点では回避できない問題となっていますが、来月あたりには対応するという返事は貰っています。
どのように設定するのかはまだ分からないですが、恐らく staticwebapp.config.json
に同じような設定が追加されるのだと思います。解消されるまでは上の Issue をウォッチしていきたいと思います。