Back to blog
AzureBest PracticesCloud SecurityNetworkingOperations & Compliance

NSG Allows Public SaltStack (Ports 4505/4506)

Learn why an Azure NSG rule exposing SaltStack ports 4505/4506 to the internet is a critical RCE risk, plus CLI, Terraform, and Azure Policy fixes.

TL;DR

This check flags Azure Network Security Group rules that expose SaltStack ports (4505/4506) to the public internet. An open Salt master is a direct path to remote code execution across every minion it controls. Lock the source down to your management subnet or VPN and drop the public rule.

SaltStack is a configuration management and orchestration tool that, by design, runs commands across fleets of machines. That is exactly why exposing it to the internet is so dangerous: a Salt master that anyone can reach is a turnkey way to push commands to every server it manages. This Lensix check, nsg_opensalt, looks for Azure NSG rules that allow inbound traffic to ports 4505 and 4506 from public source ranges.


What this check detects

The nsg_opensalt check inspects your Network Security Groups for inbound rules that meet all of the following conditions:

  • Direction is Inbound and access is Allow
  • The destination port range includes 4505 or 4506
  • The source address prefix is a public range such as *, 0.0.0.0/0, Internet, or any non-RFC1918 CIDR
  • Protocol is TCP or *

Port 4505 is the Salt publisher port, used by the master to push jobs out to minions. Port 4506 is the request server port, where minions return results and request files or pillar data. Both speak the ZeroMQ-based Salt protocol, and both should never be reachable from outside your trusted network.

Note: In a normal Salt deployment, only minions need to reach the master on 4505/4506, and the master initiates nothing inbound from the public internet. There is no legitimate reason for these ports to be open to 0.0.0.0/0.


Why it matters

An exposed Salt master is not a theoretical risk. It has been the root cause of large, automated compromises. In 2020, two CVEs in Salt (CVE-2020-11651, an authentication bypass, and CVE-2020-11652, a directory traversal) were chained to allow unauthenticated attackers to run arbitrary commands as root on any internet-facing Salt master. Within days, scanners were sweeping the internet for open 4505/4506 ports and dropping cryptominers and backdoors on thousands of hosts. Victims included LineageOS, Ghost, and DigiCert's certificate authority infrastructure.

The reason the blast radius is so severe comes down to what Salt does. The master is not just another service. It has authority over every minion. Compromise the master and you do not get one server, you get the whole fleet, with the ability to run commands as root on all of them at once.

Danger: An open Salt master is one of the highest-leverage misconfigurations in cloud infrastructure. A single unauthenticated RCE gives an attacker root-level command execution across your entire managed fleet. Treat any public exposure of 4505/4506 as an active incident, not a low-priority finding.

Even with a fully patched Salt version, leaving these ports open is a bad bet. New vulnerabilities surface, brute-force attempts against authentication add noise and risk, and the port itself advertises to scanners exactly what software you are running and where.


How to fix it

The goal is to remove public access and restrict 4505/4506 to the specific sources that actually need them, typically your minion subnets or a bastion/management network.

1. Find the offending rule

List the rules on the NSG and look for ones targeting 4505 or 4506 with a public source:

az network nsg rule list \
  --resource-group my-rg \
  --nsg-name my-nsg \
  --query "[?contains(to_string(destinationPortRange), '4505') || contains(to_string(destinationPortRange), '4506')].{Name:name, Port:destinationPortRange, Source:sourceAddressPrefix, Access:access}" \
  --output table

2. Restrict the source to a trusted range

If you genuinely need inbound Salt traffic, scope it to the subnet or CIDR that hosts your minions or your VPN. Update the rule rather than deleting it:

Warning: Changing the source on an active rule can cut off minions that connect from outside the new range. Confirm where your minions actually originate (NAT gateway IP, internal subnet, peered VNet) before narrowing the rule, or you will break configuration management.

az network nsg rule update \
  --resource-group my-rg \
  --nsg-name my-nsg \
  --name allow-saltstack \
  --source-address-prefixes 10.0.1.0/24 \
  --destination-port-ranges 4505 4506 \
  --protocol Tcp \
  --access Allow

3. Or remove the rule entirely

If the master and minions live in the same VNet, intra-VNet traffic is already allowed by default and you do not need a public inbound rule at all. Delete it:

Danger: Deleting an NSG rule is immediate and affects live traffic. Verify the rule is not the only path your minions use to reach the master before running this against production.

az network nsg rule delete \
  --resource-group my-rg \
  --nsg-name my-nsg \
  --name allow-saltstack

4. Verify Salt is patched while you are in there

Network controls are your first line of defense, not your only one. Confirm the master is on a current release:

salt --versions-report

Anything older than the 2019.2.4 / 3000.2 fixes for the 2020 CVEs should be upgraded immediately.


How to prevent it from happening again

Manual fixes do not stick. The same rule tends to reappear the next time someone is debugging connectivity. Codify the guardrail.

Define NSG rules in Terraform with explicit sources

Never use * or Internet as a source for management ports. Reference a known prefix:

resource "azurerm_network_security_rule" "salt" {
  name                        = "allow-saltstack-internal"
  priority                    = 200
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_ranges     = ["4505", "4506"]
  source_address_prefixes     = ["10.0.1.0/24"]
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.main.name
  network_security_group_name = azurerm_network_security_group.main.name
}

Enforce it with Azure Policy

Deploy a policy that denies any NSG rule opening 4505/4506 from a public source. This blocks the misconfiguration at deploy time instead of catching it after the fact:

{
  "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", "in": ["4505", "4506"] },
      { "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix", "in": ["*", "0.0.0.0/0", "Internet"] }
    ]
  },
  "then": { "effect": "deny" }
}

Tip: Pair the deny policy with a Lensix check in your CI pipeline so misconfigured Terraform plans fail before merge. Catching it in a pull request is cheaper than catching it in production, and it keeps the audit trail in your version control history.

Scan IaC in CI/CD

Run a policy-as-code scan on every pull request. Tools like Checkov, tfsec, or the Lensix IaC scanner flag open management ports before the plan is ever applied. A failing gate on a public 4505/4506 rule stops the problem at the source.


Best practices

  • Default to deny. NSG inbound rules should explicitly allow only what is needed. Avoid wildcard sources for any administrative or orchestration port.
  • Keep the master off the public internet. Reach it through a bastion host, VPN, or Azure Bastion rather than exposing the Salt protocol directly.
  • Segment by subnet. Put the Salt master and its minions in dedicated subnets and use NSG rules scoped to those subnet CIDRs.
  • Patch promptly. Salt has a history of high-severity CVEs. Subscribe to release notifications and keep masters current.
  • Audit the full management port set. The same logic applies to SSH (22), RDP (3389), and other config-management tools like Puppet (8140) and Chef. Lensix has dedicated checks for these too.
  • Monitor for drift. Continuous scanning catches the rule someone re-added during an incident at 2am and forgot to remove.

SaltStack is powerful precisely because it can act on your whole fleet at once. That power belongs behind your network perimeter, never in front of it. Close the ports, scope the sources, and put a policy gate in front of the next person who tries to open them.