31 ‐ Azure Hub & Spoke: Public Hub Private Spoke - SanjeevOCI/Study GitHub Wiki

Azure Hub & Spoke: Public Hub + Private Spoke

Goal

Centralize internet egress & admin in the Hub VNet, keep Spoke VNet private (no direct internet), while enabling:

  • Blob Storage / Azure PaaS privately (Private Endpoints or Service Endpoints)
  • OS updates / public repos via Hub NAT Gateway
  • Admin via Bastion in Hub
  • Logging, Monitoring, and Governance via Azure best practices

Quick Architecture

                  +--------------------+
                  |   On-Prem / VPN    |
                  | (Optional Future)  |
                  +---------+----------+
                            |
                            v
                     +-------------+
                     |   VPN/ER    |
                     | (Transit)   |
                     +------+------+  
                            |
+---------------------------+---------------------------+
|                                                       |   
v                                                       v    

+-------------------+ +-------------------+
| HUB VNet          | | SPOKE VNet        |
| 10.0.0.0/16       | | 10.1.0.0/16       |
|                   | |                   |
| +-------------+   | | +-------------+   |
| | Hub-Public  |<-------- Admin (SSH) ---->| Spoke-Priv  |
| | 10.0.1.0/24 |   | | | 10.1.1.0/24 |   |
| | Bastion     |   | | | VM (no PIP) |   |
| +-------------+   | | +-------------+   |
|                   | |                   |
| +-------------+   | |                   |
| | Hub-Priv    |   | |                   |
| | 10.0.2.0/24 |   | |                   |
| | NAT GW ---> IGW | |                   |
| +-------------+   | |                   |
|                   | | +-------------+   |
| +-------------+   | | | PEP-SPOKE   |   |
| | PEP-HUB(opt)|---X | | (Blob/PaaS) |   |
| +-------------+     | +-------------+   |
+-------------------+ +-------------------+
Subscription
└─ Resource Groups
   ├─ Network-Hub
   │  └─ VNet HUB 10.0.0.0/16
   │     ├─ Subnet Hub-Public 10.0.1.0/24 [IGW] (Bastion, optional tools)
   │     ├─ Subnet Hub-Private 10.0.2.0/24 [NAT path/NVA optional]
   │     ├─ IGW, NAT GW, VPN/ER, (PEP-HUB optional for hub workloads)
   └─ App-Spoke
      └─ VNet SPOKE 10.1.0.0/16
         ├─ Subnet Spoke-Private 10.1.1.0/24 (VMs, no public IPs)
         ├─ PEP-SPOKE (required for private PaaS access)
         └─ Hub-Spoke Peering

Key change vs standalone VNets:

  • NAT Gateway is in the Hub (not in Spoke).
  • Spoke sends 0.0.0.0/0 to Hub via peering (centralized egress).
  • Each VNet that needs private PaaS must have its own Private Endpoint or Service Endpoint (not transitive).
  • Logging & Monitoring enabled at NSG, Bastion, and VM level.

Addressing (example)

  • HUB VNet: 10.0.0.0/16

    • Hub-Public: 10.0.1.0/24
    • Hub-Private: 10.0.2.0/24
  • SPOKE VNet: 10.1.0.0/16

    • Spoke-Private: 10.1.1.0/24

Build Steps (High-Level)

A) Create Hub VNet (Internet-enabled)

  1. VNet (10.0.0.0/16) with subnets:

    • Hub-Public (10.0.1.0/24) for Bastion
    • Hub-Private (10.0.2.0/24) for NAT path / future NVA
  2. Gateways in Hub:

    • Internet Gateway (default Azure routing)
    • NAT Gateway
    • VPN Gateway/ER Gateway
    • (Optional) Private Endpoint in Hub if workloads need private PaaS
  3. Route Tables (Hub):

    • Hub-Public RT: 0.0.0.0/0 → IGW
    • Hub-Private RT:
      • 0.0.0.0/0 → NAT GW
      • (optional) Service Endpoints → PEP-HUB

B) Deploy Bastion in Hub

  • Place Azure Bastion in Hub-Public subnet.
  • Use Bastion session to reach Spoke VM (no public IP on Spoke VM).
  • Enable Bastion diagnostics for audit.

C) Create Spoke VNet (No Internet)

  1. VNet (10.1.0.0/16) with Spoke-Private (10.1.1.0/24)
  2. PEP-SPOKE (Private Endpoint/Service Endpoint) — required for Blob Storage/Azure PaaS
  3. Peer Spoke VNet to Hub VNet
  4. Enable NSG Flow Logs for visibility.

D) Hub-Spoke Peering Configuration

  • Create VNet Peering: Hub VNet & Spoke VNet
  • Route Propagation:
    • Spoke → Hub: 0.0.0.0/0 (for internet egress)
    • Hub → Spoke: 10.1.0.0/16 (for return traffic)
    • (Optional) Add on-prem later via VPN/ER

E) Spoke Route Table (attach to Spoke-Private)

  • 0.0.0.0/0 → Hub VNet (centralized egress via Hub NAT)
  • Service Endpoints → PEP-SPOKE (private PaaS)

F) NSGs

Spoke (subnet or NIC-level NSG):

  • Egress:
    • Service Tags: Storage, TCP 443 (Blob)
    • 0.0.0.0/0, TCP 443 (NAT path via Hub)
  • Ingress:
    • From Hub Bastion/VPN CIDR on admin ports (22/3389 as required)
    • No inbound from internet

Hub (Bastion + NAT path):

  • Bastion subnet: allow inbound from trusted admin IPs (SSH/RDP session establishment)
  • If using NVA/Azure Firewall: allow Spoke CIDR → NVA, and NVA → IGW as required

G) Spoke VM

  • Create VM in Spoke-Private, no public IP
  • Connect via Bastion (Hub-Public)

Verification (from Spoke VM via Bastion)

Blob Storage via Private Endpoint (private path):

az storage account show --name <storageaccount> --query privateEndpointConnections

Outbound updates via Hub NAT (public repos):

curl https://ifconfig.me       # shows Hub NAT GW public IP
sudo apt-get update && sudo apt-get install -y wget

No broad internet (if restricted to 443):

curl http://example.com:80     # should fail

Path sanity:

  • Spoke NIC → Effective Routes show:
    • 0.0.0.0/0 → Hub NAT
    • Service Endpoints → PEP-SPOKE
  • Hub NAT subnet RT → 0.0.0.0/0 → IGW

Why NAT GW (when Hub has IGW)?

  • NAT GW = outbound-only for private workloads; no unsolicited inbound possible.
  • Using IGW for Spoke traffic opens inbound paths unless heavily restricted (breaks “no-internet Spoke” posture).
  • Best practice:
    • Public subnets → IGW
    • Private subnets → NAT GW

Interview Soundbite

“We centralize egress by moving NAT to the Hub and point the Spoke’s 0.0.0.0/0 to the Hub via VNet peering.
This preserves the Spoke’s no-internet posture while enabling updates, and aligns with Azure’s hub-spoke design best practices.”


Common Pitfalls (and Fixes)

  • Trying to use Hub PEP for Spoke: Not supported (Private Endpoints are scoped per VNet).
  • Missing route propagation in peering: Spoke won’t see default route. Fix peering config.
  • Asymmetric routing with an NVA: Ensure return routes in Hub point back to Spoke CIDR and to IGW for internet.
  • Wrong route table attached to Spoke subnet: Double-check association.
  • NSG rules too open: Keep Spoke egress TCP/443 only; prefer NSGs for per-VM control.

Interview Nuggets (Prove Hands-On)

  • Gateways are VNet-level (NAT/PEP): create gateway → reference in subnet RT.
  • VNet Peering provides transit between Hub/Spoke; no transitive peering.
  • Private Endpoints are per VNet: every VNet needs its own.
  • Bastion in Hub Public for admin; Spoke VMs never get public IPs.
  • Effective routes & NSG Flow logs: verify NIC routes and use NSG diagnostics.
  • Egress least privilege: 443 only; switch to NVA/Azure Firewall for FQDN/domain allow-listing.
  • Enable Azure Policy and tags for governance.

Quick Checklist

  • Hub: IGW, NAT GW, VPN/ER, Bastion in Hub-Public
  • Spoke: PEP-SPOKE, VNet Peering to Hub; no IGW
  • Peering: Export 0.0.0.0/0 (Hub → Spoke), import Spoke CIDR (Spoke → Hub)
  • Routes (Spoke): 0.0.0.0/0 → Hub, Service Endpoints → PEP-SPOKE
  • Routes (Hub): Hub-Public → IGW, Hub-Private → NAT GW (+ returns to Spoke CIDR)
  • Security: Spoke egress TCP/443 to PEP + Hub; admin from Bastion/VPN only
  • Governance: Enable Azure Policy, tagging, and flow logs
  • Validate:
    az storage account show --name <storageaccount>
    curl ifconfig.me
    apt-get update