Hub & Spoke Active Active
- Anand Nerurkar
- Sep 12
- 6 min read
1 — Topology (text diagram — two regions)
Global Edge
└─ Azure Front Door (global) [public]
├─ Backend pool: APIM-Mumbai (private origin via Private Link)
└─ Backend pool: APIM-Chennai (private origin via Private Link)
Mumbai Region (Region A)
├─ Hub VNet (10.0.0.0/16) - Mumbai-Hub
│ ├─ AzureFirewallSubnet (10.0.0.4/26) ← Azure Firewall (egress/NVA)
│ ├─ BastionSubnet (10.0.0.64/27) ← Bastion for admin
│ ├─ SharedServicesSubnet (10.0.1.0/24) ← DNS forwarders, monitoring agents
│ ├─ KeyVault Private Endpoint (privatelink.vaultcore.azure.net)
│ └─ LogAnalytics Workspace (collector)
│
└─ Spoke-Prod VNet (10.1.0.0/16) - Mumbai-Prod-Spoke
├─ Public-Subnet (10.1.1.0/24) ← optional App Gateway if used
├─ APIM-Subnet (10.1.2.0/26) ← APIM (internal VNet / private endpoint)
├─ AKS-Subnet (10.1.3.0/23) ← AKS private cluster (nodes & pods)
├─ Data-Subnet (10.1.5.0/25) ← Private Endpoints: Postgres, Cosmos, Storage
├─ Messaging-Cache-Subnet (10.1.5.128/25)
│ ├─ Kafka (internal LB + brokers)
│ └─ Redis (Private Endpoint)
└─ Reserved-Subnet (10.1.6.0/24)
Chennai Region (Region B)
├─ Hub VNet (10.10.0.0/16) - Chennai-Hub (same components as Mumbai Hub)
└─ Spoke-Prod VNet (10.11.0.0/16) - Chennai-Prod-Spoke
├─ Public-Subnet (10.11.1.0/24)
├─ APIM-Subnet (10.11.2.0/26)
├─ AKS-Subnet (10.11.3.0/23)
├─ Data-Subnet (10.11.5.0/25)
├─ Messaging-Cache-Subnet (10.11.5.128/25)
└─ Reserved-Subnet (10.11.6.0/24)
Connectivity
- Hub ↔ Spoke peering (regional, same-region peering)
- Front Door → APIM via Private Link (private origin)
- UDRs in spoke subnets route 0.0.0.0/0 → Azure Firewall private IP in Hub
- Private DNS zones for privatelink.* and service private fqdn linked to hub & spokes
2 — High-level traffic flow (single region, Mumbai example)
User → Azure Front Door (public) → Front Door selects APIM-Mumbai (private origin).
Front Door → Private Link → APIM in APIM-Subnet (private).
APIM → internal App Gateway or internal LB → AKS Ingress (AKS-Subnet).
AKS microservice → Data-Subnet private endpoints (Postgres:5432, Cosmos:443) or Infra-Subnet (Redis:6380, Kafka:9093).
Outbound to third parties → routed by UDR to Azure Firewall in Hub → egress to Internet (controlled by Firewall application rules).
3 — NSG templates (apply per subnet). Priorities use Azure style (lower = higher priority)
Note: tighten Source/Dest CIDRs to specific ranges when integrating with on-prem or partner IPs. Use service tags where helpful (e.g., Internet, VirtualNetwork, AzureLoadBalancer).
APIM-Subnet NSG (apply to APIM-Subnet)
Name | Priority | Source | Destination | Protocol | Port | Action | Notes |
Allow-FrontDoor-PrivateLink | 100 | FrontDoorPrivateLinkSubnetIP(s) or ServiceTag (private-link) | APIM-Subnet | TCP | 443 | Allow | Allow FD -> APIM private origin (exact range from Private Link) |
Allow-Admin-Bastion | 200 | HubBastionSubnet (10.0.0.64/27) | APIM-Subnet | TCP | 3443 / 3444 / management ports | Allow | Admin/management only |
Allow-AKS-Health | 300 | AKS-Subnet (10.1.3.0/23) | APIM-Subnet | TCP | 443 | Allow | For APIM → AKS backend calls if needed (or reverse) |
Deny-Internet-Inbound | 4090 | Internet | APIM-Subnet | * | * | Deny | Block direct internet access |
Deny-All | 4096 | * | * | * | * | Deny | Catch-all |
AKS-Subnet NSG (apply to AKS-Subnet)
Name | Priority | Source | Destination | Protocol | Port | Action | Notes |
Allow-APIM-To-Ingress | 100 | APIM-Subnet | AKS-Subnet (Ingress LB IP) | TCP | 80,443 | Allow | APIM -> API ingress |
Allow-App-Private-Traffic | 110 | App-Private | AKS-Subnet | TCP | 10250, 10255 | Allow | kubelet/management if necessary (restrict to bastion too) |
Allow-Pods-To-Data | 200 | AKS-Subnet | Data-Subnet | TCP | 5432,443,6380,9093 | Allow | Postgres, Cosmos, Redis, Kafka |
Deny-Internet-Inbound | 4090 | Internet | AKS-Subnet | * | * | Deny | No public inbound |
Deny-All | 4096 | * | * | * | * | Deny | Catch-all |
Data-Subnet NSG (apply to Data-Subnet)
Name | Priority | Source | Destination | Protocol | Port | Action | Notes |
Allow-AKS-DB-Access | 100 | AKS-Subnet | Data-Subnet (PrivateEndpoints IPs) | TCP | 5432 | Allow | Postgres |
Allow-AKS-Cosmos | 110 | AKS-Subnet | Data-Subnet | TCP | 443 | Allow | Cosmos |
Allow-Bastion-Admin | 200 | HubBastionSubnet | Data-Subnet | TCP | 5432/443 | Allow | Admin only |
Deny-Internet | 4090 | Internet | Data-Subnet | * | * | Deny | Block public access |
Deny-All | 4096 | * | * | * | * | Deny | Catch-all |
Messaging-Cache-Subnet NSG (apply to Messaging-Cache)
Name | Priority | Source | Destination | Protocol | Port | Action | Notes |
Allow-AKS-Kafka-Redis | 100 | AKS-Subnet | Messaging-Cache | TCP | 9092,9093,6380 | Allow | Broker + TLS Redis |
Allow-Admin | 200 | HubBastionSubnet | Messaging-Cache | TCP | 22/2181 | Allow | SSH/ZK admin if self-managed |
Deny-Internet | 4090 | Internet | Messaging-Cache | * | * | Deny | No public |
Deny-All | 4096 | * | * | * | * | Deny | Catch-all |
Public-Subnet NSG (if you host App Gateway here)
Name | Priority | Source | Destination | Protocol | Port | Action | Notes |
Allow-Internet-HTTP-HTTPS | 100 | Internet | Public-Subnet | TCP | 80,443 | Allow | For App Gateway public IP |
Allow-FrontDoor-Health | 110 | FrontDoorServiceTag | Public-Subnet | TCP | health_port | Allow | Probes to App Gateway if needed |
Deny-All-Other | 4096 | * | * | * | * | Deny | Minimize exposure |
4 — UDR (User Defined Routes) policies (apply to spoke private subnets: AKS, Data, Messaging-Cache, APIM if required)
Objective: force all egress to Azure Firewall in Hub for inspection & logging.
Assume Azure Firewall private IP (hub) = 10.0.0.4 (example).
Applied To Subnet | Address Prefix | Next Hop IP / Type | Notes |
AKS-Subnet | 0.0.0.0/0 | Virtual Appliance → 10.0.0.4 | Force egress through Hub Firewall |
Data-Subnet | 0.0.0.0/0 | Virtual Appliance → 10.0.0.4 | Ensures DB replicas/backup egress goes via firewall |
Messaging-Cache | 0.0.0.0/0 | Virtual Appliance → 10.0.0.4 | Kafka external replication or connector egress controlled |
APIM-Subnet (if outbound to third parties) | 0.0.0.0/0 | Virtual Appliance → 10.0.0.4 | APIM calls egress via firewall |
Optional internal route:
Applied To Subnet | Address Prefix | Next Hop | Notes |
AKS-Subnet | 10.1.5.0/24 (Data Subnet) | None (use VNet routing) | Direct east-west traffic kept local |
5 — Azure Firewall rules (recommended set). Use Firewall Policy + application rules + network rules + FQDN tags.
Firewalls commonly use: Network rules (L3-L4), Application rules (FQDN for HTTPS), DNAT rules (if you publish internal endpoints), and Threat Intel. Prioritize logging and analytics.
A — DNAT rules (if exposing admin endpoints via firewall NAT)
Only if required for a specific admin jump host:
Name: DNAT-Bastion-Access
Source IP: specific admin IP(s) (on-prem / known VPN)
Protocol: TCP
Source Port: *
Destination IP: PublicIP (firewall) -> Translated to Bastion internal IP (10.0.0.64:3389/22)
Action: Allow
(Prefer Bastion rather than DNAT; avoid exposing management via DNAT unless absolutely necessary.)
B — Network rules (L3-L4)
Name | Priority | Source | Destination | Protocol | Port | Action | Notes |
Allow-VNet-to-AzureServices | 100 | VirtualNetwork | Service Tag: AzureKeyVault | TCP | 443 | Allow | For Key Vault outbound if hub makes calls |
Allow-OnPrem-to-AKS | 200 | OnPremRange(s)/VPN | AKS-Subnet | TCP | 443,22 | Allow | Admin from trusted on-prem only |
Allow-APIM-to-ExternalPartners | 300 | APIM-Subnet | ExternalPartnerIPs | TCP | 443 | Allow | For API backend partner calls |
Deny-All-Internet | 4090 | * | * | * | * | Deny | Enforce explicit allow rules |
C — Application rules (FQDN-based) — recommended for HTTPS outbound
Name | Priority | Source | FQDNs / Tags | Protocol | Action | Notes |
Allow-CRBureau | 100 | AKS-Subnet | HTTPS (443) | Allow | Credit bureau API egress | |
Allow-SMS-Gateway | 110 | APIM-Subnet | HTTPS | Allow | SMS & OTP provider | |
Allow-Container-Registry | 120 | VirtualNetwork | HTTPS | Allow | ACR pulls for AKS | |
Allow-DataExfil-Protection | 130 | VirtualNetwork | HTTPS | Allow | Limit egress comms | |
Deny-Other-FQDNs | 4090 | VirtualNetwork | * | HTTPS | Deny | Block all other FQDNs |
D — Threat intelligence & logging
Enable Threat Intel-based blocking (deny known-malicious IPs).
Enable diagnostic settings to send Firewall logs, Application logs, and Network rules logs to Log Analytics in Hub + EventHub/Sentinel.
6 — Firewall NAT / Outbound SNAT considerations
When AKS pods initiate outbound, Azure Firewall performs SNAT; ensure SNAT port capacity (use multiple public IPs on firewall) if egress scale is high.
Use service tags in App/Gateway to reduce rule count (e.g., Service Tag AzureKubernetesService for managed service egress where supported).
7 — DNS & Private DNS zones (critical)
Create Private DNS zones for:
Cosmos private link hostnames
Link these private DNS zones to Hub + Spoke VNets.
Ensure DNS forwarder in Hub resolves Azure PaaS names; disable public DNS resolution for privatelink names.
8 — Logging, monitoring & auditing
Forward diagnostics for: APIM (gateway logs), AKS (container logs via Fluent Bit), Azure Firewall, App Gateway, Key Vault to central Log Analytics workspace in Hub.
Enable retention & role-limited access in Log Analytics / Sentinel.
Configure alerts:
High DB replication lag; Postgres replica lag > threshold
Kafka consumer lag > threshold
High number of 5xx responses at APIM
Azure Firewall deny counts (possible exfil attempt)
NSG flow log anomalies
9 — Suggested verification / smoke tests (run after deploy)
From a jump VM in Hub:
nslookup mypostgres.privatelink.postgres.database.azure.com → should return private IP in Data-Subnet.
curl -vk https://<apim-private-host>/health via Private Link path to ensure Front Door origin probe would pass.
From AKS pod (kubectl run debug):
psql -h <postgres-private-ip> -U <user> → connect to Postgres
redis-cli -h <redis-private-ip> -p 6380 --tls → verify TLS
Front Door test:
Hit api.mybank.com from public internet in Mumbai and Chennai — ensure traffic flows to correct regional APIM.
Firewall logs:
Confirm egress attempts appear in firewall logs and blocked FQDNs are recorded.
10 — Operational runbook highlights (short)
Onboard a new partner (API integration):
Add partner FQDN to Firewall Application Allow rule (with approval).
Add NSG rule for APIM to allow outbound to partner IP/FQDN.
Update monitoring dashboards for that integration.
Promote PostgreSQL replica (DR):
Validate replica lag < threshold.
Promote replica via az CLI or portal.
Update DNS alias (if you use alias) or update connection strings in APIM (use Key Vault reference + short TTL CNAME for quick swap).
Run smoke tests.
Perform AKS maintenance:
Drain nodes one nodepool at a time.
Validate health of microservices & no increased 5xx counts.
If pod cannot reach DB, verify Private DNS resolution and UDR/firewall rules.
11 — Compact checklist for security review
APIM in own subnet; public access disabled; private link configured and tested.
AKS is private cluster; no public node IPs; control plane private.
All managed PaaS have private endpoints in Data/Messaging subnets.
NSGs applied per subnet with deny-all catchall.
UDRs route outbound through Azure Firewall in hub.
Application rules in firewall limited to required FQDNs and partners.
Private DNS zones linked to hub & spokes.
Logs forwarded to central Log Analytics and SIEM.
Health probes for Front Door use private origin verified.
.png)

Comments