Network Security Groups (NSGs) are Azure layer-3 firewalls, they basically allow filtering traffic based on Source/Destination IP, Port and Protocol. It’s a very simple component but yet lately I got a little confused around Inbound/Outbound traffic. That’s why I’m writing this blog in case you are like me and you couldn’t find an explanation for it.

I’ve been using or having to think about Network Security Groups (NSGs) rules mostly on scenarios which involved Virtual Machine deployment; in this case it’s very intuitive to understand NSGs as your point of reference is about the VM itself: Traffic getting into the VM is Inbound and traffic going out of the VM is Outbound. So, not a big issue there. However, I struggled when I faced the situation to using NSG to secure access to in-VNET PaaS services (such as App Service Environments, SQL Managed Instance or RedisCache, to name a few). In this last case, you don’t just apply the NSG to the product but to the subnet and here is where all my previous knowledge or understanding kind of collapse because, as a good security rule, I wanted to just have the minimum necessary traffic in the VNET and allow the specific connectivity betwen them. As an example, let’s assume I have the follow diagram:

NSG_Example

where I would like to ONLY allow traffic from App Services within each ASE to other App Services hosted in the same ASE AND allow only traffic from ASE A to ASE B but not the other way around. To complete the scenario say VNET address space is 10.0.0.0/23, Subnet holding ASE A’s address space is 10.0.0.0/24 and Subnet holding ASE B’s address space is 10.0.1.0/24.

So, question is “What rules should I configure in the NSG in order to allow the traffic?” My first thought was:

Inbound

Priority Name Source Destination Service Action
100 AllowAzureLoadBalancerInBound AzureLoadBalancer VirtualNetwork Custom (Any/Any) Allow
110 AllowAseAInternalTraffic 10.0.0.0/24 10.0.0.0/24 Custom (Any/Any) Allow
120 AllowAseBInternalTraffic 10.0.1.0/24 10.0.1.0/24 Custom (Any/Any) Allow
130 AllowAseAtoAseBTraffic 10.0.0.0/24 10.0.1.0/24 HTTPS (TCP 443) Allow
4096 DenyAllInBound Any Any Custom (Any/Any) Deny
65000 AllowVnetInBound VirtualNetwork VirtualNetwork Custom (Any/Any) Allow
65001 AllowAzureLoadBalancerInBound AzureLoadBalancer Any Custom (Any/Any) Allow
65500 DenyAllInBound Any Any Custom (Any/Any) Deny

Outbound

Priority Name Source Destination Service Action
100 AllowAzureLoadBalancerOutBound VirtualNetwork AzureLoadBalancer Custom (Any/Any) Allow
110 AllowAseAInternetOutBound 10.0.0.0/24 Internet Custom (Any/Any) Allow
120 AllowAseBInternetOutBound 10.0.1.0/24 Internet Custom (Any/Any) Allow
4096 DenyAllOutBound Any Any Custom (Any/Any) Deny
65000 AllowVnetOutBound VirtualNetwork VirtualNetwork Custom (Any/Any) Allow
65001 AllowInternetOutBound Any Internet Custom (Any/Any) Allow
65500 DenyAllOutBound Any Any Custom (Any/Any) Deny

What do you think? Would that work? Well, personally, I was convinced this was going to work but, guess what?, I didn’t and further more I started to get confused on what Inbound or Outbound meant (remember this is a single NSG for both subnets). So, how would that play here? do I need to specify everything? oh boy… I’m totally lost. That’s why I took a step back and did what anybody would have done it: create this in Azure, deploy 2 VMs (one on each subnet) and start experimenting. What did I found? Well, although my Inbound rules look good, I have a couple of outbound rules missing:

Inbound

Priority Name Source Destination Service Action
100 AllowAzureLoadBalancerInBound AzureLoadBalancer VirtualNetwork Custom (Any/Any) Allow
110 AllowAseAInternalTraffic 10.0.0.0/24 10.0.0.0/24 Custom (Any/Any) Allow
120 AllowAseBInternalTraffic 10.0.1.0/24 10.0.1.0/24 Custom (Any/Any) Allow
130 AllowAseAtoAseBTraffic 10.0.0.0/24 10.0.1.0/24 HTTPS (TCP 443) Allow
4096 DenyAllInBound Any Any Custom (Any/Any) Deny
65000 AllowVnetInBound VirtualNetwork VirtualNetwork Custom (Any/Any) Allow
65001 AllowAzureLoadBalancerInBound AzureLoadBalancer Any Custom (Any/Any) Allow
65500 DenyAllInBound Any Any Custom (Any/Any) Deny

Outbound

Priority Name Source Destination Service Action
100 AllowAzureLoadBalancerOutBound VirtualNetwork AzureLoadBalancer Custom (Any/Any) Allow
110 AllowAseAInternalTraffic 10.0.0.0/24 10.0.0.0/24 Custom (Any/Any) Allow
120 AllowAseAInternetOutBound 10.0.0.0/24 Internet Custom (Any/Any) Allow
130 AllowAseBInternalTraffic 10.0.1.0/24 10.0.1.0/24 Custom (Any/Any) Allow
140 AllowAseBInternetOutBound 10.0.1.0/24 Internet Custom (Any/Any) Allow
150 AllowAseAtoAseBTraffic 10.0.0.0/24 10.0.1.0/24 HTTPS (TCP 443) Allow
4096 DenyAllOutBound Any Any Custom (Any/Any) Deny
65000 AllowVnetOutBound VirtualNetwork VirtualNetwork Custom (Any/Any) Allow
65001 AllowInternetOutBound Any Internet Custom (Any/Any) Allow
65500 DenyAllOutBound Any Any Custom (Any/Any) Deny

That’s because, let’s take for instance traffic from ASE A to ASE B; from the NSG perspective, the traffic starts from ASE A so it gets evaluated by the NSG as OutBound traffic (it checks Source/Destination/Port) and then it gets evaluated, again by the NSG, but now as InBound traffic. If any of the rules is missing, the traffic will get block. Odd enough, this logic is the same when ASE A attempts to reach another App Service hosted in the same ASE A

Inbound or Outbound?

The morale of the story is Inbound or Outbound isn’t evaluated from the Resource point of view from it’s actually from the NSG point of view; this means:

  • Inbound: it’s the traffic getting into the NSG or the traffic received by the resource
  • Outbound: it’s the traffic going out of the NSG or the traffic initiated by the resource

I know, I know, this sounds pretty obvious now but I assure you, it wasn’t for me.

What does it mean if I have one NSG assigned to multiple Subnets?

NSG doesn’t care, or actually, it doesn’t know resources are in the same VNET or even same subnet, it will just inspect packages. So, for subnets in the same VNET, the NSG will be always evaluated twice once when it gets started (Outbound) and once when it gets into the subnet (Inbound); EVEN IF THE SUBNET IS THE SAME ONE.

What’s the best approach them?

After all this, I gotta say that I got to this conclusion:

  • Assigning a single NSG for all subnets to control both inbound and outbound traffic, it ain’t a good idea at all. Why? simple, it’s not obvious enough which you’ll remember now or the first time setting it but a time after, you will just forget.
  • When facing this scenario, choose between these two options:
    • Use one NSG per subnet: in that way, it will make obvious which NSG should have an inbound and which one the outbound rule.
      OR
    • Choose if you care more around protecting what it’s getting into the Subnet or what it’s getting out. In my case, I prefer to care around Inbound since in that way you are protecting the asset or the data.