Skip to content

UDR Routing Design — Hub-Spoke Through pvtfw

Purpose

Documents the User-Defined Route (UDR) strategy that forces all spoke-to-spoke traffic through the PrivateFW (pvtfw) NVA. Authoritative for understanding why specific route table entries exist, which spoke pairs are direct-peered (no NVA), and why asymmetric routing causes stateful firewall drops.

Cross-reference:

  • architecture/network-topology.md — VNET CIDR ranges
  • architecture/two-firewall-architecture.md — firewall roles
  • azure-infra/identity/net_vnet.tf — identity route table (authoritative)
  • azure-infra/prod/net_vnet.tf — prod route table
  • azure-infra/dev/net_vnet.tf — dev route table

Design Principle

All spoke-to-spoke traffic must transit pvtfw (10.98.2.4) so the Palo Alto stateful engine sees both directions of every session. If the outbound path uses pvtfw but the return path bypasses it (asymmetric routing), PA drops the return packets as out-of-state.

Asymmetric routing = NLA/RDP failures. Domain auth, RDP NLA, and Kerberos all require a completed TCP handshake. If either direction misses pvtfw, the connection never completes.


pvtfw as the Hub

                    INTERNET

                  ┌────┴────┐
                  │ PublicFW│  10.99.0.0/16
                  └────┬────┘
                       │ peering
                  ┌────┴────┐
                  │ pvtfw   │  10.98.0.0/16   ← all spoke-to-spoke
                  │(hub NVA)│    trust: 10.98.2.4
                  └──┬──┬──┘
          ┌──────────┘  └──────────┐
     ┌────┴────┐              ┌────┴────┐
     │Identity │              │  Prod   │
     │10.10.0.0│              │10.20.0.0│
     │  /16    │              │  /16    │
     └────┬────┘              └────┬────┘
          │                        │
     ┌────┴────┐              ┌────┴────┐
     │   Dev   │              │  RODC   │
     │10.30.0.0│              │10.11.0.0│
     │  /16    │              │  /16    │
     └─────────┘              └─────────┘

Each spoke VNet has a default route (0.0.0.0/0 → 10.98.2.4) and explicit spoke-to-spoke routes (per destination VNET CIDR → 10.98.2.4). This overrides Azure's system routes, which would create a direct VNet peering path that bypasses pvtfw.


Route Table Entries — Identity VNet (vnet-identity, 10.10.0.0/16)

Route NameDestinationNext HopNotes
default-to-identity0.0.0.0/010.98.2.4 (pvtfw)Default egress via pvtfw
to-dev-spoke10.30.0.0/1610.98.2.4 (pvtfw)Dev AD auth must transit pvtfw

Removed routes (with reason)

RouteReason Removed
to-prod-spokeOriginally removed in favor of a direct identity↔prod peering; that peering has since also been removed — identity→prod now rides the 0.0.0.0/0 → 10.98.2.4 default (see § Direct-Peering Exceptions, retired)
to-dashboard-spoke10.5.0.0/16 decommissioned — vnet-dashboard removed (note: prod still carries this route in code; tracked as an azure-infra issue)
to-svcs-spokeSame history as to-prod-spoke — direct peering later removed; legacy↔identity flows via pvtfw default routes

Direct-Peering Exceptions (RETIRED — June 2026)

This section previously documented intentional direct peerings (identity↔prod for Kerberos latency, identity↔svcs for legacy auth). Both have since been removed — the removal comments live in identity/net_vnet.tf:190-196 and prod/net_vnet.tf ("identity_to_prod removed — pvtfw routes identity↔prod"). There are no remaining direct spoke↔spoke peerings: every spoke pair traverses a PA. Identity↔prod traffic now follows the 0.0.0.0/0 → 10.98.2.4 defaults on both sides.

Historical caution preserved for context: while those peerings existed, adding a matching UDR would have black-holed traffic at pvtfw (no session state). With the peerings gone, the inverse now applies — do not re-add direct peerings without also removing the pvtfw routes, or you recreate the bypass this design eliminates.


Why 10.99.2.4 (CIRIUS-SVCS-FW) Is Wrong

The CIRIUS-SVCS-FW was the original firewall before the two-firewall architecture. Its trust IP (10.99.2.4) still appears in old or misconfigured route tables. Any UDR pointing at 10.99.2.4 instead of 10.98.2.4 (pvtfw) will cause asymmetric routing because:

  1. Outbound dev → DC: pvtfw (10.98.2.4) — PA creates session state
  2. Return DC → dev: CIRIUS-SVCS-FW (10.99.2.4) — PA sees unknown session, drops

This manifests as NLA authentication failures on RDP. The fix is correcting the next_hop_in_ip_address to 10.98.2.4 in the route table resource.


Twingate + Dev VNet Access Pattern

Dev VNet (10.30.0.0/16) does not have a direct Twingate resource. Direct Twingate resources for 10.30.x.x were removed because they created /16 system routes in the Twingate connector subnet that bypassed both PAs.

User → Twingate → pvtfw (pvt.ciriusgroup.com:7000, GlobalProtect)
                     └→ 10.30.x.x (dev servers)

Access path: Twingate → pvtfw GlobalProtect → RDP to target IP.


Adding a New Spoke

When adding a new VNet that needs spoke-to-spoke connectivity:

  1. Peer the new VNet to vnet-privatefw (bidirectional, allow forwarded traffic)
  2. Add a UDR entry on every other spoke pointing at the new CIDR → 10.98.2.4
  3. Add a default route on the new spoke's route table → 10.98.2.4
  4. Add spoke-to-spoke routes on the new spoke for each existing spoke CIDR → 10.98.2.4
  5. If direct peering is intended (no NVA), document it in this file under Direct-Peering Exceptions and do not add cross-spoke UDRs for that pair

Last updated: May 2026 — reflects PR #1248 (identity route fix) and removal of decommissioned 10.5.0.0/16 routes.

Internal use only — Cirius Group