しばやん雑記

ASP.NET Core とメイドさんが大好きなフリーランスのプログラマーのブログ

Application Gateway v2 が GA したので気になった機能を試す

既に Application Gateway v2 は GA してると思ってましたが、実はプレビューだったようです。

プロビジョニングや設定変更は圧倒的に早くなってますし、機能的にも v2 のが優れているので、もう v1 を使うメリットはほぼ無いでしょう。

価格は v1 と v2 で大きく変わってます。v2 は最低でも 2 インスタンス必要なので v1 よりは高くなると思いますが、パフォーマンスは v2 の方が良いのでトラフィックは多い場合は安くなりそうです。

個人的には Front Door 推しなんですが、Application Gateway も同じ L7 なのと前に使ったリソースが残っていたので、気になった機能を試しておきました。

HTTP Redirect

気が付いたら HTTP リダイレクトに対応していました。ユースケースとしては HTTP から HTTPS へのリダイレクトが多いかなと思いますが、パスベースでもリダイレクト出来るので URL Rewrite でやっていたことは実現できます。

External site へのリダイレクトは単純なので良いですが、Application Gateway の場合は別のリスナーへのリダイレクトが行えるので、HTTPS へ飛ばす場合はこっちを使います。

当然ながら HTTP と HTTPS でそれぞれリスナーを作っておく必要があります。

f:id:shiba-yan:20190502180815p:plain

Include Query と Include Path は忘れずにチェックを入れておきます。

これで保存すれば、HTTP から HTTPS へのリダイレクトが行われるようになります。

f:id:shiba-yan:20190502180824p:plain

Host 名を使ったマルチテナント時にも動作するようなので安心ですね。

パスベースで書く場合は、大体ルールが多くなりすぎてメンテナンスできず破綻することになりそうなので、何でも Application Gateway でやらない工夫は必要になると思います。

Rewrite HTTP Header

これは URL Rewrite では普通に行えていましたが Front Door では実現できなかった機能ですね。かなり柔軟に書けるので、大体のことは Application Gateway だけでカバーできると思います。

条件を正規表現で指定して、アクションでキャプチャした文字列を利用するというのは、割と URL Rewrite で使ってきたパターンですね。

URL Rewrite と同じような感じなので、今回は条件を弄らずに簡単な部分だけ試しました。

まず新しく Rewrite set を作成しますが、1 つのルールに対して 1 つの Rewrite set のみ割り当て可能なので、名前には少し気を付けたいところです。

f:id:shiba-yan:20190502181233p:plain

ドキュメントにもあったように HSTS のヘッダーをレスポンスに付ける Rewrite ルールを作成してみます。

と言っても固定値で済むので以下のように単純な設定で済みます。

f:id:shiba-yan:20190502181520p:plain

UI が分かりやすいので特に難しいことはないと思います。Header type だけは注意しましょう。

この Rewrite ルールを保存してブラウザからサイトにアクセスすると HSTS のヘッダーが返って来ます。

f:id:shiba-yan:20190502181632p:plain

とても簡単に設定できました。UI が分かりやすいというのは非常に良いポイントです。

Server Variables を使う

先ほどは固定値を返すだけのルールを作りましたが、条件で指定した正規表現がキャプチャした結果以外に、 Application Gateway が持ついくつかのサーバー変数を使ってヘッダーを弄ることも出来ます。

サーバー変数の一覧はドキュメントに書いてありますが、今回はテストとして SSL 周りの面白そうな値をヘッダーとして返すルールを作成しました。

f:id:shiba-yan:20190502184129p:plain

サーバー変数を使う場合は {var_SERVER_VAR_NAME} のように頭に var_ を付ける必要があります。

保存後にブラウザでアクセスすると、使用された暗号スイートと TLS バージョンが返って来ます。

f:id:shiba-yan:20190502183819p:plain

本来ならバックエンドへのリクエストに付けて、そのままアクセスログに保存することのが多いと思いますが、確認しやすいのでレスポンスに設定しています。

サーバー変数には Cookie の値やクライアントとの TCP RTT の値など、面白いものが多いです。

X-Forwarded-* の扱いに注意

L7 のロードバランサーを使っている場合には X-Forwarded-For などを適切にハンドリングしないと困ることになりますが、Application Gateway も例外ではなく設定が必要になります。

ASP.NET Core での対応方針は Front Door と同じなので、そっちを参照してください。

1 点だけ面倒な部分として、Application Gateway はアクセスされたホスト名を一般的な X-Forwarded-Host ではなく X-Original-Host というヘッダーで通知してきます。

These headers are x-forwarded-for, x-forwarded-proto, x-forwarded-port, and x-original-host.

How an application gateway works | Microsoft Docs

デフォルトでは X-Forwarded-Host を見に行くので、思ったような挙動になりません。

対応としては ForwardedHeadersOptions が見に行くヘッダー名を X-Original-Host に変更するか、Header Rewrite を使って X-Forwarded-Host を付ける方法があります。

今回は X-Forwarded-Host を付ける方法で対応しました。ルールは以下のようになります。

f:id:shiba-yan:20190502204020p:plain

これで ASP.NET Core アプリケーションが、正しくアクセスされたホスト名を認識出来るようになります。

Service Endpoint との組み合わせ時は更に注意

バックエンドとして App Service を使う場合では、今後は Service Endpoint を使って Application Gateway のサブネットからのみアクセス可能なように制限をすることが多くなると思います。

前に書いたようにプレビューですが、IP アドレスでの制限より分かりやすいです。

便利な Service Endpoint ですが、サブネットで App Service 向けに有効化するとアクセス元の IP アドレスが Application Gateway ではなく、IPv6 のユニークローカルアドレスに変化します。

特に X-Forwarded-For などへの対応を行わなかった場合は、クライアントの IP アドレスが v6 になることが確認できます。v6 を有効化していない VNET でも、Service Endpoint を有効化すると v6 になります。

f:id:shiba-yan:20190503015518p:plain

この時に問題となるのが、ASP.NET Core の ForwardedHeadersMiddleware でホスト名をオーバーライドするためには、アクセス元の IP Range を登録しておく必要があることです。

Front Door では IPv4 と IPv6 の Range が公開されていたのでそのまま設定できましたが、Service Endpoint が使う Range は公開されていないので、ユニークローカルアドレス自体の Range を追加しました。

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<ForwardedHeadersOptions>(options =>
    {
        options.ForwardLimit = 2;
        options.ForwardedHeaders = ForwardedHeaders.All;
        options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("fc00::"), 7));
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

とてつもなく広いですが、これでホスト名も正しくアクセスされたものが反映されるようになります。

f:id:shiba-yan:20190502204604p:plain

既に Application Gateway とバックエンドに App Service を使っていて、後から Service Endpoint を有効化する場合にはまりそうなので注意しましょう。

実装が ARR から nginx に変更

v2 ではプロビジョニングが早くなって、パフォーマンスが上がったという点が強調されていますが、中身が v1 では IIS 10 + ARR 3 という構成だったのが v2 では nginx に変更されたのが理由のようです。

バックエンドが全て死んでいる時のエラー画面が以下の通り nginx となっています。

Windows で nginx をホストする理由はほぼ無いと思うので、VM 自体も Linux ベースに変更されたのではないかと見ています。同一の API で中身が ARR と nginx で分かれているのは、中々面白い構成です。