<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Azure VNet on Brewed in the Cloud by Chris Hailes</title><link>https://blog.brewedinthecloud.com/tags/azure-vnet/</link><description>Recent content in Azure VNet on Brewed in the Cloud by Chris Hailes</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Thu, 30 Apr 2026 00:00:00 +1100</lastBuildDate><atom:link href="https://blog.brewedinthecloud.com/tags/azure-vnet/rss.xml" rel="self" type="application/rss+xml"/><item><title>How Azure Networking Quietly Enables Lateral Movement</title><link>https://blog.brewedinthecloud.com/p/east-west-networking-quietly-enables-lateral-movement/</link><pubDate>Thu, 30 Apr 2026 00:00:00 +1100</pubDate><guid>https://blog.brewedinthecloud.com/p/east-west-networking-quietly-enables-lateral-movement/</guid><description>&lt;p&gt;Most Azure estates don’t &lt;em&gt;feel&lt;/em&gt; flat.&lt;/p&gt;
&lt;p&gt;They have hubs, spokes, subnets, and diagrams that suggest intent and order. On paper, things look segmented. Contained. Reasonable.&lt;/p&gt;
&lt;p&gt;Yet once something unwanted lands inside, movement often feels frictionless like walking through an open-plan office where every door politely steps aside.&lt;/p&gt;
&lt;p&gt;That isn’t because someone forgot a firewall rule.&lt;br&gt;
It’s because Azure networking is doing exactly what it was designed to do.&lt;/p&gt;
&lt;h2 id="the-mental-model"&gt;The Mental Model
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Common assumption:&lt;/strong&gt;&lt;br&gt;
Internal Azure networks are segmented unless you explicitly connect them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why it breaks:&lt;/strong&gt;&lt;br&gt;
Azure optimises for &lt;em&gt;reachability, symmetry, and operational correctness&lt;/em&gt;. Once you create connectivity, Azure assumes you meant it, and it works hard to preserve stable, symmetric paths between everything involved.&lt;/p&gt;
&lt;p&gt;East–west movement isn’t an attacker trick.&lt;br&gt;
It’s an architectural outcome.&lt;/p&gt;
&lt;h2 id="how-it-really-works"&gt;How It Really Works
&lt;/h2&gt;&lt;p&gt;Let’s talk about behaviour, not marketing diagrams.&lt;/p&gt;
&lt;h3 id="symmetric-hub-routing-is-the-real-enabler"&gt;Symmetric hub routing is the real enabler
&lt;/h3&gt;&lt;p&gt;Azure VNet peering itself is non‑transitive. On its own, it doesn’t flatten anything.&lt;/p&gt;
&lt;p&gt;Transitivity appears when architects (often correctly, at the time):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Centralise routing through a hub&lt;/li&gt;
&lt;li&gt;Introduce Azure Firewall or NVAs&lt;/li&gt;
&lt;li&gt;Apply default UDRs (&lt;code&gt;0.0.0.0/0&lt;/code&gt;) from spokes to the hub&lt;/li&gt;
&lt;li&gt;Expect symmetric paths for reliability and troubleshooting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At that point, the hub stops being a boundary and becomes a &lt;strong&gt;routing fabric&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Once symmetry is restored, spoke‑to‑spoke reachability isn’t an accident it’s the inevitable result of a design optimised for correctness.&lt;/p&gt;
&lt;h3 id="azure-preserves-paths-aggressively"&gt;Azure preserves paths aggressively
&lt;/h3&gt;&lt;p&gt;Azure system routes and platform behaviour:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prefer the most specific prefix&lt;/li&gt;
&lt;li&gt;Preserve return paths when they exist&lt;/li&gt;
&lt;li&gt;Avoid blackholing traffic unless explicitly instructed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is excellent for application uptime.&lt;br&gt;
It’s also excellent for internal exploration.&lt;/p&gt;
&lt;p&gt;If a packet can get there &lt;em&gt;and&lt;/em&gt; come back, Azure will try very hard to keep that path stable.&lt;/p&gt;
&lt;h2 id="a-hard-admission"&gt;A Hard Admission
&lt;/h2&gt;&lt;p&gt;I’ve implemented symmetric hub routing myself more than once.&lt;/p&gt;
&lt;p&gt;It solved real problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Predictable traffic flows&lt;/li&gt;
&lt;li&gt;Fewer asymmetric failures&lt;/li&gt;
&lt;li&gt;Easier troubleshooting at 2am&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What I underestimated wasn’t the firewall or the rules.&lt;br&gt;
It was how completely Azure erased any remaining sense of internal boundary once symmetry was restored.&lt;/p&gt;
&lt;p&gt;The design was &lt;em&gt;correct&lt;/em&gt;.&lt;br&gt;
The blast radius was just larger than I intended.&lt;/p&gt;
&lt;h2 id="realworld-impact"&gt;Real‑World Impact
&lt;/h2&gt;&lt;p&gt;This isn’t theoretical. It changes outcomes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Design&lt;/strong&gt;&lt;br&gt;
Symmetric hub routing collapses routing domains. Once in a spoke, the rest of the estate becomes &lt;em&gt;discoverable by default&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reliability&lt;/strong&gt;&lt;br&gt;
The same paths that propagate traffic propagate failures security incidents and outages share infrastructure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;br&gt;
Lateral movement becomes a question of &lt;em&gt;which routes exist&lt;/em&gt;, not which controls failed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;br&gt;
Broad east–west flows quietly consume firewall throughput and bandwidth even during normal operation.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Practical anchor:&lt;/strong&gt;&lt;br&gt;
This should change when and how freely, you restore routing symmetry in your designs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="implementation-example"&gt;Implementation Example
&lt;/h2&gt;&lt;h3 id="the-standard-hubandspoke-pattern"&gt;The “standard” hub‑and‑spoke pattern
&lt;/h3&gt;&lt;div class="mermaid"&gt;flowchart LR
SpokeA --&gt; Hub
SpokeB --&gt; Hub
Hub --&gt; SpokeA
Hub --&gt; SpokeB
Hub --&gt; SharedServices
SharedServices --&gt; Hub
&lt;/div&gt;
&lt;p&gt;No direct peering between spokes.&lt;br&gt;
Yet full spoke‑to‑spoke reachability exists via the hub.&lt;/p&gt;
&lt;h3 id="the-udr-that-makes-it-inevitable"&gt;The UDR that makes it inevitable
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bicep" data-lang="bicep"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;spokeRouteTable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Microsoft.Network/routeTables@2023-09-01&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;rt-spoke-default&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;resourceGroup&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nv"&gt;location&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;default-to-hub&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;addressPrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;0.0.0.0/0&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nextHopType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;VirtualAppliance&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nextHopIpAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;10.0.0.4&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Hub firewall or NVA&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This single route:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Restores symmetry&lt;/li&gt;
&lt;li&gt;Makes the hub a transit router&lt;/li&gt;
&lt;li&gt;Removes natural routing boundaries between spokes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Operationally clean.&lt;br&gt;
Architecturally expansive.&lt;/p&gt;
&lt;h2 id="gotchas--edge-cases"&gt;Gotchas &amp;amp; Edge Cases
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Peering flags don’t save you&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;AllowForwardedTraffic = false&lt;/code&gt; doesn’t help once routing is reintroduced elsewhere.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Gateway transit magnifies the effect&lt;/strong&gt;&lt;br&gt;
Shared gateways flatten routing domains even further.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Private Endpoints don’t change this&lt;/strong&gt;&lt;br&gt;
They reduce exposure, not reachability. Routes still define movement.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="guidance-for-future-designs"&gt;Guidance for Future Designs
&lt;/h2&gt;&lt;p&gt;This isn’t about banning hubs or symmetry. It’s about intent.&lt;/p&gt;
&lt;h3 id="treat-routing-symmetry-as-privileged"&gt;Treat routing symmetry as privileged
&lt;/h3&gt;&lt;p&gt;In Azure, symmetric routing is not neutral.&lt;br&gt;
It is a &lt;em&gt;privileged capability&lt;/em&gt; that should be granted deliberately and narrowly.&lt;/p&gt;
&lt;p&gt;If symmetry isn’t required, let paths fail closed.&lt;/p&gt;
&lt;h3 id="design-routing-domains-not-just-vnets"&gt;Design routing domains, not just VNets
&lt;/h3&gt;&lt;p&gt;VNets and spokes are implementation details.&lt;br&gt;
Routing domains define blast radius.&lt;/p&gt;
&lt;p&gt;If two workloads don’t need to talk &lt;em&gt;both ways&lt;/em&gt;, they don’t belong in the same routing domain regardless of how tidy the diagram looks.&lt;/p&gt;
&lt;h3 id="make-hubs-earn-their-reach"&gt;Make hubs earn their reach
&lt;/h3&gt;&lt;p&gt;Centralised hubs trade convenience for scale including the scale of failure.&lt;/p&gt;
&lt;p&gt;Every east–west path enabled by a hub should be justified.&lt;br&gt;
“Operational simplicity” alone is not a sufficient reason.&lt;/p&gt;
&lt;h3 id="defer-symmetry-dont-default-to-it"&gt;Defer symmetry, don’t default to it
&lt;/h3&gt;&lt;p&gt;Today, I treat symmetric hub routing as an optimisation to be earned late not a baseline assumed early.&lt;/p&gt;
&lt;p&gt;That single shift changes how networks age under pressure.&lt;/p&gt;
&lt;div class="insight"&gt;
&lt;div class="insight-icon"&gt;🍺&lt;/div&gt;
&lt;div class="insight-content"&gt;
&lt;strong&gt;Brewed Insight:&lt;/strong&gt; Azure rewards correctness and connectivity.&lt;br&gt;
Restraint is something you have to design &lt;em&gt;against&lt;/em&gt; the platform on purpose.
&lt;/div&gt;
&lt;/div&gt;
&lt;style&gt;
.insight {
display: flex;
align-items: center;
background-color: #0089e41c;
border-left: 10px solid #D69A2D;
padding: 10px;
margin: 20px 0;
border-radius: 4px;
}
.insight-icon {
font-size: 24px;
margin-right: 10px;
}
.insight-content {
flex: 1;
}
&lt;/style&gt;&lt;h2 id="learn-more"&gt;Learn More
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/virtual-network/virtual-networks-udr-overview" target="_blank" rel="noopener"
&gt;Azure virtual network traffic routing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/virtual-network/virtual-network-peering-overview" target="_blank" rel="noopener"
&gt;Virtual network peering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/azure/architecture/reference-architectures/hybrid-networking/hub-spoke" target="_blank" rel="noopener"
&gt;Hub-spoke network topology in Azure&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>