How to close RDP and SSH access from the internet in Azure
This guide shows you how to find network security group rules that expose RDP (3389) and SSH (22) to the whole internet, and how to replace them with Azure Bastion or just-in-time access. It is written for Azure administrators who look after virtual machines and subscriptions.
Before you start
Roles
- Reader on the subscriptions is enough for the review steps.
- Network Contributor (or a custom role with
Microsoft.Network/networkSecurityGroups/securityRules/writeanddelete) to edit or remove rules. - To set up just-in-time access you need Microsoft Defender for Servers Plan 2 on the subscription, plus rights to write JIT policies. That plan is billed separately, so check Microsoft's pricing page first.
Tools
- The Azure portal, or Azure CLI (
az login), or Azure PowerShell (Connect-AzAccount). - A list of who really needs to reach which VM, and from where. Collect it before you touch any rule.
Good to know
Removing a rule only affects new connections. An open RDP or SSH session stays alive, but the next login attempt is blocked, so schedule the change and keep a second way into the VM (for example Bastion) ready.
Step by step
1. List what is open today
Run the read-only command in the next section for every subscription, or in the portal go to Network security groups, open each group and read Inbound security rules. Look for rules with an Allow action whose source is Any, *, 0.0.0.0/0 or the Internet service tag, and whose destination port is 22, 3389, a range that contains them, or *.
2. Note which VMs each rule reaches
In the NSG, open Network interfaces and Subnets to see what the group is attached to. A rule on a subnet NSG affects every VM in that subnet. Ask the owners of those VMs how they connect.
3. Choose a replacement path
- Azure Bastion (best for regular access). Bastion gives RDP/SSH over TLS on port 443 from the Azure portal, and the VMs need no public IP. Basic, Standard and Premium need a dedicated subnet named
AzureBastionSubnetand a public IP on the Bastion itself. Deploy it from the Azure portal by following Microsoft's Bastion deployment instructions for your SKU. Bastion is billed from the moment it is deployed. - Just-in-time VM access (when you must keep a direct path). In the portal: Microsoft Defender for Cloud > Workload protections > Just-in-time VM access > Not configured tab, tick the VMs and select Enable JIT on VMs. Defender suggests ports 22, 3389, 5985 and 5986. Set allowed source IPs and a maximum request time per port instead of leaving the default of any source.
- A narrow allow rule from a fixed office or VPN range, if the first two are not possible.
4. Make the change (changes the tenant)
The commands below change your Azure environment. Run them only after step 3 is working, and test with one NSG first.
Narrow an existing rule to a trusted range (replace the names and the CIDR with your own):
az network nsg rule update \
--resource-group myResourceGroup \
--nsg-name myNSG \
--name RDP-rule \
--source-address-prefixes 203.0.113.0/24Or delete the rule altogether:
az network nsg rule delete \
--resource-group myResourceGroup \
--nsg-name myNSG \
--name RDP-ruleIn the portal the same actions are under Network security groups > your group > Inbound security rules > select the rule > Save or Delete. You cannot delete the default rules, but a custom rule with a lower priority number overrides them.
5. Repeat for every subscription and NSG
Rules on subnets and on individual network interfaces both count. Finish by running the check below again.
Check it worked
This command is read-only. It lists every custom inbound Allow rule that is open to any source, in every NSG of the current subscription. Switch subscription with az account set --subscription <id> and repeat.
az network nsg list --query "[].{nsg:name, group:resourceGroup, rules:securityRules[?direction=='Inbound' && access=='Allow' && (sourceAddressPrefix=='*' || sourceAddressPrefix=='Internet' || sourceAddressPrefix=='0.0.0.0/0')].{name:name, port:destinationPortRange, ports:destinationPortRanges, priority:priority}}" --output jsonRead the port and ports values. You want no rule that allows 22, 3389, a range containing them, or * from the internet. Rules for 80 or 443 on a public web server can be legitimate. Also try a new RDP or SSH connection from outside your approved path: it should time out, and your Bastion or JIT route should still work.
Common mistakes
- Only checking the NSG on the network card. A subnet NSG can also allow the port. Check both.
- Trusting the Internet tag as the only risky source. A source of
*or0.0.0.0/0means the same thing. - Missing port ranges and lists. A rule for
3380-3400or22,80,443opens a port without a rule named after it. - Deleting the rule before the replacement works. Existing sessions stay up, which hides the problem until the next login.
- Forgetting to protect other management ports. JIT suggests 5985 and 5986 (WinRM) as well.
- Leaving JIT sources on "Any". The default source for JIT access is any address, so tighten it.
How ConfigCobra helps
ConfigCobra is read-only and never changes your Azure environment. It connects with a certificate-based app registration and Reader on the subscriptions you choose, and every connected cloud is scanned automatically every day. Findings for the CIS Microsoft Azure Foundations Benchmark v6.0.0 (127 controls) point at the exact Azure resource, so you can see which NSG still has the exposure. Before you apply a fix, remediation impact analysis shows the affected resources and identities and the effect of the change. You get an alert if a control regresses, and a copy-paste remediation script or portal steps per finding. The related checks below cover the individual RDP, SSH and Bastion controls.
Related checks
Go further
- All guidesAdmin how-tos for Microsoft 365, Azure, Google Cloud and AWS
- Check libraryEvery CIS check, explained
- RemediationSee what a fix changes in your tenant before you apply it, then use the generated script
- Inventory & InsightsEvery user, app, device and resource in one inventory, with built-in risk views
See where your Microsoft Azure stands
A read-only assessment against the CIS benchmarks, prioritised so you know what to fix first.