とあるところから以下のようなと相談がありまして、いろいろ試してみました。
おなやみごと
- ユーザーの端末はインターネットへの接続を許可されていない閉域環境にある。ただ、ある外部 Web サイト
(第三者が管理?) へのアクセスだけは許可したい。 - 上記の外部 Web サイトは一部のユーザーだけが使用する (ある作業の一場面でしか使用しない) ので、Azure Firewall (Premium) を導入するまででもない。他のサービスを利用して実現したい。
- 端末の「プロキシ サーバーの設定」は別の用途で設定済みであるため、このための設定変更はできない。
準備する
検証環境を用意する
相談事を検証するため、Azure 上に仮想ネットワークをデプロイし、下図のような構成で用意しました。
「リバース プロキシ ( のところ)」に配置するものについて検証していきます。
なお、クライアントはとりあえず Windows OS の端末と想定します。
ターゲットとなる外部 Web サイトを調べる
対象の外部 Web サイトにアクセスした時、およびそのサイトで操作した時に発生する通信 (どのような FQDN に対して通信が発生するか) を把握しましょう。
地道に行うなら、ブラウザーの [開発者ツール] の [ネットワーク] タブで、操作ごとに発生する通信を確認すれば良いと思います。(もっと良い方法はあると思いますけど...)
おおよその Web サイトでは、以下のような構成、および振る舞いを行っていると思います。
- HTTPS のみのアクセスを許可している。
- ページ内のコンテンツが (FQDN が異なる) 複数のサーバー (Web サイトや CDN) に配置されている。 (JavaScript、CSS、画像 など)
- 表示されたページ内で何らかの操作を行うと、JavaScript などが Web サイトに対してリクエストしている (リンクを踏めばこうなる)。
SSL 証明書と名前解決
前述について、クライアントのブラウザーから HTTPS でかつ完全な URL (対象の外部 Web サイトの URL) でアクセスする場合は、クライアントとリバース プロキシ ( のところ) 間も HTTPS でかつその URL (FQDN) で通信できなければならないので、SSL 証明書を用意します。今回はプライベートな環境で使用する目的で自己署名証明書を PowerShell で以下のようなかんじで作成しておきます。
New-SelfSignedCertificate `
-Type SSLServerAuthentication `
-Subject "*.example.com" `
-DnsName "*.example.com", "example.com" `
-CertStoreLocation "cert:\LocalMachine\My" `
-KeyDescription "Self-signed certificate" `
-KeyExportPolicy Exportable `
-NotAfter (Get-Date).AddYears(30)
作成した証明書 (PKCS #12 でエクスポート) は、クライアントの証明書ストア (信頼されたルート証明機関) にインポートします。
名前解決については、クライアントの hosts ファイルに記載して行うこととします。
※クライアントからのリクエストに「ホスト名」が含まれる想定。
リバースプロキシとして検証してみたサービス
リバース プロキシとなりそうな候補として、以下の 3 つのサービスについて検証してみました。
- Windows 仮想マシン (IIS+ARR)
- 内部 App Service Environment (ILB ASE)
- Azure Application Gateway (SKU v1, v2)
これらについて、順に説明していきます。
リバプロとして Windows 仮想マシン (IIS+ARR)
こんなかんじの構成で。
IIS と Web サイトを構築する
今回は、Web サーバーとして「Internet Information Services (IIS)」を利用します。仮想マシン (Windows Server) を作成してサーバーの役割で「Web サービス (IIS) 」を追加します。
リバース プロキシとして「Application Request Routing (ARR)」を利用します。インストーラーは以下からダウンロードできます。
IIS 上の Web サイトは (プライベート) IP アドレス+ポートの組み合わせでひとつずつ作成するのがカンタンだと思います。また、Web サイトにバインドできる SSL 証明書 (サーバー証明書) をひとつのみですので、ドメイン単位でサブドメインのワイルドカードの SSL 証明書 (前述で作成した証明書) を用意して、Web サイトをバインドします。(ドメイン単位で Web サイトを作成して SSL 証明書をバインド)
場合によっては、複数の IP アドレスを用意する必要があります。仮想マシンに複数の IP アドレスを割り当てる方法については、以下のドキュメントをご参考ください。
- ひとつのネットワーク インターフェイス カード (NIC) に複数のプライベート IP アドレスを割り当てる方法
- 仮想マシンに複数のネットワーク インターフェイス カード (NIC) をアタッチする方法
IIS 上の Web サイトは以下のような構成で用意します。
URL を書き換える
IIS 上の Web サイトにアクセスしてきたら、対象の外部 Web サイト (FQDN) にアクセスできるようにします。
Web サイトごとに「URL 書き換え」で設定していきます。
要求されたホスト名が一致した時は該当の外部 Web サイト (FQDN) の URL を書き換える、一致しない時は次のルールに遷移するというように書き換えルールを作っていきます。
作成した書き換えルールは Web サイトの web.config に保存されます。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Access To www" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" pattern="^www.example.com$" />
</conditions>
<action type="Rewrite" url="https://www.example.com/{R:1}" />
</rule>
<rule name="Access To apis" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" pattern="^apis.example.com$" />
</conditions>
<action type="Rewrite" url="https://apis.example.com/{R:1}" />
</rule>
<!-- (中略) -->
</rules>
</rewrite>
</system.webServer>
</configuration>
アクセスログを設定する
リバース プロキシとして ARR を使うと、アクセスログにはアクセス元の IP アドレスではなく、IIS 上の Web サイトの IP アドレスが記録されてしまいます。
アクセスログにアクセス元の IP アドレスを記録したい場合のひとつの手として、IIS のログ記録でカスタムフィールドを追加して HTTP ヘッダーの「X-Forwarded-For」を設定します。
このように設定することによって、アクセス元の IP アドレス (X-Forwarded-For) が出力されるようになります。1
リバプロとして 内部 App Service Environment (ILB ASE)
こんなかんじの構成で。
前述の仮想マシン上の IIS+ARR で構築した内容を「ILB ASE」で実現してみます。
以下のドキュメントの手順を参考に「ILB ASE」と「Web アプリ」を作成します。
※デプロイにかなりの時間 (数時間ほど) がかかりますので焦らずに。
カスタム ドメインを追加する
前述で作成した証明書を Web アプリにバインドするため、カスタム ドメインを追加します。
なお、ILB ASE では、カスタム ドメインを所有者の検証なしで追加することができます。
証明書をバインドする
前述で作成した証明書をアップロードして、
追加したカスタム ドメインにバインドします。
IIS のリバース プロキシ機能を有効にする
Kudu で /site 配下に applicationHost.xdt を配置します。
applicationHost.xdt の内容は以下のとおりに記載して保存します。その後、Web アプリに設定を反映するために再起動しましょう。2
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<system.webServer>
<proxy
xdt:Transform="InsertIfMissing"
enabled="true"
preserveHostHeader="false"
reverseRewriteHostInResponseHeaders="false" />
</system.webServer>
</configuration>
URL を書き換える
Kudu で /site/wwwroot 配下に web.config を配置します。
前述の Windows 仮想マシン (IIS+ARR) の「URL を書き換える」の時と同じように URL の書き換えルールを書きます。前述との違いは、すべての FQDN に対する書き換えルールをひとつの web.config にまとめて記載します。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Access To www.example.com" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" pattern="^www.example.com$" />
</conditions>
<action type="Rewrite" url="https://www.example.com/{R:1}" />
</rule>
<rule name="Access To apis.example.com" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" pattern="^apis.example.com$" />
</conditions>
<action type="Rewrite" url="https://apis.example.com/{R:1}" />
</rule>
<rule name="Access To demodemo.jp" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" pattern="^demodemo.jp$" />
</conditions>
<action type="Rewrite" url="https://demodemo.jp/{R:1}" />
</rule>
<!-- (中略) -->
</rules>
</rewrite>
</system.webServer>
ネットワークのアクセス制御とルーティングを設定する
以下のドキュメントを参考に、NSG とルートテーブルを設定します。
診断ログを有効にする
アクセスログ (AppServiceHTTPLogs) は、診断ログを有効にすることで出力されます。以下のドキュメントを参照してください。
- ログを Azure Monitor に送信する
出力される AppServiceHTTPLogs の内容については、以下のドキュメントを参照してください。
IIS のアクセスログを出力したい場合は、以下のドキュメントを参照してください。
- Web サーバーのログ記録を有効にする
リバプロとして Azure Application Gateway
こんなかんじの構成で。
Application Gateway は、ホスト名によるルーティングする仕組みがないため、対象の外部 Web サイトの FQDN ごとに以下の要素を用意する必要があります。
- リスナー
- バックエンドプール
- HTTP 設定
- 要求ルーティング規則
バックエンド プールを作成する
バックエンド プールは、外部 Web サイトの FQDN ごとに作成します。
[ターゲットの種類] で「IP アドレスまたは FQDN」を選択し、[ターゲット] に先に調べた FQDN を入力します。
HTTP 設定を作成する
バックエンド プールの外部 Web サイトに HTTPS でアクセスする必要がある場合は、「End to End TLS 暗号化」を使用します。そのためには、この HTTP 設定で対象の外部 Web サイトの証明書 (Base-64 エンコード X.509(.CER) 形式) をアップロードする必要があります。なお、必要な証明書は SKU によって異なります (後述)。
- Application Gateway でのエンド ツー エンド TLS 暗号化
Application Gateway v1 で必要な証明書
Application Gateway v1 では「認証証明書 (バックエンド サーバー証明書の公開キー)」が必要です。入手方法については、以下のドキュメントを参照してください。
- 認証証明書をエクスポートする (v1 の場合)
なお、ブラウザーで対象の Web サイトにアクセスして、下図のように証明書を表示して赤枠のものをファイルコピーすることもできます (試したときはできました)。
Application Gateway v2 で必要な証明書
Application Gateway v2 では「信頼されたルート証明書」が必要です。入手方法については、以下のドキュメントを参照してください。
- 信頼されたルート証明書をエクスポートする (v2 の場合)
なお、ブラウザーで対象の Web サイトにアクセスして、下図のように証明書を表示して赤枠のものをファイルコピーすることもできます (試したときはできました)。
正常性プローブを作成する
また、バックエンド サーバーで End to End TLS 暗号化を有効にし、それらに対して Application Gateway から要求をルーティングするには、正常性プローブが成功して、正常な応答が受け取れなければなりません。このために「正常性プローブ」を用意します。
この正常性プローブには、実際にエンドユーザーがアクセスする URL (Web サーバー) で HTTP ステータス「200 ~ 399」を必ず返すコンテンツ (Favicon とか) を指定すれば良いと思います。
そして、各 HTTP 設定で「カスタム プローブ」を使用し、この「正常性プローブ」を指定すれば良いでしょう。
新しいホスト名でオーバーライドする
Application Gateway を経由してバックエンド サーバーにアクセスした場合、バックエンド サーバー (対象の外部 Web サイト) には Application Gateway のホスト名が送信されます。
対象の外部 Web サイトでどのように処理しているかわからない場合は、この項目に対象のホスト名 (FQDN) を設定しておくとこの値がバックエンド サーバーに送信されるので、設定しておくほうが良いでしょう。
リスナーを作成する
クライアントから HTTPS でアクセスするため、先に作成した SSL 証明書 (自己署名証明書) をアップロードしておきます。
- Application Gateway での TLS termination
また、FQDN ごとにリスナーを作成する必要があるので、[リスナーの種類] で「マルチサイト」を選択し、[ホスト名] にそれぞれの FQDN を入力します。
要求ルーティング規則を作成する
要求ルーティング規則によって、先に作成した「リスナー」が「バックエンド プール」と「HTTP 設定」にバインドされます。
ネットワークのアクセス制御とルーティングを設定する
以下のドキュメントを参考に、NSG とルートテーブルを設定します。
診断ログを有効にする
アクセスログ (ApplicationGatewayAccessLog) は、診断ログを有効にすることで出力されます。以下のドキュメントを参照してください。
出力される ApplicationGatewayAccessLog の内容については、以下のドキュメントを参照してください。
- Application Gateway v1 の場合
- Application Gateway v2 の場合
ちょっと気になる点...
フロントエンド IP が気になる
Application Gateway v2 のフロントエンド IP には必ずパブリック IP アドレスが付きます。プライベート IP のみの構成はサポートされていません。プライベート IP のみ使用したい場合は、以下の FAQ をご参考ください。
- プライベート フロントエンド IP アドレスのみで Application Gateway V2 を使用するにはどうすればよいですか?
NSG が気になる
Application Gateway で NSG はサポートされていますが、インターネットへのアウトバウンドで特定のポート (例えば HTTP と HTTPS) のトラフィックのみを許可して、これ以外のトラフィックを拒否するというようなルールを設定することはできません。
- ネットワーク セキュリティ グループ
ただ、Application Gateway のリソースを配置するサブネットには、Application Gateway のリソースしか配置できないので、それほど気にする必要はないかもしれませんけど。
まとめ
今回利用したサービスを以下のように「そもそもの役割」で分けることができると思います。
-
リバース プロキシ
- Windows 仮想マシン (IIS+ARR)
- 内部 App Service Environment (ILB ASE)
-
L7 ロード バランサー
- Azure Application Gateway (SKU v1, v2)
**「リバース プロキシ」**で括ったサービスは「URL Rewrite」という機能を使って、バックエンドのサーバーである対象の外部 Web サイトが動作しているか否かは意識せず、各サービス上に設置した Web サイトにアクセスされた時の振る舞い (URL 書き換え) を定義しました。
また、「URL 書き換えルール」は web.config に保存されるため、このファイルをアプリケーションのソースとして別途管理したほうが良さそうですね。
いっぽう**「ロードバランサー」**で括ったサービスは、バックエンドのサーバーである対象の外部 Web サイトが動作しているか否かを意識しなければなりません。そのために End to End TLS 暗号化やバックエンドの正常性チェックを施す必要があります。そして、End to End TLS 暗号化を有効にするためには、バックエンドのサーバーである対象の外部 Web サイトの証明書 (v1 の場合は公開キーの証明書、v2 の場合は信頼されたルート証明書) が必要で、証明書が更新されたら、Application Gateway の HTTP 設定にアップロードした証明書も更新する (第三者が管理している場合はいつ更新されるか分からず、後追いで対応することになるだろう) 運用が必要ですね。