Back to blog
AzureBest PracticesCloud SecurityNetworking

NSG Allows Public DNS: Closing Port 53 on Azure

Learn why an Azure NSG allowing public DNS on port 53 is a security risk, how to remediate it with CLI and IaC, and how to prevent it with Azure Policy.

TL;DR

This check flags Azure NSG rules that allow inbound DNS traffic (port 53) from the public internet. Open DNS resolvers get abused for amplification DDoS attacks and data exfiltration, so lock the source down to known internal ranges or remove the rule entirely.

DNS is one of those protocols that feels harmless because it is everywhere and mostly invisible. That is exactly why an exposed DNS port tends to slip through reviews. Lensix raises nsg_opendns when it finds a Network Security Group rule that permits inbound traffic on port 53 from a public source such as 0.0.0.0/0, Internet, or *. In most architectures that is a misconfiguration, and in a few it is an outright invitation for abuse.


What this check detects

The check inspects the inbound security rules attached to your NSGs and looks for any allow rule that matches all three of these conditions:

  • Destination port includes 53 (TCP, UDP, or both, including port ranges that cover it like 0-65535)
  • Source address prefix is a public value: 0.0.0.0/0, *, Internet, or a broad public CIDR
  • Access is set to Allow with a direction of Inbound

DNS uses UDP 53 for most queries and TCP 53 for larger responses and zone transfers. A wide-open rule that covers both is the worst case, but even UDP-only exposure is enough to turn your VM into a tool for someone else's attack.

Note: An NSG rule that opens a port range such as 0-65535 or 1-1024 will also trip this check, since port 53 falls inside the range. These overly broad rules are common in "temporary" lab setups that quietly make it to production.


Why it matters

An open DNS server on a public IP is a gift to attackers for two reasons.

DNS amplification DDoS

DNS is a stateless UDP protocol, which means an attacker can spoof the source IP of a query so the response goes to a victim instead of the requester. A small query can trigger a much larger response, so attackers send a flood of spoofed queries to open resolvers and direct the amplified traffic at a target. If your VM is running an open or recursive resolver, it becomes an unwilling participant in someone else's attack, your egress bandwidth bill climbs, and your IP can end up on abuse blocklists.

DNS tunneling and exfiltration

If an attacker has already landed on a host inside your network, an exposed DNS path is a convenient covert channel. DNS tunneling encodes data inside DNS queries and responses, which lets attackers exfiltrate data or maintain command and control through a protocol that most firewalls wave through. An NSG that allows arbitrary inbound DNS widens the surface for this kind of abuse.

Warning: Even if no DNS service is currently listening on the VM, the open rule is still a liability. Any future workload, container, or malware that binds to port 53 immediately becomes reachable from the internet without a single config change.

The business impact is concrete: surprise bandwidth charges, degraded service when your IP is blocklisted, and a compliance finding during any audit that expects least-privilege network rules.


How to fix it

Start by finding the offending rule. List the NSGs in your subscription and inspect the rules.

# List all NSGs
az network nsg list --query "[].{name:name, rg:resourceGroup}" -o table

# Inspect rules on a specific NSG
az network nsg rule list \
  --nsg-name myNsg \
  --resource-group myResourceGroup \
  -o table

Look for any rule with destination port 53 and a source of *, 0.0.0.0/0, or Internet. Once you have identified it, you have three paths depending on whether you actually need DNS exposed.

Option 1: Remove the rule (no public DNS needed)

If nothing legitimate depends on inbound DNS from the internet, delete the rule.

Danger: Deleting an NSG rule can break live traffic. Confirm that no production service relies on this rule before running the command. Check connection logs and talk to the team that owns the workload first.

az network nsg rule delete \
  --nsg-name myNsg \
  --resource-group myResourceGroup \
  --name allow-dns-inbound

Option 2: Restrict the source to known ranges

If internal systems need to query this host, scope the source to your VNet or specific trusted CIDRs instead of the entire internet.

az network nsg rule update \
  --nsg-name myNsg \
  --resource-group myResourceGroup \
  --name allow-dns-inbound \
  --source-address-prefixes 10.0.0.0/16 \
  --destination-port-ranges 53 \
  --protocol Udp \
  --access Allow

Option 3: Fix overly broad port ranges

If the real problem is a catch-all rule like 0-65535, replace it with explicit ports your service actually uses. Do not just carve out 53, the whole range is the issue.

az network nsg rule update \
  --nsg-name myNsg \
  --resource-group myResourceGroup \
  --name allow-app-traffic \
  --source-address-prefixes 10.0.0.0/16 \
  --destination-port-ranges 443 8080

Tip: If you genuinely need an internal DNS resolver, use Azure Private DNS Resolver or Private DNS zones instead of running your own resolver on a public-facing VM. You get managed resolution without exposing port 53 to the internet.


Defining it correctly in IaC

Codify the secure rule so the fix survives the next deployment. Here is a Terraform example that scopes DNS to the VNet range.

resource "azurerm_network_security_rule" "dns_internal" {
  name                        = "allow-dns-internal"
  priority                    = 200
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "53"
  source_address_prefix       = "10.0.0.0/16"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.main.name
  network_security_group_name = azurerm_network_security_group.main.name
}

The same rule in a Bicep template:

{
  "name": "allow-dns-internal",
  "properties": {
    "priority": 200,
    "direction": "Inbound",
    "access": "Allow",
    "protocol": "Udp",
    "sourcePortRange": "*",
    "destinationPortRange": "53",
    "sourceAddressPrefix": "10.0.0.0/16",
    "destinationAddressPrefix": "*"
  }
}

How to prevent it from happening again

One-off fixes get undone. The durable answer is to block the misconfiguration before it ever reaches Azure.

Use Azure Policy to deny it at the platform level

Azure Policy can audit or outright deny NSG rules that expose sensitive ports. Assign a policy that denies inbound rules with a source of * or Internet on port 53. The built-in initiative for network security includes related rules, and you can author a custom one targeting Microsoft.Network/networkSecurityGroups/securityRules.

{
  "if": {
    "allOf": [
      { "field": "type", "equals": "Microsoft.Network/networkSecurityGroups/securityRules" },
      { "field": "Microsoft.Network/networkSecurityGroups/securityRules/access", "equals": "Allow" },
      { "field": "Microsoft.Network/networkSecurityGroups/securityRules/direction", "equals": "Inbound" },
      { "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange", "equals": "53" },
      {
        "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix",
        "in": [ "*", "Internet", "0.0.0.0/0" ]
      }
    ]
  },
  "then": { "effect": "deny" }
}

Gate it in CI/CD

Scan your Terraform or Bicep before apply with a policy-as-code tool like Checkov, tfsec, or Conftest. Fail the pipeline when a public source meets port 53. This catches the problem at pull request time, when fixing it costs a one-line change instead of an incident.

Tip: Pair the pipeline scan with continuous monitoring in Lensix. Pre-merge scanning catches what goes through your IaC, and continuous checks catch the manual portal change someone made at 2am during an outage.


Best practices

  • Default to deny. NSGs should start closed and open only the specific ports and sources a workload requires.
  • Never use * or Internet as a source for management or infrastructure ports. DNS, SSH, RDP, and database ports belong behind private ranges.
  • Prefer managed DNS. Azure Private DNS and Private DNS Resolver remove the need to run your own internet-facing resolver.
  • Avoid wide port ranges. A rule that opens 0-65535 exposes far more than the author intended. List explicit ports.
  • Tag and review temporary rules. Lab and debugging rules should carry an expiry tag and get cleaned up on a schedule.
  • Enable NSG flow logs. If something does abuse an exposed port, flow logs and traffic analytics give you the evidence to scope the blast radius.

DNS exposure is an easy finding to dismiss because the port feels mundane. Treat it the way an attacker does: as free infrastructure for amplification attacks and a quiet channel for exfiltration. Close it, codify the closure, and let policy keep it closed.