Manage Kubernetes Rbac Users With Azure Ad
More Info:
Amazon EKS uses IAM to provide authentication to your Kubernetes cluster through the AWS IAM Authenticator for Kubernetes. You can configure the stock kubectl client to work with Amazon EKS by installing the AWS IAM Authenticator for Kubernetes and modifying your kubectl configuration file to use it for authentication.
Risk Level
Low
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS AKS
- CIS Critical Security Controls v8
- CMMC 2.0
- CSA Cloud Controls Matrix v4
- DPDPA
- Digital Operational Resilience Act (EU)
- Essential 8
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- MITRE ATT&CK (Cloud)
- NIS2 Directive
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- SWIFT Customer Security Controls Framework
- Sarbanes-Oxley IT General Controls
- UK NCSC Cyber Assessment Framework
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Identify how users currently authenticate to the AKS cluster
- On any machine with
azinstalled and access to the subscription, list the cluster and check if Azure AD integration is enabled and which profile it uses:az aks show \--resource-group <RESOURCE_GROUP_NAME> \--name <CLUSTER_NAME> \--query "{aadProfile: aadProfile, oidcIssuerProfile: oidcIssuerProfile}" \--output json - Review whether
aadProfileis present and configured (managed or legacy AAD) and whether OIDC is enabled. If neither is used, users are not centrally managed through Entra ID.
- On any machine with
-
Inventory who currently has access via kubeconfigs and cluster roles
- On any machine with
kubectlconfigured for the cluster, list cluster role bindings and role bindings to see how access is granted:kubectl get clusterrolebindings -o widekubectl get rolebindings --all-namespaces -o wide - Review subjects (users, groups, service accounts). Note any non–Entra ID identities (local certificates, generic “admin” users, etc.) that should instead map to Entra ID users or groups.
- On any machine with
-
Confirm Azure AD is the source of truth for human users
- In the Azure portal, navigate to Microsoft Entra ID → Users and Groups.
- Verify that the people who should have cluster access are represented as Entra ID users and grouped in security groups that map naturally to RBAC roles (e.g., “aks-admins”, “aks-readers”).
- If you see users accessing the cluster who are not in Entra ID or are using personal accounts that are not governed by your org’s Entra ID tenant, plan to migrate them.
-
Design or adjust RBAC to use Entra ID groups instead of direct users
- Decide the mapping between Entra ID security groups and Kubernetes roles (e.g.,
cluster-admin,edit,view, or custom roles). - For each existing binding using Entra ID users directly (subject kind
Userwith Entra ID UPNs/IDs), consider replacing them withGroupsubjects corresponding to Entra ID security groups. - Capture current bindings that need updating:
kubectl get clusterrolebindings -o yaml > /tmp/clusterrolebindings-backup.yamlkubectl get rolebindings --all-namespaces -o yaml > /tmp/rolebindings-backup.yaml
- Decide the mapping between Entra ID security groups and Kubernetes roles (e.g.,
-
Reconfigure the cluster (if needed) to rely on Azure AD for user auth
- If Step 1 showed no or legacy AAD integration and your org requires Entra ID–backed access, plan a configuration change via your chosen method (Azure Portal,
azCLI, or IaC such as ARM/Bicep/Terraform) to:- Enable managed Azure AD integration and/or OIDC issuer as appropriate for your AKS version.
- Disable or phase out local admin credentials and client certificate–based user access where policy allows.
- Because the exact commands and flags differ by AKS version and provisioning method, use your existing cluster deployment pipeline (ARM/Bicep/Terraform template or equivalent) to update the AKS resource instead of ad‑hoc changes, and ensure changes are reviewed and approved.
- If Step 1 showed no or legacy AAD integration and your org requires Entra ID–backed access, plan a configuration change via your chosen method (Azure Portal,
-
Verify that Kubernetes RBAC is now driven by Entra ID identities
- After any changes, on a machine with
azinstalled, obtain credentials as an Entra ID user:az loginaz aks get-credentials \--resource-group <RESOURCE_GROUP_NAME> \--name <CLUSTER_NAME> \--overwrite-existingkubectl auth can-i get pods --all-namespaces - Repeat the
kubectl get clusterrolebindingsandkubectl get rolebindings --all-namespacescommands to confirm subjects are Entra ID users/groups (and ideally groups), and that non–Entra ID or legacy admin identities have been removed or restricted according to your access policy.
- After any changes, on a machine with
Using kubectl
kubectl cannot configure Azure Kubernetes Service integration with Microsoft Entra ID, because this setting is managed at the AKS control-plane / cloud provider level (CLI/portal/IaC). To address this finding, follow the guidance in the Manual Steps section for updating your AKS cluster’s Entra ID and RBAC configuration.
Automation
#!/usr/bin/env bash
#
# Purpose:
# Report how Kubernetes RBAC is being used for user/subject access
# so you can manually review whether access is managed via Azure AD
# (Microsoft Entra ID) identities and groups.
#
# Run on:
# Any machine with kubectl access and the target context selected.
#
# Requirements:
# - kubectl installed and configured
# - jq installed (optional but recommended; script degrades gracefully)
set -euo pipefail
command -v kubectl >/dev/null 2>&1 || {
echo "ERROR: kubectl not found in PATH." >&2
exit 1
}
if ! kubectl version --short >/dev/null 2>&1; then
echo "ERROR: kubectl cannot reach the cluster (check KUBECONFIG/context)." >&2
exit 1
fi
JQ_AVAILABLE=true
command -v jq >/dev/null 2>&1 || JQ_AVAILABLE=false
echo "=== CIS AKS 5.5.1 Review: RBAC Subjects vs Azure AD Identities ==="
echo "Timestamp: $(date -u +"%Y-%m-%dT%H:%M:%SZ")"
echo
###############################################################################
# 1. Cluster role bindings and role bindings: list all subjects
###############################################################################
echo "1) All ClusterRoleBindings with their subjects"
echo "------------------------------------------------------------"
if $JQ_AVAILABLE; then
kubectl get clusterrolebindings -o json | jq -r '
.items[] |
{
name: .metadata.name,
roleRef: (.roleRef.apiGroup + "/" + .roleRef.kind + ":" + .roleRef.name),
subjects: ([.subjects[]? | (.kind + ":" + .name + " (ns=" + (.namespace // "-") + ")")] | join(", "))
} |
"CRB: \(.name)\n Role: \(.roleRef)\n Subjects: \(.subjects // \"<none>\")\n"'
else
kubectl get clusterrolebindings -o wide
fi
echo
echo "2) All RoleBindings with their subjects (all namespaces)"
echo "------------------------------------------------------------"
if $JQ_AVAILABLE; then
kubectl get rolebindings --all-namespaces -o json | jq -r '
.items[] |
{
ns: .metadata.namespace,
name: .metadata.name,
roleRef: (.roleRef.apiGroup + "/" + .roleRef.kind + ":" + .roleRef.name),
subjects: ([.subjects[]? | (.kind + ":" + .name + " (ns=" + (.namespace // "-") + ")")] | join(", "))
} |
"RB: \(.ns)/\(.name)\n Role: \(.roleRef)\n Subjects: \(.subjects // \"<none>\")\n"'
else
kubectl get rolebindings --all-namespaces -o wide
fi
echo
###############################################################################
# 2. Highlight potentially problematic subject patterns
###############################################################################
echo "3) Potentially risky subjects (for manual review)"
echo " NOTE: This is heuristic only; it DOES NOT determine compliance."
echo "------------------------------------------------------------"
# Heuristic filters:
# - system:serviceaccount:* are normal; we keep them for context but they
# are not Azure AD users.
# - system:authenticated, system:unauthenticated, system:masters, etc.
# are broad groups and should be examined closely.
# - User kind with opaque names that are not recognizable AAD UPNs
# may warrant review.
if $JQ_AVAILABLE; then
echo "3a) Subjects that are Kubernetes-wide system groups or users"
kubectl get clusterrolebindings,rolebindings --all-namespaces -o json | jq -r '
.items[] |
. as $obj |
(.subjects[]? |
select(
(.kind == "Group" and (.name | startswith("system:"))) or
(.kind == "User" and (.name | startswith("system:")))
) |
{
kind: .kind,
name: .name,
namespace: (.namespace // $obj.metadata.namespace // "-"),
bindingKind: $obj.kind,
bindingName: $obj.metadata.name,
roleRef: ($obj.roleRef.apiGroup + "/" + $obj.roleRef.kind + ":" + $obj.roleRef.name)
}) |
"Binding: \(.bindingKind)/\(.bindingName) [ns=\(.namespace)]\n Role: \(.roleRef)\n Subject: \(.kind):\(.name)\n"'
echo
echo "3b) User subjects that are NOT obvious Azure AD group names"
echo " (Azure AD usage is environment-specific; validate manually.)"
kubectl get clusterrolebindings,rolebindings --all-namespaces -o json | jq -r '
.items[] |
. as $obj |
(.subjects[]? |
select(.kind == "User") |
{
kind: .kind,
name: .name,
namespace: (.namespace // $obj.metadata.namespace // "-"),
bindingKind: $obj.kind,
bindingName: $obj.metadata.name,
roleRef: ($obj.roleRef.apiGroup + "/" + $obj.roleRef.kind + ":" + $obj.roleRef.name)
}) |
"Binding: \(.bindingKind)/\(.bindingName) [ns=\(.namespace)]\n Role: \(.roleRef)\n User: \(.name)\n"'
else
echo "jq not available; cannot run deep heuristics."
echo "You can still manually inspect RBAC subjects using:"
echo " kubectl get clusterrolebindings -o yaml"
echo " kubectl get rolebindings --all-namespaces -o yaml"
fi
echo
###############################################################################
# 3. Azure AD integration surface (server-side RBAC)
###############################################################################
echo "4) Server-side RBAC enabled on API server (for managed AKS this is usually on)"
echo "------------------------------------------------------------"
# For managed AKS you typically cannot see flags; we rely on API discovery.
if kubectl api-versions | grep -q '^rbac.authorization.k8s.io/'; then
echo "rbac.authorization.k8s.io API group is present: Kubernetes RBAC is enabled."
else
echo "WARNING: rbac.authorization.k8s.io API group NOT found; RBAC may be disabled."
fi
echo
###############################################################################
# 4. Guidance: How to interpret this output
###############################################################################
cat <<'EOF'
INTERPRETING THE OUTPUT (MANUAL REVIEW REQUIRED)
-----------------------------------------------
This script does NOT determine compliance automatically. Use the output to answer:
1) Are human users and teams granted access via Azure AD (Microsoft Entra ID)?
- In AKS with Azure AD integration, subjects commonly appear as:
- kind: User or Group
name: matching your Entra ID UPNs or group display names/object IDs.
- Problem indicators:
- User subjects that are local/service/system-style names not tied to Entra ID.
- Broad groups like "system:authenticated" or "system:unauthenticated"
bound to powerful roles (e.g., cluster-admin).
2) Are service accounts being used only for workloads, not as a substitute for user identity?
- ServiceAccount subjects are expected for pods.
- Problem indicators:
- ServiceAccounts bound to cluster-admin or other wide-scoped roles
when they are used by generic workloads instead of tightly-scoped roles.
3) Are any high-privilege bindings NOT mapped back to Entra ID identities?
- For every binding to cluster-admin or other cluster-wide roles,
verify that User/Group subjects represent specific Entra ID users or groups,
not generic or anonymous subjects.
To investigate a specific binding in detail:
kubectl get clusterrolebinding <name> -o yaml
kubectl get rolebinding -n <namespace> <name> -o yaml
Then compare the 'subjects' to your Entra ID tenant using the Azure Portal or CLI.
EOF