OnCallReady

Lesson 23.3 · Azure II: Networking & AKS · 15 min read

Network security groups: priorities, defaults, service tags

In plain words

Imagine a bouncer at a club door with a numbered list of rules. He reads from the top, lowest number first, and the first rule that fits the person in front of him decides: in or out. At the very bottom, printed by the club owner and impossible to remove, are rules like "anyone from our own neighbourhood may enter" and "nobody else". And once someone is let in, they may walk back out without being checked again.

A network security group is that bouncer. Rules have priorities from 100 to 4096; first match wins. The default rules at 65000 and up allow everything inside the VNet, allow Azure load balancer probes, allow all outbound internet and deny the rest. It's stateful, so replies are allowed automatically. An empty NSG isn't "deny all"; it's "deny the internet".

By default everything in a VNet can reach everything else in it. So the payments database answers anyone who can reach its subnet - including the test VM someone left running. You need a firewall rule list per subnet, and on Azure that is an NSG.

What you need to know already: 23.1 (VNets, subnets, NICs), 9.1 (TCP connections), 9.8 (ports), 8.3 (CIDR ranges), 22.4 (--query, sort_by).

What an NSG is

An NSG (network security group) is a list of allow/deny rules, each matching on source address, source port, destination address, destination port and protocol (TCP, UDP, ...). You attach it to a subnet or to a NIC, and it filters every packet going in or out.

Two words from that: it works at L4 - layer 4, the TCP/UDP layer with ports (Ch 9): it sees addresses and ports, not URLs. And it is stateful: it remembers connections, so the reply traffic of an allowed connection is allowed automatically - you never write the reply rule.

Evaluation

Inbound means traffic coming into the subnet or NIC, outbound traffic leaving it. For each direction separately, rules are evaluated by priority (a number on each rule), lowest number first, and the first match wins. Your rules use 100-4096. Every NSG also has six default rules you cannot delete:

az network nsg rule list -g <rg> --nsg-name <nsg> --include-default -o table: list an NSG's rules; --include-default also shows the built-in ones.

# nsg-app = the NSG you create in the next mission
az network nsg rule list -g rg-oncall-lab --nsg-name nsg-app --include-default -o table
Name                           Priority  SourceAddressPrefixes  Access  Protocol  Direction  DestinationPortRanges  DestinationAddressPrefixes
-----------------------------  --------  ---------------------  ------  --------  ---------  ---------------------  --------------------------
AllowVnetInBound               65000     VirtualNetwork         Allow   *         Inbound    *                      VirtualNetwork
AllowAzureLoadBalancerInBound  65001     AzureLoadBalancer      Allow   *         Inbound    *                      *
DenyAllInBound                 65500     *                      Deny    *         Inbound    *                      *
AllowVnetOutBound              65000     VirtualNetwork         Allow   *         Outbound   *                      VirtualNetwork
AllowInternetOutBound          65001     *                      Allow   *         Outbound   *                      Internet
DenyAllOutBound                65500     *                      Deny    *         Outbound   *                      *

(columns trimmed). Read what they mean:

So to isolate a subnet from the rest of the VNet you need your own deny rule above 65000, e.g. priority 4000 "deny VirtualNetwork inbound", with specific allows below it (lower numbers).

Service tags

A service tag is a name that stands for a set of IP ranges Microsoft keeps up to date. Instead of IP ranges, rules can name them: Internet, VirtualNetwork, AzureLoadBalancer, AzureCloud, AzureKeyVault.WestEurope, Storage, AzureMonitor, GatewayManager... They update automatically as Azure's ranges change. App Gateway v2 (23.16) subnets must allow GatewayManager (Azure's own management traffic) on 65200-65535, or the gateway fails to provision (fails to be created and started).

Creating rules - and az's surprise default

az network nsg create -g <rg> -n <name>: create an empty NSG (it gets the six default rules). az network nsg rule create: add one rule; --nsg-name which NSG; -n the rule's name; --priority its number; --direction Inbound or Outbound; --access Allow or Deny; --protocol Tcp, Udp or *; --source-address-prefixes where the traffic comes from (CIDR or service tag); --destination-port-ranges which ports it goes to.

az network nsg create -g rg-oncall-lab -n nsg-app
az network nsg rule create -g rg-oncall-lab --nsg-name nsg-app -n allow-https-from-appgw \
  --priority 100 --direction Inbound --access Allow --protocol Tcp \
  --source-address-prefixes 10.20.1.0/24 --destination-port-ranges 443

Leave out --destination-port-ranges and the CLI defaults it to 80:

# ... = -g rg-oncall-lab --nsg-name nsg-app
az network nsg rule create ... -n allow-https --priority 100 --protocol Tcp --source-address-prefixes 10.20.1.0/24
az network nsg rule show ... -n allow-https --query destinationPortRange
"80"

The rule is called "allow-https" and allows HTTP. Always pass the port. The other defaults: --access Allow, --direction Inbound, --protocol *, source and destination *.

Priorities must be unique per direction:

ERROR: (SecurityRuleConflict) Security rule dup conflicts with rule allow-https. Rules cannot
have the same Priority and Direction. To learn more, see aka.ms/nsgrules.

Leave gaps (100, 110, 120 ... or 100, 200) so a rule can be inserted later without renumbering.

Subnet and NIC

az network vnet subnet update ... --nsg nsg-app attaches an NSG to a subnet. An NSG can sit on the subnet, on the NIC, or both. With both, inbound traffic must be allowed by the subnet NSG and then the NIC NSG; outbound by the NIC NSG and then the subnet NSG. Most teams use subnet NSGs only - one place to look.

az network vnet subnet update -g rg-oncall-lab --vnet-name vnet-sysop -n snet-app --nsg nsg-app

AKS and NSGs

AKS (Azure's managed Kubernetes, 23.20) creates its node VMs in a separate resource group whose name starts with MC_ (the node resource group). It manages an NSG on the node pool's NICs there and adds rules for LoadBalancer Services (Ch 16) itself. Do not edit that NSG by hand - AKS keeps putting it back the way it wants it (it is reconciled, like a controller does in 15.9). Put your own policy on the subnet NSG, and remember the AKS subnet must still allow: node-to-node, the load balancer probes, and egress to the control plane and required endpoints.

Debugging

Later (Ch 24): Traffic Analytics loads flow logs into a Log Analytics workspace so you can query them.

Walking a packet through by hand

The same list sorted by priority, inbound only (sort_by(..., &priority) is JMESPath, 22.4):

# nsg-app after the NSG mission
az network nsg rule list -g rg-oncall-lab --nsg-name nsg-app --include-default \
    --query "sort_by([?direction=='Inbound'], &priority)[].{p:priority, name:name, access:access, src:sourceAddressPrefix, port:destinationPortRange}" -o table
P      Name                           Access    Src                Port
-----  -----------------------------  --------  -----------------  ------
100    allow-https-from-appgw         Allow     10.20.1.0/24       443
110    allow-lb-probes                Allow     AzureLoadBalancer  *
4000   deny-vnet-in                   Deny      VirtualNetwork     *
65000  AllowVnetInBound               Allow     VirtualNetwork     *
65001  AllowAzureLoadBalancerInBound  Allow     AzureLoadBalancer  *
65500  DenyAllInBound                 Deny      *                  *

The drill makes you do this on random rule sets until it is automatic.

Application security groups

Instead of IP prefixes, NICs can be members of ASGs (application security groups - named groups of NICs, such as asg-web, asg-db) and rules can say "from asg-web to asg-db on 5432". The rule survives VMs being recreated with new IPs. ASGs work on NICs (VMs, and VM scale sets - VMSS, a group of identical VMs Azure grows and shrinks as one, which is what an AKS node pool is, 23.20) - not for pods, which is one reason pod-level policy lives in Kubernetes NetworkPolicy (16.29) instead.

Where NSGs stop

NSGs filter by address and port. They cannot say "only to *.github.com", see the TLS SNI (the host name a client sends at the start of TLS, 9.15), or inspect HTTP. That is a firewall's (FQDN rules, 23.7) or a WAF's (web application firewall, 23.16) job.

What you can now do

Why it helps

"Why can't service A reach service B?" and "why can everything reach the database?" are both NSG questions. Walking a packet through a sorted rule list, including the defaults, is a skill you'll use weekly, and the drill makes it automatic. Knowing that AllowVnetInBound lets every peered VNet and on-prem range in explains a lot of audit findings.

There are also sharp edges that cause real outages: az network nsg rule create without a port silently allows 80, denying the AzureLoadBalancer tag makes every backend unhealthy, App Gateway v2 fails without GatewayManager allowed on 65200-65535, and editing the AKS-managed NSG by hand gets reverted. Knowing these saves you from creating incidents while fixing another.

FAQ

Does an empty NSG block everything?

No. Its default rules allow all inbound traffic from VirtualNetwork, which includes peered VNets and on-prem ranges learnt over VPN or ExpressRoute, allow Azure load balancer health probes, and allow all outbound traffic to the internet. Only inbound from the internet and anything else not covered is denied. To isolate a subnet, you add your own deny rule for VirtualNetwork at a priority below 65000, with specific allows at lower numbers.

How are NSG rules evaluated?

Per direction, inbound and outbound separately, in order of priority from the lowest number, and the first matching rule decides; later rules are never read. Your rules use 100 to 4096; the defaults sit at 65000 and above and can't be deleted, only overridden by your rules. Priorities must be unique per direction. Leave gaps like 100, 110, 120 so you can insert rules later without renumbering everything.

What happens with an NSG on both the subnet and the NIC?

Traffic must pass both. Inbound, the subnet NSG is evaluated first and then the NIC NSG; outbound, the NIC NSG first and then the subnet NSG. A deny in either blocks it. That doubles the places to look when debugging, which is why most teams use subnet-level NSGs only. AKS is an exception: it manages its own NSG on the node NICs, which you shouldn't edit.

What are service tags?

Named groups of IP ranges maintained by Microsoft, used in rules instead of prefixes: Internet, VirtualNetwork, AzureLoadBalancer, AzureCloud, Storage, AzureKeyVault.WestEurope, AzureMonitor, GatewayManager and many more. They update automatically as Azure's ranges change, so you don't maintain lists. Regional variants like Storage.WestEurope narrow them. They're essential for egress rules and for services that need management traffic.

Can an NSG filter by domain name?

No. NSGs work at layer 4: addresses, ports and protocols. They can't allow "only *.github.com", read TLS SNI, or inspect HTTP paths. For domain-based egress control you need Azure Firewall application rules or FQDN-filtered network rules, or a third-party firewall; for HTTP inspection a WAF on Application Gateway or Front Door. Pod-level policy inside AKS belongs in Kubernetes NetworkPolicy.

In an interview Mid

What is a network security group and how are its rules evaluated?

An NSG is a list of allow/deny rules on source, source port, destination, destination port and protocol, attached to a subnet or a NIC. It works at L4 (addresses and ports, not URLs) and is stateful - replies to an allowed connection are allowed automatically.

Evaluation, per direction: lowest priority number first, first match wins. Your rules use 100-4096; below them sit default rules you cannot delete:

So isolating a subnet = specific allows at low numbers, then your own "deny VirtualNetwork" above the defaults (e.g. 4000). Use service tags (AzureLoadBalancer, Storage) instead of IP lists. Gotcha: az network nsg rule create without --destination-port-ranges defaults to 80. With NSGs on both subnet and NIC, both must allow.

Also asked: How would you isolate an application subnet so only the App Gateway subnet can reach it on 443? · What are service tags and why use them in NSG rules? · What can an NSG not filter, and what do you use instead?

Practise this lesson in the terminal Free, in your browser - a real Ubuntu terminal to try it in, with missions that check your work.