🛡️

ゼロから作る『V-Net閉域化』- Azure Functionsを完全プライベート化

に公開

本稿は、MCPハンズオンシリーズの第4弾です。前回のAPIM前段化 - Remote MCPをAPIMで公開するの続編として、APIMは公開ゲートウェイのまま、バックエンドのAzure FunctionsをV-Netの中に完全に隠す構成を実現します。

📖 MCPハンズオンシリーズ

このシリーズでは、MCPの基本から実践まで段階的に学習できます:

記事 内容 形態
🧩 入門ガイド MCPの概要・エージェンティックWebの基本理解 導入編
🤖 ローカル編 Azure Functionsでローカル MCPサーバーを構築 ハンズオン①
🌐 リモート編 Azure FunctionsによるリモートMCPサーバー構築 ハンズオン②
🛰 APIM前段化 APIMによる MCPエンドポイントのAPI管理 ハンズオン③
📍 本記事 V-Net閉域化による完全プライベート化 ハンズオン④

この記事で得られること

前回のハンズオン③では、Azure Functionsの前段にAPI Management(APIM)を配置し、Functionsのシステムキー(mcp_extension)をNamed Valuesに秘匿化して自動付与する仕組みを構築しました。これにより、システムキー漏洩リスクや運用ガバナンスは改善できましたが、その時点でもAzure Functionsは依然としてパブリックに公開されており、ネットワーク面の攻撃面積は十分に絞り込めていませんでした。

本記事では、前回のAPIM+Functions構成を踏まえつつ、パブリック経路を閉じてPrivate Linkのみで到達可能にすることで、セキュリティを次の段階へ高めます。APIMのポリシーやMCPクライアント側の設定は最小変更のまま、FunctionsにPrivate Endpointを作成し、Private DNS(privatelink.azurewebsites.net)で名前解決します。これによりFunctionsへの接続は仮想ネットワーク内に限定されます。

APIM側はStandard v2の「Outbound V-Net Integration」を有効にし、V-Net経由でプライベート化されたFunctionsバックエンドへ到達させます。クライアント(例:VS Code Copilot)は引き続きAPIMの公開URLに接続し、APIMがV-Net経由でPrivate EndpointのFunctionsに中継する形です。これによりゼロトラスト/Defense in Depthの考え方に沿って、インターネット面に露出するのはAPIMのみとなり、バックエンドは閉域化されます。

ゴールと全体像

構築する構成は以下の通りです:

重要なポイント

  • APIM Standard v2(またはPremium v2)の Outbound V-Net Integration でV-Net内へ"出ていく"
  • Functions側は Private Endpoint(PE) を張り、privatelink.azurewebsites.netのPrivate DNSゾーンで解決
  • APIMのバックエンドURLは従来どおり https://<function>.azurewebsites.net/...のまま(DNSがPEに解決)
  • APIMポリシー(SSE対応やx-functions-key付与)は変更不要、ネットワークだけ閉じる設計

事前確認

V-Net閉域化を実施する前に、以下の要件を満たしているか確認してください。特にAPIM v2のOutbound V-Net Integrationには明確な前提条件があるため、事前チェックを必ずクリアしてから作業を進めることが重要です。

1. SKU要件

APIMについては Standard v2またはPremium v2(プレビュー版)が必要です。これらのSKUでのみOutbound V-Net Integration機能が提供されています。

2. 配置要件

APIMインスタンスと作成するV-Netは 同一リージョン・同一サブスクリプション に配置する必要があります。リージョンやサブスクリプションが異なる場合は統合できません。

3. Functions要件

Azure Functionsについては Private Endpointに対応したプラン(Flex / Premium / Dedicated等)を使用し、Private DNSゾーン privatelink.azurewebsites.net が正しく構成されている必要があります。

Step 1. V-Netと2つのサブネットを作成

https://portal.azure.com/signin/index

Portal → 検索 → 仮想ネットワーク+ 作成

1-1. V-Net設定項目

Named Values設定
基本設定:

  • リソースグループ:APIMを作成したリソースグループ
  • 仮想ネットワーク名mcp-vnet(グローバル一意)
  • リージョン:APIMと同じリージョン推奨(例:Southeast Asia)

セキュリティ:
既定

IPアドレス:
以下のアドレス設計で進めます。

Named Values設定

  • V-Net10.20.0.0/16

サブネット1: snet-pe(FunctionsのPrivate Endpoint用)

Named Values設定

用途:Function AppにPrivate Endpointを張る“受け口”のサブネット。

  • サブネットの目的:Default(特別な予約サブネットではない)
  • 名前snet-pe
  • IPv4 アドレス範囲10.20.0.0/24
  • 開始アドレス10.20.0.0
  • プライベート サブネット:オフ
  • NAT ゲートウェイ:なし
  • ネットワーク セキュリティ グループ:なし(PE用サブネットではNSG/UDRは適用されないため)
  • ルート テーブル:なし
  • サービス エンドポイント:なし(Private Link を使用するため不要)
  • サブネット委任:なし
  • プライベート エンドポイントのネットワーク ポリシー:無効(既定のまま・PE配置の要件)

サブネット2: snet-apim-int(APIM Outbound V-Net Integration用)

Named Values設定

用途:APIM が VNet 内へ“出ていく” ための専用サブネット。ここに リソースは直接配置しません(APIMの統合先として“予約”するだけ)

  • サブネットの目的:Default
  • 名前snet-apim-int
  • IPv4 アドレス範囲10.20.1.0/24
  • 開始アドレス10.20.1.0
  • サイズ/24(最小/27、推奨/24このサブネットはAPIM専用
  • プライベート サブネット(既定の送信アクセスなし):オフ(推奨)

    APIMは統合後、Azure Storage(443) などのパブリック依存先への送信が必要です

  • NAT ゲートウェイ:なし(既定の送信アクセスありのため不要)
  • ネットワーク セキュリティ グループ:なし(後ほど作成・関連付け)

    少なくとも 宛先:Service Tag = Storage / 443 / Allow(Outbound)が必要

  • ルート テーブル:なし(既定では不要)
  • サービス エンドポイント:なし
    Named Values設定
  • サブネット委任Microsoft.Web/serverFarms(必須)

    v2の"Outbound VNet Integration"は委任付きの専用サブネットが要件

  • プライベート エンドポイントのネットワーク ポリシー:既定(このサブネットにPEは配置しません)

Named Values設定
2つのサブネットが作成させていることを確認後、レビューと作成に移り、作成

1-2. NSGを作成しsnet-apim-intに関連付け

Portal → 検索 → ネットワーク セキュリティ グループ+作成

Named Values設定
基本設定:

  • リソースグループ:V-Netを作成したリソースグループ
  • リソースグループ:V-Netを作成したリソースグループ
  • 名前mcp-nsg
  • リージョン:V-Netと同じリージョン

作成完了後、NSGリソースを開いて送信ルールを追加します。

送信セキュリティ規則の追加

NSG画面で [設定 > 送信セキュリティ規則][+追加]

NSG送信ルール設定
NSG送信ルール設定

設定項目:

  • ソースService Tag
  • ソースサービスタグVirtualNetWork
  • ソースポート範囲*
  • 宛先Service Tag
  • 宛先サービスタグStorage
  • サービスCustom
  • 宛先ポート範囲443
  • プロトコル:TCP
  • アクション:許可
  • 優先度:100
  • 名前allow-out-storage-443

サブネットへの関連付け

NSG画面で [設定 > サブネット][+関連付け]

NSG関連付け

  • 仮想ネットワーク:作成したV-Net(mcp-vnet
  • サブネットsnet-apim-int

これでAPIMがAzure Storageなどの依存サービスに送信通信できるようになります。

Step 2. Function AppにPrivate Endpointを追加

Azure Portalから以下の手順でPrivate Endpointを設定します

2-1. Private Endpointの作成

  1. Azure PortalAzure Functionsネットワークプライベート エンドポイント+追加 (簡易)

NSG関連付け

  1. 設定項目:
  • 名前pe-func(任意)
  • 仮想ネットワーク:作成したV-Net(mcp-vnet
  • V-Net/サブネットsnet-pe
  • Private DNS統合はい

2-2. Private DNSの自動設定

Private DNS統合を有効化すると、以下が自動で設定されます

  • privatelink.azurewebsites.netのPrivate DNSゾーンにAレコードが自動登録
  • V-Netにリンクされます(Zone Group)

以降、APIMからhttps://<app>.azurewebsites.netにアクセスしても、VNet内のDNSが <app>.privatelink.azurewebsites.net(PEのプライベートIP)へ解決して到達します。

Step 3. APIM(Standard v2)でOutbound V-Net Integrationを有効化

3-1. V-Net統合の設定

  1. Azure PortalAPI Managementネットワーク
  2. Outbound features: virtual network integration を選択

NSG関連付け

  1. 設定項目:
  • Virtual network:Step1で作成したV-Net(mcp-vnet
  • サブネットsnet-apim-int

3-2. 統合後の状態

インテグレーション後もAPIMのゲートウェイは パブリックのまま です。バックエンドに対してはV-Net経由でプライベート到達します。

Step 4. APIMのバックエンドURLはそのまま

4-1. URL設定

バックエンドのURLは これまで通り で変更不要です:

https://<function>.azurewebsites.net/...

4-2. DNS解決の仕組み

V-Net内DNSがPrivate EndpointのAレコードへ自動解決してくれるため、APIMポリシー(SSE向け)は前回のままでOKです。

Step 5. 完全クローズドにする

最後にAzure Functions側の公開アクセスを無効化すれば、Private Endpoint経由以外の受信を拒否できます。

動作確認結果

  1. Azure PortalAzure Functionネットワーク
  2. 公衆ネットワーク アクセス: 無効 に設定

Step 6. 動作確認

V-Net統合とPrivate Endpoint設定後、VS CodeからAPIM経由でMCPサーバーに接続できることを確認します。

6-1. VS Codeの設定

.vscode/mcp.jsonの設定は前回と同じです:

{
  "servers": {
    "remote-mcp-via-apim": {
      "type": "sse",
      "url": "https://<apim-name>.azure-api.net/mcp/runtime/webhooks/mcp/sse"
    }
  }
}

設定値:

  • <apim-name>:APIMの既定ドメイン
  • APIMリソース概要のゲートウェイのURLで確認できます

APIM URL確認

6-2. 接続テスト

Copilot Agentチャット画面で以下をテスト:

テスト手順:

  1. reverse_text 関数を呼び出し
  2. text: "いろはにほへと ちりぬるを" を渡す
  3. "をるぬりち とへほにはろい" が返ってくれば成功!

動作確認結果

まとめ

本記事では、前回のAPIM前段化に続いて、Azure Functionsで構築したMCPサーバーの完全閉域化を実現する方法を学びました。

仮想ネットワーク(V-Net)に2つの専用サブネット(snet-pesnet-apim-int)を構築し、Azure FunctionsにPrivate Endpointを配置することで、インターネットからの直接アクセスを完全に遮断しました。Private DNS(privatelink.azurewebsites.net)による内部名前解決により、APIMからFunctionsへのアクセスは従来のURL(https://<function>.azurewebsites.net)のまま、DNS解決だけがプライベートIPアドレスに向くよう設計しました。

APIM Standard v2のOutbound V-Net Integrationを活用することで、APIMゲートウェイ自体はパブリックのままクライアント接続を受けつつ、バックエンドへの通信はV-Net経由で行う混合構成を実現しました。NSG(Network Security Group)によりAPIMサブネットからAzure Storageへの送信通信を許可し、委任サブネット(Microsoft.Web/serverFarms)の要件も満たした運用可能なアーキテクチャを構築しました。

このV-Net閉域化により、前回のAPI管理機能(システムキー秘匿、SSEストリーミング最適化、監査ログ)を維持しながら、さらなるセキュリティ強化を達成しました。MCPサーバーは完全にプライベートネットワーク内に隠蔽され、Defense in Depthの原則に従ってインターネットの攻撃面積を最小化できました。クライアントからの接続方式は変わらず、運用面でのセキュリティと可観測性を両立した実用的なアーキテクトとなりました。


本シリーズを通じて、MCPの基礎からV-Net構成の実装まで段階的に学習できました。皆さんのMCPプロジェクトの参考になれば幸いです!


連載ナビ

参考資料

Discussion