ストレージ アカウントで公開した静的な Web サイトに App Service からプライベート リンク経由でアクセスする

Pocket

はじめに

この記事では、下記を実現してみたいと思います。

  • ストレージ アカウントに格納した JSON を「静的 Web サイト ホスティング」で公開する。
    ただし、パブリック インターネットからは参照できないようにする。
  • Azure Functions で、上記で公開した JSON を参照して、内容をそのままレスポンスする HTTP トリガーを作成する。

図にすると、このような感じになります。

全体概要図

これを実現するとき、課題となるのは “JSON をパブリック インターネットから参照できないようにする” という制約です。
この設定をすることは簡単ですが、その状態で Azure Functions から JSON を参照するには、一工夫が必要になります。

この “一工夫” で、より安全にストレージ アカウントにアクセスすることができるようになります。
ぜひ最後までお付き合いください。

使用する機能について

ストレージ アカウントでは、下記の機能を使用します。

  • 静的 Web サイト ホスティング
    • Web サーバーなどを用意しなくても、ストレージ アカウントに格納したファイルを簡単に公開することができる機能です。
  • プライベート エンドポイント接続
    • 仮想ネットワーク (以下、VNet) 上のクライアントから VNet と Microsoft バックボーン ネットワークを経由することで、
      パブリック インターネットを経由せずにアクセスすることができる機能です。
      ストレージ アカウントのファイアウォール機能と組み合わせることで、パブリック インターネットからの接続を拒否し、
      プライベート エンドポイント経由のアクセスのみできる状態にして、安全なアクセスを実現できます。

Azure Functions (App Service も含む) では、下記の機能を使用します。

  • VNet 統合
    • Azure Functions (App Service) から VNet 内のリソースにアクセスできるようになる機能です。
    • ※ Azure Functions の場合、下記のいずれかの価格プランが必要です。
      • Premium プラン
      • App Service プラン (専用プラン)
    • ※ App Service の場合、下記のいずれかの価格プランが必要です。
      • Standard プラン
      • Premium プラン
      • PremiumV2 プラン
      • Elastic Premium プラン

作業手順について

『JSON をストレージ アカウントに格納し、静的 Web サイト ホスティングして、プライベート エンドポイント経由で Azure Functinos からアクセスする』を実現する場合、本来なら下記のように作業を進めるのが良いと思います。

  • ストレージ アカウント、Azure Functions など必要なリソースをデプロイする。
  • ストレージ アカウントのファイアウォール機能を使用して、プライベート エンドポイント経由以外のアクセスを拒否する。
  • ストレージ アカウントの静的 Web サイト ホスティングを有効化する。
  • ストレージ アカウントに JSON を格納する。
  • ストレージ アカウントのプライベート エンドポイントをデプロイする。
  • ??? の設定追加
  • Azure Functions の VNet 統合を有効化する。
  • Azure Functions で HTTP トリガーを作成する。

ですが、この記事では異なる手順で作業を進めます。
理由としては、各作業の合間で、その時点での設定状態における JSON のアクセス可否を試していきたいからです。

【この記事での作業手順】

  • ストレージ アカウント、Azure Functions など必要なリソースをデプロイする。
  • ストレージ アカウントの静的 Web サイト ホスティングを有効化する。
  • ストレージ アカウントに JSON を格納する。
  • Azure Functions の VNet 統合を有効化する。
  • Azure Functions で HTTP トリガーを作成する。
  • ストレージ アカウントのプライベート エンドポイントをデプロイする。
  • ストレージ アカウントのファイアウォール機能を使用して、プライベート エンドポイント経由以外のアクセスを拒否する。
  • ??? の設定追加

ストレージ アカウント、Azure Functions など必要なリソースをデプロイする。

下記のリソースをデプロイします。

  • VNet
    • Azure Functions の VNet 統合のためのサブネット
    • ストレージ アカウントのプライベート エンドポイントのためのサブネット
  • ストレージ アカウント
  • Azure Functions

VNet

VNet を作成

サブネット

※ Azure Functions の Premium プランの場合、VNet 統合のためのサブネットは 200 個以上の IP アドレスが確保できる必要があるようです。
  なので、IP アドレス範囲は x.x.x.x/24 もしくは、これより大きい範囲を指定してください。

サブネットを作成

ストレージ アカウント

ストレージ アカウントを作成

Azure Functions

※ Azure Functions の VNet 統合機能は、従量課金プランでは使用できません。
  Premium プランや App Service プラン (専用プラン) を使用してください。

Azure Functions を作成

ストレージ アカウントの静的 Web サイト ホスティングを有効化する。

ストレージ アカウントの静的 Web サイト ホスティングを有効化します。
このとき、静的 Web サイトとして公開するファイルを格納するための $web というコンテナーが自動的に作成されます。

ストレージ アカウントの静的な Web サイトを有効化

プライマリ エンドポイントの欄に表示されている URL が、ページにアクセスするときの URL になります。
今回は https://tstmmarticle002.z11.web.core.windows.net/ という URL ですが、ストレージ アカウント名の他に z11 という文字列が含まれています。
これは リージョン コード というもので、URL に含める必要はありますが、Azure の内部で使用されるだけで、他にそのコードを使用する必要はないとドキュメントに記載されています。

ストレージ アカウントに JSON を格納する。

自動的に作成された $web コンテナーに、下記の JSON をアップロードします。

  • sample.json
    {
        "message": "Hello, world!"
    }
    

data コンテナーに sample.json をアップロード

試しに、アップロードした JSON にブラウザからアクセスしてみます。

JSON にブラウザからアクセス

この時点では、ストレージ アカウントのファイアウォール設定はデフォルトの「すべてのネットワーク」になっています。
そのため、このようにパブリック インターネットから参照することができます。

Azure Functions の VNet 統合を有効化する。

Azure Functions の VNet 統合を有効化します。

Azure Functions のメニューで「ネットワーク」>「VNet 統合」

VNet 統合の設定

この後、ストレージ アカウントのプライベート エンドポイントをデプロイしますが、
プライベート エンドポイントを経由した通信をするために Azure Functions のアプリケーション設定に下記 2 つの設定を追加します。

名前
WEBSITE_DNS_SERVER 168.63.129.16
WEBSITE_VNET_ROUTE_ALL 1
  • WEBSITE_DNS_SERVER
    • アプリケーションがプライベート DNS ゾーンを使用できるようにするために必要となっています。
      Azure プラットフォームとデプロイしたリソース間の連携に使用されるようです。
  • WEBSITE_VNET_ROUTE_ALL
    • Azure Functions からのすべての送信トラフィックが、VNet を経由するようになります。

アプリケーション設定の追加

Azure Functions で HTTP トリガーを作成する。

公開した JSON の内容を取得し、そのままレスポンスする HTTP トリガーを作成します。
今回はお手軽に PowerShell で作成します。

関数のコード

using namespace System.Net

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

# Retrieve the content of the published JSON.
$uri = "https://tstmmarticle002.z11.web.core.windows.net/sample.json"
$body = Invoke-RestMethod -Method Get -Uri $uri | ConvertTo-Json

# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = [HttpStatusCode]::OK
    Body = $body
})

テスト結果

HTTP トリガーのテスト結果

このとおり、JSON の取得に成功しました。
ストレージ アカウントは、まだプライベート エンドポイントの設定をしていないので、Azure Functions からはパブリック IP でアクセスしているはずです。
それを確かめてみます。

Azure Functions のコンソール機能を使用して、下記のコマンドを実行してみます。
nameresolver は、DNS 機能をテストするために Azure Functions に用意されているツールです。

nameresolver tstmmarticle002.z11.web.core.windows.net

静的 Web サイト ホスティングの名前解決結果 - 1

ストレージ アカウントのプライベート エンドポイントをデプロイする。

ストレージ アカウントのプライベート エンドポイントをデプロイします。

ストレージ アカウントのプライベート エンドポイント デプロイ手順 - 1

ストレージ アカウントのプライベート エンドポイント デプロイ手順 - 2

ストレージ アカウントのプライベート エンドポイント デプロイ手順 - 3

ストレージ アカウントのプライベート エンドポイント デプロイ手順 - 4

ストレージ アカウントのプライベート エンドポイント デプロイ手順 - 5

ストレージ アカウントのファイアウォール機能を使用して、
プライベート エンドポイント経由以外のアクセスを拒否する。

この時点では、公開した JSON はどこからでもアクセスできる状態になっています。
これを、プライベート エンドポイントを経由したときのみアクセスできるようにファイアウォールを設定します。
下図のように設定することで、プライベート エンドポイント経由以外のアクセスを拒否できます。

ストレージ アカウントのファイアウォール設定

ファイアウォールが機能しているか確認するために、アップロードした JSON にブラウザからアクセスしてみます。

JSON にブラウザからアクセス - 404 エラー

404 になったので、意図したとおりパブリック インターネットからのアクセスは拒否されているようです。

Azure Functions は、どうでしょうか。
VNet 統合しているので、プライベート エンドポイント経由でアクセスできるはずですが…

HTTP トリガーのテスト結果 - エラー

このようにエラーになってしまいます。
エラーメッセージに Response status code does not indicate success: 404 (The requested content does not exist.). とあることから、名前解決したときにプライベート エンドポイント (プライベート IP) が取得できていないようです。
先ほどと同じように、Azure Functions のコンソール機能を使用して、名前解決がどうなっているか確認してみます。

静的 Web サイト ホスティングの名前解決結果 - 2

やはり、名前解決がうまくいっていないようです。
この問題を解決するのが、次の作業になります。

??? の設定追加

ストレージ アカウントのプライベート エンドポイントをデプロイしたとき、あるリソースが増えているはずです。
それが ”プライベート DNS ゾーン” です。
この時点で、プライベート DNS ゾーンは下図のようになっているかと思います。

プライベート DNS ゾーンの設定状態 - 設定追加前

リージョン コードが含まれた名前 (tstmmarticle002.z11) が登録されています。
さきほどの nameresolver コマンドでの名前解決では、リージョン コードを含まない名前で名前解決しようとしているように読み取れます。
なので、ここにリージョン コードを含まない名前を追加してみます。
※ IP アドレスは「リージョン コードあり / なし」のどちらも同じ IP アドレスを指定します。

プライベート DNS ゾーン - レコード セット追加

プライベート DNS ゾーンの設定状態 - 設定追加後

プライベート DNS ゾーンの設定を追加した状態で、あらためて Azure Functions の HTTP トリガーをテストしてみます。
また、名前解決がどうなったかも確認してみます。

HTTP トリガーのテスト結果 - 最後

静的 Web サイト ホスティングの名前解決結果 - 3

HTTP トリガーも動作するようになり、名前解決もプライベート IP が取得されるようになりました!
ストレージ アカウントのファイアウォールは変更していないので、パブリック インターネットから JSON にアクセスすることはできません。

まとめ

パブリック インターネットへの公開は防ぎつつ、Azure Functions からプライベート エンドポイント経由で JSON にアクセスすることができました。
今回実施した作業の中で、忘れがちな作業は、この 2 つでしょうか。

  • Azure Functions のアプリケーション構成で設定を追加する。
  • プライベート DNS ゾーンにレコード セットを追加する。

初めてこの構成を組んだ時、私はこの 2 点の設定が必要なことになかなか気づくことができず、非常に悩まされました…
この記事が、同じような悩みを抱えている方の一助になれば幸いです。

参考情報

Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です