しばやん雑記

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

Azure App Service と Regional VNET Integration を使ったセキュアなアプリケーションの実装パターン

2020 年は Azure App Service にとってはネットワーク周りの新機能やアップデートが非常に多い年になりました。そろそろ今年も終わりなので App Service の定石アーキテクチャをアップデートする良い機会です。

まずは今年のネットワーク関連アップデートについておさらいしておきます。Regional VNET Integration はプレビューの時から使ってきたので、最近 GA したという気が個人的にはしていません。

  • GA
    • Regional VNET Integration (NSG / Route Table)
    • Private Link (Private Endpoint)
    • NAT Gateway
    • Hybrid Connection for Linux App Service
    • Premium V3 / VMSS Worker
  • Preview
    • Azure Functions restricting storage

今年のアップデート総まとめという気持ちでまとめています。個人的にはもうマルチテナントの App Service で十分すぎるので、ASE はもう使うことはないだろうという思っています。

最後にある見出しが一番言いたいことという感じですね。1 年使ってはまった点も入れています。

恐らく App Service に関しては今年最後になりそうな気がします。今後もアーキテクチャに関してはアップデートしつつ共有していきたいと思います。

Regional VNET Integration を使う利点

既にいろいろな人が話しているはずですし、ドキュメントにもまとまっているので何回も書く気はないのですが、軽く触れておきます。一般的には以下のような点が利点として挙げられています。

個人的には Regional VNET Integration を使うことで、これまでフワッとしがちだった PaaS / Serverless のアーキテクチャを、VNET という枠にはめて管理できるという点が気に入っています。別にフワッとしていることに問題はありませんが、アクセス制限のしやすさは段違いです。

  • VNET 内にデプロイされたリソースへのアクセス
  • Service Endpoint を使ったリソースへのアクセス制限
  • Private Endpoint を使ったプライベートかつセキュアなアクセスの実現(閉域化)
  • Express Route や VNET Peering 経由でリソースへのアクセス
  • NAT Gateway / Azure Firewall / NVA を使った Outbound 通信の制御
  • VNET での分離が社内規則・コンプライアンスが必要な場合への対応

もちろん Service Endpoint や Private Endpoint を使ったアクセスは常に使っているので、とっくの昔に IP アドレスでのアクセス制限という世界からは脱却しているのですが、Managed Identity と組み合わせることでよりセキュアな環境を簡単に実現できるというのが大きなメリットです。

地味に大きいのは VNET が必須となっているケースでも対応できるというのがあります。ASE で良いという話もありますが、あれは IaaS に近いので個人的には使う気になりません。

制約・注意点

基本的な制約については公式ドキュメントが良い感じにまとまっているので、実際に触ってみて挙動に疑問があれば読み直すのがお勧めです。自分も何回も読み直しています。

マルチテナントで PaaS な App Service 上で実現しているので、正直なところそれなりに制約は存在しますが、昔に比べるとかなり改善されました。

1 年以上色々と使ってきましたが、特に問題は出てきていません。安定して利用出来ています。

最初に Premium V2 / V3 / Elastic Premium の Service Plan を作る

既に何回も書いている気がしますが、Regional VNET Integration が利用可能なのは Premium V2 / V3 / Elastic Premium と新しい Scale unit 上で動く Standard です。

自分はたまに Scale unit ガチャと呼んでいますが、最初に Standard 以下を選んで App Service Plan を作成してしまうと、古い Scale unit 上にデプロイされることがあります。その場合は Gateway が必要な VNET Integration しか利用できません。

作成時に Premium V2 / V3 を選ぶと必ず新しい Scale unit にデプロイされるので、その後は Standard に下げても Regional VNET Integration が使えるままになります。

Terraform や ARM Template で構築する際には後から App Service Plan の Tier を変更するのは手間だとは思いますが、後で必要になった場合には非常に苦労するので先にやっておきましょう。

複数の App Service Plan を同じ Subnet に追加は出来ない

Azure Portal から Regional VNET Integration の設定を行うと、そもそも既に App Service Plan を追加済みの Subnet はグレーアウトで表示されて選べないので安心ですが、ARM REST API を使った場合には何故か追加が出来てしまったので事故りました。

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

具体的には Terraform で管理していたリソースで、新しく App Service Plan を作成した時に Subnet を作成するのを忘れて、そのまま既存の App Service Plan が追加されている Subnet に追加したのがきっかけです。

特にエラーが出ることもなく、正常に追加出来てしまったので気が付かなかったのですが、1 か月後ぐらいに App Service OS のアップデートが行われたタイミングで、急に VNET 接続が失われてしまいました。

Subnet のアドレス空間に注意

ドキュメントにも最小でも /28 のアドレス空間を持った Subnet が必要と書いてありますが、App Service はアップデートなどで定期的にインスタンスが入れ替わるので、一時的にインスタンス数が増えます。

そして使えないアドレスも存在するので余裕を持った設計をしましょう。

When you provision a subnet, the Azure subnet loses 5 IPs for from the start. One address is used from the integration subnet for each plan instance. If you scale your app to four instances, then four addresses are used.

Integrate app with Azure Virtual Network - Azure App Service | Microsoft Docs

特に Elastic Premium を使う場合には予想以上にインスタンス数が増える可能性もあるので、アドレス空間の確認と最大スケーリング数の設定で制限を超えないようにしましょう。

Azure Functions と NSG の組み合わせには注意

Regional VNET Integration と WEBSITE_VNET_ROUTE_ALL を組み合わせると Outbound では NSG が使えるようになるので、インターネットへの接続を全て遮断するようなルールを追加することも出来ます。

全遮断はしないと思いますが、Azure Functions はデプロイ中に自分自身に用意された管理用 API をインターネット経由で実行しようとするので、NSG で App Service への接続を遮断するとデプロイが常に失敗するようになります。自分に Private Endpoint を追加すると通るようになりますが、NSG には注意しましょう。

これは Route Table と NVA を組み合わせた時にも発生する可能性があります。

ネットワーク設計だけに頼らない

App Service と Service Endpoint / Private Endpoint をフルに利用したアーキテクチャの場合は、そもそも VM のようにセキュリティ上脆弱になりやすい部分が少なくなるので多少はましですが「VNET や NSG / Private Endpoint で制限しているから安全です」みたいな考えは捨てましょう。

ネットワーク設計で不必要なトラフィックを遮断しつつ、リソースへのアクセスに関しては Key Vault を使った認証情報の管理やローテーション、Managed Identity を使ったロールベースでのアクセス制限を組み合わせていきましょう。どちらかではなく両方を少ない労力で実現できる時代です。

実際のアーキテクチャ例

ここからは実際に自分が作ったことのあるアーキテクチャについてまとめています。

Express Route を使ったオンプレとの連携例がないのは、そう簡単に Express Route を用意できなかったということになりますが、Regional VNET Integration では当然対応しているのでいつか試したいところです。

VNET + Service Endpoint でアクセス元制限

これが超ド定番かつ基本的な使い方ですね。Azure Storage や SQL Database などのアプリケーション開発に必須と言えるサービスへのアクセス制限を、VNET の Subnet 単位で簡単に行えます。

対応しているサービスが最近は増えていないですが、定番どころは抑えてあります。

Subnet 単位での許可は大きく感じますが、1 つの App Service Plan は 1 つの Subnet を占有するのでつまり App Service Plan 単位での制限となります。設計次第では Application Security Group のように扱えます。

アーキテクチャを図にしてみると面白いことに VNET には Subnet 以外存在しません。

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

Regional VNET Integration は VNET 内にリソースをデプロイするのではなく、あくまでも統合なので VNET と Subnet は単なる通り道として使われます。土管的な感じです。

VNET + Private Endpoint で閉域化

Cognitive Search や SignalR Service は Private Endpoint は対応していますが、一方で Service Endpoint は対応していないのでアクセス元を制限するには Private Endpoint を使う以外に方法はありません。

最近は Service Endpoint よりも Private Endpoint のがサポートするリソースが多いです。

Service Endpoint とは異なり、Private Endpoint は Subnet に Network Interface がデプロイされて、それぞれが Private IP を持ちます。大抵は Private Endpoint と同時に Private DNS Zone もデプロイされるので、ホスト名はそのまま Private IP として解決されます。

App Service が不必要なリソースへアクセスしないように NSG を使って Outbound を制限することも出来ますが、前述したように制限しすぎには注意です。

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

この例では 1 つの VNET に対して Regional VNET Integration と Private Endpoint をデプロイしていますが、それぞれを別の VNET にデプロイしてピアリングで繋ぐことも出来ます。ただし Private DNS Zone をピアリング先にも関連付けておかないと Private IP での名前解決は出来ません。

Azure Monitor Private Link Scope を使うと Application Insights や Log Analytics も Private Endpoint 経由でのアクセスに出来るので、テレメトリを VNET 外に出すことなく収集できます。

大量の Private DNS Zone が作成されますが、こればかりは仕方ないのかもしれません。名前付けに一貫性がないのが大体の原因です。

VNET + NAT Gateway で Outbound IP の固定化

たまにある外部連携先から接続する IP アドレスを要求されるケースですが、App Service では Scale unit 単位で共通の Outbound IP が使われているので、同じ Scale unit 上の App Service からはアクセス可能です。*1

そもそも Outbound IP の数が多いので登録が大変という話もありましたが、NAT Gateway を組み合わせることで専用の Outbound IP を使うことが出来るようになりました。

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

上の例では Subnet に NAT Gateway を設定していますが、Route Table と Azure Firewall や NVA を組み合わせることも出来ます。考え方は大体同じです。

NAT Gateway の設定方法については以前のエントリに書いておいたので、こっちを参照してください。

NAT Gateway は追加で課金されますが、これまでは ASE が必要だったと考えると確実に有利です。

Azure Functions + VNET + Private Endpoint で閉域化したサーバーレス環境

閉域化した状態でサーバーレスのスケーラビリティの恩恵を受けることは少し前では考えられないことでしたが、Azure Functions の Premium Plan と Regional VNET Integration の組み合わせで可能になりました。

Azure Functions の特徴である高速なイベントベースのスケーリングも、追加の設定は必要ですが利用することが出来ます。詳しくは以下のドキュメントを参照してください。

それによって以下のように Azure Functions の Trigger 自体を Private Endpoint 経由に出来ます。もちろんイベントベースでのスケーリングが自動的に行われます。

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

今のところは Azure Functions が裏で使うストレージアカウントはパブリックのままです。

現在は早期プレビュー中ですが Azure Functions のストレージを含んだ全てを VNET 経由でアクセスする機能も実装されています。これによって Private Endpoint で制限をかけることが出来ます。

これで社内規則やコンプライアンス対応で VNET 経由でのアクセスが必須の場合にも対応できます。将来的には全てのパブリックリージョンで Azure Functions の全てを VNET 経由に出来るようになるはずです。個人的には Gov Cloud 向けに実装されている気がしています。

まとめと関連エントリ

長々と書いてきましたが、PaaS / Serverless が持つマネージドかつスケーラブルという特徴を保ったまま、更に IaaS が持つネットワーク周りの機能が簡単に使えるようになったというのは革命的だと思います。

しかし App Service がリリースされてすぐの牧歌的なアーキテクチャとは異なり、必要なリソースと設定が増えてくるので Terraform や ARM Template などを使ったコード化は必須になりつつあります。

最後に貼る場所がなかった関連エントリを載せておきます。来年の App Service にも期待しています。

*1:とはいえ何らかの認証はしているはず