銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

ASP.NET CoreでプロキシサーバーでSSL terminateさせるときにRequest.Schemeをhttpsにしたい

ASP.NET Coreを特にLinuxなどで動かす場合、Kestrelを使うことが多いのですが、Kestrel自体はシンプルなサーバーなのでフロントにHAProxynginxなどのプロキシーサーバーを配置することが多くなります。さらにHTTPSでアクセスするようにする場合、証明書はKestrel側ではなくてプロキシサーバー側に配置して、プロキシサーバーからKestrelまでは(プライベートネットワークだし)HTTPで通信するケースもあるかと思います。その場合、ASP.NET Core側でRequest.Scheme*1httpになります。これをhttpsにしたい場合のお話です。

OpenID ConnectやAzure AD B2Cなどの認証などのために、リクエストをリダイレクトする場合、ユーザーがアプリにリクエストしたURLを基にcallbackするURLを生成するため、Request.Schemeが使われます。このとき、リバースプロキシ経由でHTTPリクエストをKestrelが受信している場合、当然のごとくRequest.Schemehttpになります。

f:id:tanaka733:20171119231146p:plain

認証サーバーに認証後にCallbackするURLも一緒に渡し場合、Request.Schemeを基にURLを構築している場合*2このURLがhttpになります。認証サービスプロバイダー側がこれを許可していれば別ですが、例えばAzure AD B2Cの場合httpsでないと許可されないため動きません。他にもうまくいかないプロバイダーや認証以外のサービスもあると思います。そこで、ASP.NET Core側にhttpsでユーザーからリクエストは送信されて、リバースプロキシ経由でhttpになっていることを伝える必要がでてきます。 実はこの設定については、タイトルがnginxなので自分はすっかり読み飛ばしていたのですが、ASP.NET Coreのドキュメントにも載っています。

docs.microsoft.com

まず前提条件として、リバースプロキシサーバーがHTTPリクエストをKestrelに送信する場合に、X-Forwarded-ProtoというHTTPヘッダーにオリジナルのリクエストのプロトコルを値にしたヘッダーを追加している必要があります。多くの場合、X-Forwarded-Forと併せて設定されているのではないかと思います。

developer.mozilla.org

この条件のもと次のようなミドルウェアを追加するコードをStartupクラスのConfigureメソッドに記述します。認証サービスで利用する場合は、サンプルのようにUseAuthenticationメソッドの前に記述する必要があります。

//using
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.HttpOverrides;
//

app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});

app.UseAuthentication();

このメソッドはMicrosoft.AspNetCore.HttpOverridesというNuGetライブラリで提供されていますが、Microsoft.AspNetCore.Allが依存しているのでこれを参照している場合は明示的に追加する必要はありません。

www.nuget.org

これでこのミドルウェアを通った後は、HTTPヘッダーからX-Forwarded-Protoがなくなり、Request.Schemehttpsを返すようになるのでめでたし、ということになります。

*1:RequestはMicrosoft.AspNetCore.Mvc.ControllerBase クラスのプロパティ

*2:少なくとも現時点のAzure AD B2CのASP.NET Core向けライブラリはRequest.Schemeを内部で参照しており、変更不可だった