Azure App Service配置私有端点与NAT网关实现静态出站IP
我正在开发一个个人项目,包含2个以Linux Web App托管的ASP.NET Core Web API,近期计划新增一个React前端作为另一Linux Web App。基础设施设计涉及为后端Web App配置Application Insights、使用Azure Key Vault,并集成共享SQL数据库。我希望将资源部署在私有网络中(不暴露于公网),同时为其中一个需对接第三方API的API配置NAT网关以提供静态出站IP,该第三方API要求将特定静态出站IP加入白名单。
我了解到多种使用私有端点、网络安全组管理流量及用户定义路由引导流量的方案,但不确定这些方法能否与我的NAT网关实现(尤其是静态出站IP部分)连贯整合。
基于此,我有以下技术问题寻求指导:
- 如何将Application Insights、Key Vault及SQL数据库的私有端点与现有NAT网关实现的静态出站IP方案整合?具体实现方式是什么?
- 结合当前需求,现有Terraform代码中还缺少哪些可提升资源安全性或功能性的配置?
现有Terraform代码
resource "azurerm_resource_group" "rg" { name = "${local.basename}-rg" location = "East US" } resource "azurerm_service_plan" "asp" { name = "${local.basename}-asp" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location os_type = "Linux" sku_name = "S1" } resource "azurerm_linux_web_app" "app" { name = "${local.basename}-app" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location service_plan_id = azurerm_service_plan.asp.id site_config { vnet_route_all_enabled = true # WEBSITE_VNET_ROUTE_ALL } identity { type = "SystemAssigned" } } resource "azurerm_virtual_network" "vnet" { name = "${local.basename}-vnet" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location address_space = ["10.0.0.0/16"] } resource "azurerm_subnet" "snet" { name = "${local.basename}-linuxapp-snet" resource_group_name = azurerm_resource_group.rg.name virtual_network_name = azurerm_virtual_network.vnet.name address_prefixes = ["10.0.1.0/24"] delegation { name = "delegation" service_delegation { name = "Microsoft.Web/serverFarms" actions = ["Microsoft.Network/virtualNetworks/subnets/action"] } } } resource "azurerm_subnet_nat_gateway_association" "example" { subnet_id = azurerm_subnet.snet.id nat_gateway_id = azurerm_nat_gateway.ng.id } resource "azurerm_public_ip" "pip" { name = "${local.basename}-pip" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location allocation_method = "Static" sku = "Standard" zones = ["1"] } resource "azurerm_nat_gateway" "ng" { name = "${local.basename}-ng" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location sku_name = "Standard" idle_timeout_in_minutes = 10 zones = ["1"] } resource "azurerm_nat_gateway_public_ip_association" "association" { nat_gateway_id = azurerm_nat_gateway.ng.id public_ip_address_id = azurerm_public_ip.pip.id } resource "azurerm_app_service_virtual_network_swift_connection" "example" { app_service_id = azurerm_linux_web_app.app.id subnet_id = azurerm_subnet.snet.id }
问题1:私有端点与NAT网关静态出站IP的整合实现
核心逻辑
私有端点让Web App通过VNet内网访问Azure PaaS服务(App Insights、Key Vault、SQL),NAT网关仅负责第三方API的出站流量,两者通过用户定义路由(UDR)区分流量走向,避免冲突。
具体实现步骤
创建专用子网用于私有端点
为PaaS服务的私有端点创建独立子网,需禁用私有端点网络策略,避免与Web App子网的NAT规则冲突:resource "azurerm_subnet" "private_endpoints" { name = "${local.basename}-pe-snet" resource_group_name = azurerm_resource_group.rg.name virtual_network_name = azurerm_virtual_network.vnet.name address_prefixes = ["10.0.2.0/24"] private_endpoint_network_policies = "Disabled" }为各PaaS服务创建私有端点
- SQL数据库私有端点:关联SQL服务器,配置内网访问通道
resource "azurerm_private_endpoint" "sql" { name = "${local.basename}-sql-pe" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location subnet_id = azurerm_subnet.private_endpoints.id private_service_connection { name = "${local.basename}-sql-psc" private_connection_resource_id = azurerm_mssql_server.sql.id subresource_names = ["sqlServer"] is_manual_connection = false } }- Key Vault私有端点:关联Key Vault,支持Web App系统身份访问
resource "azurerm_private_endpoint" "kv" { name = "${local.basename}-kv-pe" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location subnet_id = azurerm_subnet.private_endpoints.id private_service_connection { name = "${local.basename}-kv-psc" private_connection_resource_id = azurerm_key_vault.kv.id subresource_names = ["vault"] is_manual_connection = false } }- Application Insights私有端点:需同时关联API和 ingestion 子资源,确保遥测数据内网传输
resource "azurerm_private_endpoint" "app_insights" { name = "${local.basename}-ai-pe" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location subnet_id = azurerm_subnet.private_endpoints.id private_service_connection { name = "${local.basename}-ai-psc" private_connection_resource_id = azurerm_application_insights.ai.id subresource_names = ["api", "ingestion"] is_manual_connection = false } }配置用户定义路由(UDR)区分流量
在Web App子网关联UDR,分别定义两类流量的走向:- 访问Azure PaaS服务的流量:引导至VNet内网,走私有端点
- 访问第三方API的流量:引导至NAT网关,使用静态公网IP出站
resource "azurerm_route_table" "web_app_udr" { name = "${local.basename}-web-udr" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location disable_bgp_route_propagation = true } # 路由到私有端子网内的PaaS服务 resource "azurerm_route" "paas_private" { name = "${local.basename}-paas-route" resource_group_name = azurerm_resource_group.rg.name route_table_name = azurerm_route_table.web_app_udr.name address_prefix = "10.0.2.0/24" next_hop_type = "VnetLocal" } # 路由到第三方API,走NAT网关 resource "azurerm_route" "third_party_api" { name = "${local.basename}-third-party-route" resource_group_name = azurerm_resource_group.rg.name route_table_name = azurerm_route_table.web_app_udr.name address_prefix = "x.x.x.x/32" # 替换为第三方API公网IP next_hop_type = "NatGateway" next_hop_in_ip_address = azurerm_nat_gateway.ng.id } # 关联UDR到Web App子网 resource "azurerm_subnet_route_table_association" "web_snet_udr" { subnet_id = azurerm_subnet.snet.id route_table_id = azurerm_route_table.web_app_udr.id }流量验证
- Web App访问SQL/Key Vault/App Insights时,通过私有端点走内网,不经过NAT网关
- 访问第三方API时,流量通过UDR引导到NAT网关,使用指定的静态公网IP出站
问题2:现有Terraform代码缺失的安全/功能性配置
安全类配置
网络安全组(NSG)限制子网流量
为Web App子网和私有端子网添加NSG,仅允许必要的入站/出站流量:resource "azurerm_network_security_group" "web_snet_nsg" { name = "${local.basename}-web-nsg" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location security_rule { name = "AllowVnetInbound" priority = 100 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "80,443" source_address_prefix = "10.0.0.0/16" destination_address_prefix = "*" } security_rule { name = "AllowThirdPartyOutbound" priority = 100 direction = "Outbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "*" source_address_prefix = "*" destination_address_prefix = "x.x.x.x/32" # 第三方API IP } } # 关联NSG到Web App子网 resource "azurerm_subnet_network_security_group_association" "web_snet_nsg" { subnet_id = azurerm_subnet.snet.id network_security_group_id = azurerm_network_security_group.web_snet_nsg.id }禁用Web App公网访问
确保Web App仅通过VNet内网访问,不暴露公网:resource "azurerm_linux_web_app" "app" { # 保留现有配置 public_network_access_enabled = false }Key Vault访问策略
为Web App的系统身份分配Key Vault的最小权限,避免硬编码密钥:resource "azurerm_key_vault_access_policy" "web_app" { key_vault_id = azurerm_key_vault.kv.id tenant_id = data.azurerm_client_config.current.tenant_id object_id = azurerm_linux_web_app.app.identity[0].principal_id key_permissions = ["Get", "List"] secret_permissions = ["Get", "List"] certificate_permissions = ["Get", "List"] }禁用SQL公网访问
仅允许私有端点和VNet内流量访问SQL数据库:resource "azurerm_mssql_server" "sql" { # 保留现有配置 public_network_access_enabled = false private_endpoint_connections { id = azurerm_private_endpoint.sql.id } }
功能性配置
多Web App资源定义
现有代码仅定义了一个Web App,需添加第二个API和React前端的Web App资源,关联到服务计划并加入VNet。Application Insights集成
为Web App配置App Insights扩展,启用遥测收集:resource "azurerm_application_insights" "ai" { name = "${local.basename}-ai" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location application_type = "web" } resource "azurerm_linux_web_app" "app" { # 保留现有配置 app_settings = { APPLICATIONINSIGHTS_CONNECTION_STRING = azurerm_application_insights.ai.connection_string } }本地变量定义
补充local.basename的定义:locals { basename = "your-project-prefix" }区域冗余配置(可选)
若需更高可用性,可将NAT网关和公网IP配置为多区域:resource "azurerm_public_ip" "pip" { # 保留现有配置 zones = ["1", "2", "3"] } resource "azurerm_nat_gateway" "ng" { # 保留现有配置 zones = ["1", "2", "3"] }
内容的提问来源于stack exchange,提问作者nop

