しばやん雑記

Azure Serverless とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure App Service の Access Restriction を使って Front Door からのアクセスのみ許可する

ドキュメントを適当に読んでいたところ、App Service の Access Restriction で特定の Front Door からのアクセスのみを許可する方法がプレビューとして記載されていたので、この辺りの確認をしておきました。

Front Door のドキュメントにもバックエンドへのアクセスを Front Door のみにロックダウンする方法は書かれていますが、ちょっと前から Access Restriction の機能追加で簡単に実現できるようになったという話です

初期のころは Front Door のバックエンドに割り当てられている 147.243.0.0/16 を追加する方法が多かったですが、少し前から Service Tag を使った制限に対応しているので、IP Range を使った方法はドキュメントから消されたようです。早めに Service Tag へ移行したほうが良いでしょう。

Service Tag のサポートについては以前に書いたので、こっちを参照してください。

結局のところ Service Tag だけでは Front Door からのトラフィックであれば、どのインスタンスか判別出来ないため全て素通りさせてしまう問題がありましたが、ドキュメントにもあるように X-Azure-FDID を併用することで一応は解決できます。

やっと本題ですが App Service の Access Restriction に、特定の HTTP ヘッダーの値によるアクセス制限を追加できるようになったので X-Azure-FDID による制限を組み込みやすくなったという話です。

この機能追加は割と前から ARM 上は実装されていたのですが、まともな使い方の紹介は存在しなかったのでスルーしていました。実質 Front Door 向けの機能です。

HTTP ヘッダーであれば何でも使えるわけではなく、以下の 4 つに限定されているようです。基本的にはリバースプロキシの下にいる場合を想定したヘッダーですね。

  • X-Forwarded-Host
  • X-Forwarded-For
  • X-Azure-FDID
  • X-FD-HealthProbe

実際に他のヘッダー名を追加しようとするとバリデーションでエラーになりました。

この中でも X-Forwarded-For は最初は少し違和感がありましたが、Access Restriction の処理が実行されているのは App Service の Web Worker の前にいる ARR なので、ARR の前に更にリバースプロキシが居ると Access Restriction の IP 制限は意図した通りに動かないので必要という話です。

そもそも Front Door や CDN を置いている場面で IP 制限が必要なのかというのは別の話です。

特定の Front Door からのアクセスのみ許可する

前置きが非常に長くなったので Front Door を使って HTTP ヘッダーでのアクセス制限を有効化してみます。基本はドキュメント通りではありますが、ARM Explorer の便利な使い方を書きたかったので。

Front Door がバックエンドに送る HTTP ヘッダーの一覧は以下の通りです。

今後は Front Door の Service Tag と X-Azure-FDID を Access Restriction に設定するといい感じに幸せになれます。出来れば Private Link とかで直接繋がってほしい気持ちはあります。

肝心の Front Door ID と呼ばれる GUID は Azure Portal から確認できるようになっていました。少し前までは無かったと思うので、最近追加されたのかもしれません。

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

ドキュメントでは Azure PowerShell を使って Access Restriction に追加していましたが、Azure PowerShell の使い勝手はイマイチなので ARM Explorer の Raw モードを使いました。

https://resources.azure.com/raw/

このモードを使うと VS Code の REST Client 拡張のように ARM REST API を実行できます。送信したい部分を選択して Ctrl+S を押せばリクエストが実行されます。

URL を直接指定するので自由に API Version を指定できます。API Version は何だかんだで結構更新されているので、ARM Explorer の標準モードでは出てこないプロパティなども多いです。

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

案外 ARM REST API を直接叩くのは面倒なので、ARM Explorer の Raw モードを使うのが簡単です。

config/web エンドポイントに対して以下のような JSON を PUT で送信すれば反映されます。

{
  "properties": {
    "ipSecurityRestrictions": [
      {
        "ipAddress": "AzureFrontDoor.Backend",
        "action": "Allow",
        "tag": "ServiceTag",
        "priority": 100,
        "name": "Azure Front Door",
        "headers": {
          "X-Azure-FDID": [
            "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
          ]
        }
      }
    ]
  }
}

ARM Explorer 上では以下のようになるので、リクエストボディと URL を同時に選択して送信します。

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

特にエラーが出ていなければ設定完了なので、Front Door のバックエンドに設定した App Service に直接ブラウザでアクセスすると、以下のように 403 エラーが返ってくるようになります。

もちろん Front Door 経由でアクセスした場合には正しくアクセスできます。

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

Front Door ID が ARM REST API を叩けば取れるので、Terraform や ARM Template でも問題なく扱えます。

Service Tag と X-Azure-FDID を同時に指定しているので、設定した Front Door 以外からバックエンドへのアクセスはこれで塞ぐことができました。

Azure CDN と特定のホスト名からのアクセスのみ許可する

Front Door の場合は専用のヘッダーが用意されていましたが、同じインフラを利用している Azure CDN (Standard Microsoft) ではそういったものは用意されていないので、Front Door を使うようにするか X-Forwarded-Host と組み合わせるぐらいの選択肢しかありません。

Azure CDN (Standard Microsoft) は Front Door と同じインフラを利用しているので、Service Tag も Front Door と同じものが使えます。したがって以下のような JSON を用意することで、ある程度バックエンドのロックダウンが行えます。

{
  "properties": {
    "ipSecurityRestrictions": [
      {
        "ipAddress": "AzureFrontDoor.Backend",
        "action": "Allow",
        "tag": "ServiceTag",
        "priority": 100,
        "name": "Azure CDN",
        "headers": {
          "X-Forwarded-Host": [
            "daruyanagi.com"
          ]
        }
      }
    ]
  }
}

値のマッチングルールはヘッダーによって変えられていて、X-Forwarded-Host の場合は DNS や証明書と同様のワイルドカードでの指定もできるようです。

App Service 直接アクセスの場合はカスタムドメインを割り当てていても X-Forwarded-Host は送信されないので、結果的に Azure CDN などのリバースプロキシ経由の場合のみ通るようになります。

しっかりとしたロックダウンが必要な場合には Front Door を使った方が良いとは思いますが、価格が CDN と比べると高くなるので必要に応じて使い分けていけばよいと思います。