Back to blog
AzureCloud SecurityCompute & ContainersIdentity & AccessKubernetes

AKS RBAC Not Enabled: Why It Matters and How to Fix It

Learn why running an AKS cluster without RBAC exposes secrets and enables lateral movement, plus step-by-step remediation and prevention with IaC and Azure Policy.

TL;DR

This check flags AKS clusters running without Kubernetes role-based access control, which means anyone with cluster access gets broad, unscoped permissions. The fix is to recreate the cluster with RBAC enabled, since it cannot be toggled on after creation.

Role-based access control is the foundation of authorization inside any Kubernetes cluster. When an Azure Kubernetes Service (AKS) cluster runs without it, the cluster has no granular way to decide who can read secrets, delete pods, or modify deployments. Lensix raises aks_norbacc when it detects an AKS cluster where RBAC is disabled, and it is one of the higher-impact findings you can get on a managed Kubernetes environment.

This post walks through what the check looks at, the concrete risks of leaving RBAC off, how to remediate (with the important caveat that AKS does not let you flip this switch on an existing cluster), and how to keep it from regressing.


What this check detects

The aks_norbacc check inspects the configuration of each AKS cluster in your subscription and looks at the enableRBAC property. If that property is set to false, the check fails.

RBAC in Kubernetes governs what authenticated users and service accounts are allowed to do through the Kubernetes API. It works through four object types:

  • Role and ClusterRole define a set of permissions (verbs like get, list, create on specific resources).
  • RoleBinding and ClusterRoleBinding attach those permissions to a user, group, or service account.

With RBAC disabled, none of these objects are enforced. The API server effectively treats every authenticated request as fully authorized, so the principle of least privilege has nothing to act on.

Note: For AKS clusters created after March 2020, RBAC is enabled by default. This finding usually points to older clusters that were provisioned before the default changed, or clusters where it was explicitly disabled in a template. The cluster you are looking at has likely been running for a while.


Why it matters

Without RBAC, the authorization layer of your cluster is essentially open. Anyone who can authenticate to the API server can do anything the API allows. That has a few practical consequences worth spelling out.

Secrets are exposed to everyone

Kubernetes Secrets hold database passwords, API tokens, TLS private keys, and cloud credentials. In an RBAC-enabled cluster you grant secret access to specific workloads and people. Without RBAC, any pod's service account or any developer with kubeconfig access can run:

kubectl get secrets --all-namespaces -o yaml

and walk away with every credential in the cluster. A single compromised application pod becomes a path to your entire secret store.

Lateral movement after a single compromise

Consider a typical attack chain: an attacker exploits a vulnerable web app running in one namespace. With RBAC, the blast radius is limited to whatever that pod's service account can touch. Without RBAC, the attacker uses the default service account token mounted in the pod to query the API server, list every workload, read secrets in other namespaces, and create new privileged pods to escape onto the node.

Danger: On a cluster without RBAC, a compromised pod can typically create a new pod with hostPID, privileged: true, and a host filesystem mount. That is a direct route to the underlying node and, from there, potentially to the cloud instance metadata endpoint and your Azure managed identity credentials.

No audit-friendly separation of duties

Compliance frameworks like CIS, SOC 2, and PCI DSS expect you to demonstrate that access is scoped and least-privileged. A cluster without RBAC fails this on its face. There is no way to show that, for example, the read-only monitoring team cannot delete production workloads, because nothing prevents them from doing so.


How to fix it

Here is the part that trips people up: AKS does not support enabling RBAC on an existing cluster. The enableRBAC setting is immutable after creation. Remediation means creating a new RBAC-enabled cluster and migrating workloads to it.

Warning: This is a migration, not a config change. Plan for it like a cluster upgrade with a maintenance window. You will be standing up a parallel cluster, moving workloads, repointing DNS or ingress, and decommissioning the old one.

Step 1: Create a new cluster with RBAC enabled

Using the Azure CLI, RBAC is on by default, so you simply omit --disable-rbac. To be explicit and create a cluster integrated with Microsoft Entra ID for authentication:

az aks create \
  --resource-group myResourceGroup \
  --name myAKSCluster-rbac \
  --enable-aad \
  --enable-azure-rbac \
  --node-count 3 \
  --generate-ssh-keys

--enable-aad wires the cluster to Entra ID for authentication, and --enable-azure-rbac lets you manage Kubernetes permissions through Azure role assignments rather than only in-cluster RoleBindings. Kubernetes RBAC itself is enabled regardless.

Step 2: Verify RBAC is active

az aks show \
  --resource-group myResourceGroup \
  --name myAKSCluster-rbac \
  --query enableRBAC \
  --output tsv

This should return true. You can also confirm from inside the cluster:

kubectl api-versions | grep rbac.authorization.k8s.io

Step 3: Recreate your roles and bindings

Once RBAC is live, define least-privilege access. A namespace-scoped read-only role for a team looks like this:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: viewer
rules:
  - apiGroups: [""]
    resources: ["pods", "services", "configmaps"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: dev-team-viewer
  namespace: production
subjects:
  - kind: Group
    name: "aad-group-object-id"
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: viewer
  apiGroup: rbac.authorization.k8s.io

Step 4: Migrate workloads and cut over

Apply your manifests or Helm charts to the new cluster, validate that everything is healthy, then repoint ingress and DNS. Keep the old cluster running until you have confirmed the new one is serving traffic correctly.

Danger: Do not delete the old cluster until cutover is fully verified. This command is irreversible and takes all cluster-stored state with it. Confirm you are targeting the old cluster name, not the new one.

az aks delete \
  --resource-group myResourceGroup \
  --name myAKSCluster-norbac \
  --yes --no-wait

How to prevent it from happening again

Since the only fix is a rebuild, prevention is far cheaper than remediation. Bake RBAC into your provisioning and block clusters that lack it before they reach production.

Define clusters in IaC with RBAC explicit

In Terraform, the AKS resource enables RBAC by default. Make it explicit so reviewers can see the intent and so no one can quietly disable it:

resource "azurerm_kubernetes_cluster" "this" {
  name                = "myakscluster"
  location            = azurerm_resource_group.this.location
  resource_group_name = azurerm_resource_group.this.name
  dns_prefix          = "myaks"

  default_node_pool {
    name       = "default"
    node_count = 3
    vm_size    = "Standard_DS2_v2"
  }

  identity {
    type = "SystemAssigned"
  }

  azure_active_directory_role_based_access_control {
    azure_rbac_enabled = true
  }

  role_based_access_control_enabled = true
}

Enforce it with Azure Policy

Azure Policy can audit or deny non-compliant clusters at the subscription or management group level. Assign the built-in policy that requires RBAC on AKS:

az policy assignment create \
  --name "require-aks-rbac" \
  --scope "/subscriptions/<subscription-id>" \
  --policy "ac4a19c2-fa67-49b4-8d5f-44954ce39bb8" \
  --params '{"effect": {"value": "Audit"}}'

Start with Audit to surface existing offenders, then move to Deny once your estate is clean.

Gate it in CI/CD

If you provision with Terraform, scan plans with a policy-as-code tool like Checkov or tfsec before apply. A pipeline step that fails on a missing RBAC setting stops the misconfiguration at the pull request stage:

checkov -d ./infra --check CKV_AZURE_5

Tip: Pair the CI gate with continuous monitoring in Lensix. Pipeline checks catch new code, while ongoing scanning catches clusters created out-of-band through the portal or by other teams who bypassed your IaC.


Best practices

RBAC being enabled is the baseline, not the finish line. A few practices make the authorization layer genuinely effective:

  • Integrate with Microsoft Entra ID. Tie cluster access to your existing identity provider so access follows joiner-mover-leaver processes and benefits from conditional access and MFA.
  • Use Azure RBAC for Kubernetes. Managing permissions through Azure role assignments gives you a single place to audit access across the cluster and the subscription.
  • Scope roles to namespaces. Prefer Role over ClusterRole wherever possible. A team that only works in one namespace should not hold cluster-wide permissions.
  • Disable automounting of default service account tokens for workloads that do not call the Kubernetes API. Set automountServiceAccountToken: false to shrink the attack surface inside pods.
  • Review bindings regularly. Permissions accumulate. Periodically list ClusterRoleBindings and confirm each one is still needed.

RBAC turns "anyone who gets in can do anything" into "each identity can do exactly what its job requires." On a managed Kubernetes cluster, that difference is the line between a contained incident and a full compromise.

Because AKS makes RBAC immutable, the cheapest moment to get this right is at creation. Enforce it in your templates, gate it in your pipeline, and let Lensix watch for any cluster that slips through.