“Kubernetes API server should be configured with restricted access” is a Microsoft Defender for Cloud recommendation raised when an AKS cluster’s public API server accepts connections from any IP address. Fix it by defining AKS API server authorized IP ranges that cover your admin networks, CI/CD agents and the cluster’s own egress IP, or by making the cluster private.
What Defender for Cloud is actually detecting
This is a control plane configuration check on the Microsoft.ContainerService/managedClusters resource, not a network probe. Defender for Cloud lists it with severity High and type Control plane, and backs it with the built-in Azure Policy Authorized IP ranges should be defined on Kubernetes Services (definition ID 0e246bcf-5f6f-4f87-bc6f-775d4712c7ea, default effect Audit). The policy rule flags a cluster only when both conditions are true:
- apiServerAccessProfile.authorizedIPRanges does not exist, and
- apiServerAccessProfile.enablePrivateCluster does not exist or is false.
| Cluster state | Result |
|---|---|
| Public API server, no authorized ranges | Unhealthy (the finding) |
| Public API server with any authorized range list | Healthy |
| Private cluster | Healthy (not audited) |
Note what the rule does not do: it checks that a list exists, not how narrow it is. A cluster that authorizes a very broad range passes while gaining little. If you are deciding where this sits in a long backlog, see our guide to triaging Defender for Cloud recommendations.
Real-world risk
By default AKS gives the API server a public IP address. Reaching it is not the same as controlling the cluster: every request still has to authenticate and pass Kubernetes RBAC or Azure RBAC. The honest risk is what happens when something else goes wrong. A leaked kubeconfig, a CI secret or a service account token becomes usable from any network on the Internet, and the endpoint stays exposed to any future flaw in the API server’s request handling. Authorized IP ranges add a network condition on top of authentication, so a stolen credential alone is no longer enough. They do not replace strong identity, RBAC or patching.
How to confirm it
Check one cluster with Azure CLI. Empty or null ranges together with null or false private means the finding is accurate:
az aks show -g RG -n CLUSTER
--query "{fqdn:fqdn, ranges:apiServerAccessProfile.authorizedIpRanges, private:apiServerAccessProfile.enablePrivateCluster}" -o json
List every affected cluster you can see with Azure Resource Graph (needs the resource-graph CLI extension):
az graph query -q "resources
| where type =~ 'microsoft.containerservice/managedclusters'
| extend ranges = properties.apiServerAccessProfile.authorizedIPRanges,
isPrivate = coalesce(tobool(properties.apiServerAccessProfile.enablePrivateCluster), false)
| where isPrivate == false and (isnull(ranges) or array_length(ranges) == 0)
| project name, resourceGroup, subscriptionId"
If the policy definition is assigned at that scope, directly or through an initiative, you can also read its compliance results:
az policy state list -d 0e246bcf-5f6f-4f87-bc6f-775d4712c7ea
--filter "complianceState eq 'NonCompliant'" --query "[].resourceId" -o tsv
Finally, prove the endpoint is reachable from an untrusted network. Any HTTP status, even 401 or 403, means the network path is open; 000 means no connection was made:
curl -sk -o /dev/null -w '%{http_code}n' --max-time 10 https://FQDN/readyz
How to fix it
Step 1: build the allow list
The update replaces the whole list, so collect everything before you run it:
- Admin networks: office, VPN and jumpbox egress addresses. Microsoft also suggests a jumpbox in the firewall’s virtual network, with the firewall’s IP addresses authorized.
- Automation: CI/CD agents, GitOps controllers running elsewhere, Terraform runners and any external tool that calls the Kubernetes API.
- The cluster’s own egress IP: in non-private clusters, AKS routes API server traffic through the cluster’s outbound type, and Microsoft recommends authorizing that egress address (load balancer, NAT gateway or firewall).
- Node public IPs: node pools with public IPs must use public IP prefixes, and those prefixes must be added.
Find the outbound type and, for the default load balancer, its public IPs. For a managed NAT gateway use networkProfile.natGatewayProfile.effectiveOutboundIPs instead; for userDefinedRouting, use your firewall’s public IP.
az aks show -g RG -n CLUSTER --query networkProfile.outboundType -o tsv
for id in $(az aks show -g RG -n CLUSTER
--query "networkProfile.loadBalancerProfile.effectiveOutboundIPs[].id" -o tsv); do
az network public-ip show --ids "$id" --query ipAddress -o tsv
done
Write each CIDR with its network address (for 137.117.106.88 to 137.117.106.95, use 137.117.106.88/29). You can specify up to 200 ranges, or up to 2,000 with API Server VNet Integration. The feature requires the Standard SKU load balancer.
Step 2: save the current setting
az aks show -g RG -n CLUSTER --query apiServerAccessProfile -o json > CLUSTER-apiserver-backup.json
Step 3: apply the authorized IP ranges
Separate ranges with commas. The addresses below are documentation examples; replace them with your own:
az aks update -g RG -n CLUSTER
--api-server-authorized-ip-ranges 198.51.100.0/24,203.0.113.25/32,192.0.2.44/32
The same change in Azure PowerShell:
Set-AzAksCluster -ResourceGroupName RG -Name CLUSTER `
-ApiServerAccessAuthorizedIpRange '198.51.100.0/24','203.0.113.25/32','192.0.2.44/32'
In the portal: open the cluster, go to Settings > Networking, select Manage under Resource settings, edit Authorized IP ranges and select Save. Microsoft notes the rules can take up to two minutes to propagate.
New clusters: consider a private AKS cluster
A private AKS cluster gives the API server a private IP and also satisfies the policy:
az aks create -g RG -n CLUSTER --load-balancer-sku standard
--enable-private-cluster --generate-ssh-keys
Plan connectivity first: clients need VPN, ExpressRoute, peering or Bastion access to the cluster network, or az aks command invoke. Microsoft-hosted Azure DevOps agents are not supported with private clusters, so use self-hosted agents.
Existing clusters: private mode through API Server VNet Integration
An existing AKS Standard cluster can switch to private mode only after it has API Server VNet Integration. Microsoft warns that enabling it is one-way, needs a manual stop and start right away, runs a node image upgrade that restarts workloads, and changes the API server IP address. Treat it as a maintenance window project:
az aks update -g RG -n CLUSTER --enable-apiserver-vnet-integration --apiserver-subnet-id SUBNET_ID
az aks stop -g RG -n CLUSTER
az aks start -g RG -n CLUSTER
az aks update -g RG -n CLUSTER --enable-private-cluster
--enable-apiserver-vnet-integration --apiserver-subnet-id SUBNET_ID
How to verify the fix and rescan
- Re-run az aks show … –query apiServerAccessProfile.authorizedIpRanges and confirm the list.
- From an authorized network, run kubectl get nodes and check that all nodes are Ready.
- From an unauthorized network, repeat the curl test; it should now return 000, and kubectl get nodes –request-timeout=15s should fail.
- Ask Azure Policy to re-evaluate with az policy state trigger-scan -g RG, then repeat the az policy state list query.
- Defender for Cloud refreshes on its next assessment, not the moment you save. To check the recommendation across subscriptions:
az graph query -q "securityresources
| where type == 'microsoft.security/assessments'
| where properties.displayName == 'Kubernetes API server should be configured with restricted access'
| where properties.status.code == 'Unhealthy'
| project id, subscriptionId"
What can break and how to roll back
- Cluster to control plane traffic: if the egress IP is missing, the cluster’s own traffic to the API server is blocked. Microsoft documents that AKS adds the load balancer outbound IP automatically when the feature is enabled at cluster creation; do not assume the same on updates, and always list NAT gateway or firewall egress yourself.
- Outbound type changes: moving to a NAT gateway or user-defined routing changes the egress IP. Microsoft says to add the new range to the authorized list.
- Pipelines on hosted agents: their egress addresses are not a small fixed set, so deployments start failing. Self-hosted agents with a known egress IP are the usual answer.
- Portal views: the Kubernetes resources view in the Azure portal needs the public IP of the machine you browse from.
- People on dynamic IPs: route admins through a VPN or jumpbox with a fixed address instead of adding home IPs one by one.
Rolling back is safe because the setting is changed through Azure Resource Manager, not the Kubernetes API: a list that is too narrow never locks you out of editing it. Re-apply the previous list from the backup file with az aks update, or, as a last resort, remove the restriction entirely (the finding returns):
az aks update -g RG -n CLUSTER --api-server-authorized-ip-ranges ""
Common false positive reasons
- Stale evaluation: the ranges were set or the cluster was made private recently and the policy or Defender has not re-assessed it yet.
- “Our NSG or firewall protects it”: not a false positive. In a standard AKS cluster the API server runs in AKS-managed infrastructure, so rules on your node subnet do not filter who reaches its public endpoint.
- Intentionally public: a lab or demo cluster may be open by design. That is accepted risk; record it as an exemption rather than disabling the policy.
- The reverse case: a Healthy result only proves a list exists. Review the ranges themselves during audits.
FAQ
Do authorized IP ranges replace Entra ID and RBAC?
No. They decide who can reach the endpoint; authentication and RBAC still decide what a caller can do. Keep both.
What does 0.0.0.0/32 mean?
It is a placeholder that allows only the outbound public IP of the cluster’s Standard load balancer. It satisfies the check but also blocks kubectl from every outside network.
Can I use authorized IP ranges on a private cluster?
No. The ranges apply only to a public API server endpoint, and the feature is not supported with private clusters.
Can I convert an existing public cluster to private?
Only through API Server VNet Integration, which is one-way and needs a restart. Otherwise, restrict the public endpoint with authorized ranges.
Tracking this finding across many hosts
If your cloud posture scanner is among the 16 that SITEY imports findings from, these results can sit in one self-hosted list next to your host vulnerabilities. Duplicates are merged per scanner, not across scanners, so a cluster flagged by two different tools appears twice. Its AI triage can suggest likely false positives with evidence, and a human makes the final call.
Sources
- Microsoft Learn: container security recommendations in Defender for Cloud
- Microsoft Learn: API server authorized IP ranges in AKS
- Microsoft Learn: Create a private AKS cluster
- Microsoft Learn: API Server VNet Integration in AKS
- Azure Policy: Authorized IP ranges should be defined on Kubernetes Services (definition JSON)