Chapter 04 · Platform

Networking & Security

Defense in depth: VNet isolation, private endpoints, Entra ID integration, Key Vault for secrets, and the HTTP security headers that protect the agent runtime.

VNet topology

The recommended production layout uses a single VNet with four purpose-specific subnets. Each subnet isolates a class of traffic — compute workloads, data-plane private endpoints, AI service endpoints, and platform services — so that Network Security Groups (NSGs) can enforce least-privilege rules at the subnet boundary.

VNet: vnet-harness (10.0.0.0/16)
Subnet Layout
snet-compute
Container Apps Environment
10.0.1.0/24
snet-data
Private endpoints: graph backend, Azure SQL, Blob Storage
10.0.2.0/24
|                                |
snet-ai
Private endpoints: Azure OpenAI, AI Search
10.0.3.0/24
snet-platform
Key Vault, App Insights private link
10.0.4.0/24
i
VNet is optional for non-production

VNet integration is optional for dev/staging. Container Apps supports VNet injection for production workloads, but running without a VNet is simpler and cheaper for non-production environments.

Private endpoints

Private endpoints keep data-plane traffic on the Azure backbone. Each service gets its own private endpoint in the appropriate subnet, with a corresponding Private DNS Zone for name resolution.

Service Private DNS Zone Purpose
Azure OpenAI privatelink.openai.azure.com LLM + embedding calls stay on the backbone
AI Search privatelink.search.windows.net RAG queries never hit the public internet
Knowledge graph backend privatelink.postgres.database.azure.com (Azure DB for PostgreSQL Flexible Server) — Neo4j on AKS uses a private cluster IP instead Knowledge graph reads/writes
Blob Storage privatelink.blob.core.windows.net Document ingestion, FAISS indices
Key Vault privatelink.vaultcore.azure.net Secret retrieval at startup

Bicep example

A private endpoint for Azure OpenAI. The same pattern applies to every service in the table above — change the privateLinkServiceId and groupIds for the target resource.

bicep
resource openAiPrivateEndpoint 'Microsoft.Network/privateEndpoints@2024-01-01' = {
  name: 'pe-openai'
  location: location
  properties: {
    subnet: { id: aiSubnet.id }
    privateLinkServiceConnections: [
      {
        name: 'openai-connection'
        properties: {
          privateLinkServiceId: openAiAccount.id
          groupIds: ['account']
        }
      }
    ]
  }
}

Entra ID integration

The harness uses three identity scenarios, each addressing a different trust boundary.

1. User authentication

Users sign in via Entra ID using the OpenID Connect (OIDC) protocol. The Angular/React dashboard uses MSAL.js with PKCE to acquire tokens client-side. The API validates JWT tokens on every request. An Entra ID App Registration is required with configured redirect URIs and API scopes.

2. Managed Identity (service-to-service)

Container Apps get a system-assigned managed identity. This identity authenticates to Azure OpenAI, Key Vault, Blob Storage, and AI Search without any stored credentials. The following role assignments are required:

Role Target Resource
Cognitive Services OpenAI User Azure OpenAI
Key Vault Secrets User Key Vault
Storage Blob Data Reader Blob Storage
Search Index Data Reader AI Search

3. MCP Server JWT auth

External agents connecting to the MCP server authenticate with JWT bearer tokens. The MCP server validates issuer, audience, and signing key. Configuration is managed via AppConfig.MCP.Server.Authentication.

!
Never use API keys in production

Managed Identity eliminates secret rotation burden and removes the risk of key leakage. API keys are acceptable only for local development. In production, every service-to-service call should use Managed Identity or workload identity federation.

MCP server exposure

How you expose the MCP server depends on who needs to reach it.

Internal consumers (same VNet)

Use internal ingress on Container Apps. MCP clients connect via the VNet-internal FQDN. No public IP is allocated. This is the default for agents running within your infrastructure.

External consumers (other organizations/services)

Use external ingress + Azure Front Door with WAF. JWT auth is mandatory. Rate limiting is recommended to prevent abuse. Front Door provides DDoS protection, TLS termination, and geographic routing.

CORS policy

Explicit allowlist only. Never wildcard in production. The allowed origins must match the exact scheme and host of your dashboard deployment.

json
{
  "MCP": {
    "Server": {
      "Transport": "Http",
      "Port": 8082,
      "Authentication": {
        "Authority": "https://login.microsoftonline.com/{tenantId}/v2.0",
        "Audience": "api://harness-mcp",
        "ValidIssuers": ["https://sts.windows.net/{tenantId}/"]
      },
      "Cors": {
        "AllowedOrigins": ["https://your-app.azurecontainerapps.io"]
      }
    }
  }
}

HTTP security headers

Every response from the harness includes these headers. They form the browser-side layer of the defense-in-depth strategy.

Header Value Why
X-Frame-Options DENY Prevents clickjacking
X-Content-Type-Options nosniff Prevents MIME-type sniffing
Strict-Transport-Security max-age=31536000; includeSubDomains Forces HTTPS
Content-Security-Policy default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' fonts.googleapis.com Prevents XSS
Referrer-Policy strict-origin-when-cross-origin Controls referrer leakage
Permissions-Policy camera=(), microphone=(), geolocation=() Disables unused browser APIs
Where these are configured

These headers are configured in the ASP.NET Core middleware pipeline. See Infrastructure.Common/Middleware/SecurityHeadersMiddleware.cs in the Developer Guide for the implementation.

Key Vault configuration

All secrets — API keys, connection strings, certificates — live in Azure Key Vault. The harness loads them at startup via the Azure Key Vault configuration provider, which integrates directly with the .NET configuration system.

Secrets map to configuration paths using -- as a delimiter. For example, a Key Vault secret named AI--AgentFramework--AzureOpenAI--ApiKey maps to the configuration path AI:AgentFramework:AzureOpenAI:ApiKey. This convention means you can reference any configuration value as a Key Vault secret without changing application code.

AI--AgentFramework--AzureOpenAI--ApiKey
Key Vault secret name. Double hyphens replace the : separator used in .NET configuration paths.
AI:AgentFramework:AzureOpenAI:ApiKey
Configuration path. The Key Vault provider automatically translates -- to : at load time.

The container app authenticates to Key Vault using its system-assigned managed identity with the Key Vault Secrets User role. No connection string or client secret is needed — the identity is assigned at the infrastructure level.