Best practices for GKE RBAC

This document is about good practices for planning your role-based accesscontrol (RBAC) policies in Google Kubernetes Engine (GKE) (GKE). Thisdocument assumes that you know about the following:

RBAC is a core security feature in Kubernetes that lets you create fine-grainedpermissions to manage what actions users and workloads can perform on resourcesin your clusters. You create RBACroles andbind those roles tosubjects,which are authenticated users such as service accounts or Google Groups.

This document is for Security specialists and Operators who plan andimplement RBAC policies for their organization. To learn more about common rolesand example tasks that we reference in Google Cloud content, seeCommon GKE user roles and tasks.

For a checklist of the guidance in this document, seeChecklist summary.

To learn how to implement RBAC in Google Kubernetes Engine (GKE), seeConfigure role-based access control.

How RBAC works

RBAC supports the following types of roles and bindings:

  • ClusterRole: a set of permissions that can be applied to any namespace,or to the entire cluster.
  • Role: a set of permissions that is limited to a single namespace.
  • ClusterRoleBinding: bind aClusterRole to a user or a group for allnamespaces in the cluster.
  • RoleBinding: bind aRole or aClusterRole to a user or a groupwithin a specific namespace.

You define permissions asrules in aRole or aClusterRole. Eachrulesfield in a role consists of an API group, the API resources within that APIgroup, and the verbs (actions) allowed on those resources. Optionally, youcan scope verbs to named instances of API resources by using theresourceNamesfield. For an example, seeRestrict access to specific resource instances.

After defining a role, you use aRoleBinding or aClusterRoleBinding to bindthe role to a subject. Choose the type of binding based on whether you want togrant permissions in a single namespace or in multiple namespaces.

RBAC role design

Use the principle of least privilege

When assigning permissions in an RBAC role, use the principle of least privilegeand grant the minimum permissions needed to perform a task. Using the principleof least privilege reduces the potential for privilege escalation if yourcluster is compromised, and reduces the likelihood that excessive access resultsin a security incident.

When designing your roles, carefully consider common privilege escalation risks,such asescalate orbind verbs,create access for PersistentVolumes, orcreate access for Certificate Signing Requests. For a list of risks, refer toKubernetes RBAC - privilege escalation risks.

Avoid default roles and groups

Kubernetes creates a set of default ClusterRoles and ClusterRoleBindings thatyou can use for API discovery and to enable managed component functionality. Thepermissions granted by these default roles might be extensive depending on therole. Kubernetes alsohas a set of default users and user groups, identified by thesystem: prefix.By default, Kubernetes and GKE automatically bind these roles tothe default groups and to various subjects. For a full list of the default rolesand bindings that Kubernetes creates, refer toDefault roles and role bindings.

The following table describes some default roles, users, and groups. Werecommend that you avoid interacting with these roles, users, and groups unlessyou've carefully evaluated them, because interacting with these resources can haveunintended consequences to your cluster's security posture.

NameTypeDescription
cluster-adminClusterRoleGrants a subject permission to do anything on any resource in the cluster.
system:anonymousUser

Kubernetes assigns this user to API server requests that have no authentication information provided.

Binding a role to this user gives any unauthenticated user the permissions granted by that role.

system:unauthenticatedGroup

Kubernetes assigns this group to API server requests that have no authentication information provided.

Binding a role to this group gives any unauthenticated user the permissions granted by that role.

system:authenticatedGroup

GKE assigns this group to API server requests made by any user who is signed in with a Google Account, including all Gmail accounts. In practice, this isn't meaningfully different fromsystem:unauthenticated because anyone can create a Google Account.

Binding a role to this group gives any user with a Google Account, including all Gmail accounts, the permissions granted by that role.

system:mastersGroup

Kubernetes assigns thecluster-admin ClusterRole to this group by default to enable system functionality.

Adding your own subjects to this group gives those subjects access to do anything to any resource in your cluster.

If possible, avoid creating bindings that involve the default users, roles,and groups. This can have unintended consequences to your cluster's securityposture. For example:

  • Binding the defaultcluster-admin ClusterRole to thesystem:unauthenticated group gives any unauthenticated users access to allresources in the cluster (including Secrets). These highly-privilegedbindings are actively targeted by attacks such as mass malware campaigns.
  • Binding a custom Role to thesystem:unauthenticated group givesunauthenticated users the permissions granted by that Role.

When possible, use the following guidelines:

  • Don't add your own subjects to thesystem:masters group.
  • Don't bind thesystem:unauthenticated group to any RBAC roles.
  • Don't bind thesystem:authenticated group to any RBAC roles.
  • Don't bind thesystem:anonymous user to any RBAC roles.
  • Don't bind thecluster-admin ClusterRole to your own subjects or to any ofthe default users and groups. If your application requires manypermissions, determine the exact permissions required and create aspecific role for that purpose.
  • Evaluate the permissions granted by other default roles before bindingsubjects.
  • Evaluate the roles bound to default groups before modifying the members ofthose groups.

Prevent usage of default groups

You can use the gcloud CLI to disable non-default RBAC bindings in acluster that reference thesystem:unauthenticated andsystem:authenticatedgroups or thesystem:anonymous user. Use one or both of the following flagswhen you create a new GKE cluster or update an existing cluster.Using these flags doesn't disable the default Kubernetes bindings that referencethese groups. These flags require GKE version 1.30.1-gke.1283000or later.

Caution: Updating an existing cluster with these flags doesn't delete existingnon-default bindings that reference these groups. After updating your cluster,find and remove non-default bindings. For details, seeDetect and remove usage of default roles and groups.

Detect and remove usage of default roles and groups

Note: To help secure your clusters against mass malware attacks that exploitcluster-admin access misconfigurations, GKE clusters runningversion 1.28 and later won't allow you to bind thecluster-admin ClusterRoleto thesystem:anonymous user or to thesystem:unauthenticated orsystem:authenticated groups.

To check whether your clusters reference these users and groups in RBACbindings, enable the standard tier of Kubernetes security posture scanning foryour clusters or fleet so that GKE can show you results in thesecurity posture dashboard in the Google Cloud console. For instructions,seeEnable workload configuration auditing.

The following sections show you how to find the specific RoleBindings orClusterRoleBindings that reference default users and groups, and how to deletethose resources.

ClusterRoleBindings
  1. List the names of any ClusterRoleBindings with the subjectsystem:anonymous,system:unauthenticated, orsystem:authenticated:

    kubectlgetclusterrolebindings-ojson\|jq-r'["Name"], ["-----"], (.items[] | select((.subjects | length) > 0) | select(any(.subjects[]; .name == "system:anonymous" or .name == "system:unauthenticated" or .name == "system:authenticated")) | [.metadata.namespace, .metadata.name]) | @tsv'

    The output should list only the following ClusterRoleBindings:

    Name----"system:basic-user""system:discovery""system:public-info-viewer"

    If the output contains additional non-default bindings, do the followingforeach additional binding. If your output doesn't contain non-defaultbindings, skip the following steps.

  2. List the permissions of the role associated with the binding:

    kubectlgetclusterrolebindingCLUSTER_ROLE_BINDING_NAME-ojson\|jq' .roleRef.name +" " + .roleRef.kind'\|sed-e's/"//g'\|xargs-lbash-c'kubectl get $1 $0 -o yaml'

    ReplaceCLUSTER_ROLE_BINDING_NAME with the name ofthe non-default ClusterRoleBinding.

    The output is similar to the following:

    apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata:...rules:- apiGroups:  - ""  resources:  - secrets  verbs:  - get  - watch  - list

    If you determine that the permissions in the output are safe to grant to thedefault users or groups, no further action is required. If you determinethat the permissions granted by the binding are unsafe, proceed to the nextstep.

  3. Delete an unsafe binding from your cluster:

    kubectldeleteclusterrolebindingCLUSTER_ROLE_BINDING_NAME

    ReplaceCLUSTER_ROLE_BINDING_NAME with the name ofthe ClusterRoleBinding to delete.

RoleBindings
  1. List the namespace and name of any RoleBindings with the subjectsystem:anonymous,system:unauthenticated, orsystem:authenticated:

    kubectlgetrolebindings-A-ojson\|jq-r'["Namespace", "Name"], ["---------", "-----"], (.items[] | select((.subjects | length) > 0) | select(any(.subjects[]; .name == "system:anonymous" or .name == "system:unauthenticated" or .name == "system:authenticated")) | [.metadata.namespace, .metadata.name]) | @tsv'

    If your cluster is configured correctly, the output should beblank.If the output contains any non-default bindings, do the followingsteps foreach additional binding. If your output is blank, skip thefollowing steps.

    If you only know the name of the RoleBinding then you can use thefollowing command to find matching rolebindings across all namespaces:

    kubectlgetrolebindings-A-ojson\|jq-r'["Namespace", "Name"], ["---------", "-----"], (.items[] | select((.subjects | length) > 0) | select(.metadata.name == "ROLE_BINDING_NAME") | [.metadata.namespace, .metadata.name]) | @tsv'

    ReplaceROLE_BINDING_NAME with the name of the non-default RoleBinding.

  2. List the permissions of the Role associated with the binding:

    kubectlgetrolebindingROLE_BINDING_NAME--namespaceROLE_BINDING_NAMESPACE-ojson\|jq' .roleRef.name +" " + .roleRef.kind'\|sed-e's/"//g'\|xargs-lbash-c'kubectl get $1 $0 -o yaml --namespaceROLE_BINDING_NAMESPACE'

    Replace the following:

    • ROLE_BINDING_NAME: the name of the non-defaultRoleBinding.
    • ROLE_BINDING_NAMESPACE: the namespace of thenon-default RoleBinding.

    The output is similar to the following:

    apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata:...rules:- apiGroups:  - ""  resources:  - secrets  verbs:  - get  - watch  - list

    If you determine that the permissions in the output are safe to grant to thedefault users or groups, no further action is required. If you determinethat the permissions granted by the binding are unsafe, proceed to the nextstep.

  3. Delete an unsafe binding from your cluster:

    kubectldeleterolebindingROLE_BINDING_NAME--namespaceROLE_BINDING_NAMESPACE

    Replace the following:

    • ROLE_BINDING_NAME: the name of the RoleBindingto delete.
    • ROLE_BINDING_NAMESPACE: the namespace of theRoleBinding to delete.

Scope permissions to the namespace level

Use bindings and roles as follows, depending on the needs of your workload oruser:

  • To grant access to resources inone namespace, use aRole with aRoleBinding.
  • To grant access to resources inmore than one namespace, use aClusterRole with aRoleBinding for each namespace.
  • To grant access to resources inevery namespace, use aClusterRolewith aClusterRoleBinding.

Grant permissions in as few namespaces as possible.

Don't use wildcards

The* character is awildcard that applies to everything. Avoid usingwildcards in your rules. Explicitly specify API groups, resources, and verbs inRBAC rules. For example, specifying* in theverbs field would grantget,list,watch,patch,update,deletecollection, anddelete permissionson the resources. The following table shows examples of avoiding wildcards inyour rules:

RecommendedNot recommended
-rules:apiGroups:["apps","extensions"]resources:["deployments"]verbs:["get","list","watch"]

Grantsget,list, andwatch verbs specifically to theapps andextensions API groups.

-rules:apiGroups:["*"]resources:["deployments"]verbs:["get","list","watch"]

Grants the verbs todeployments in any API group.

-rules:apiGroups:["apps","extensions"]resources:["deployments"]verbs:["get","list","watch"]

Grants onlyget,list, andwatch verbs to deployments in theapps andextensions API groups.

-rules:apiGroups:["apps","extensions"]resources:["deployments"]verbs:["*"]

Grants all verbs, includingpatch ordelete.

Use separate rules to grant least-privilege access to specific resources

When planning your rules, try the following high-level steps for a moreefficient least-privilege rule design in each role:

  1. Draft separate RBAC rules for each verb on each resource that a subjectneeds to access.
  2. After drafting the rules, analyze the rules to check whether multiple ruleshave the sameverbs list. Combine those rules into a single rule.
  3. Keep all the remaining rules separate from each other.

This approach results in a more organized rule design, where rules that grantthe same verbs to multiple resources are combined, and rules that grantdifferent verbs to resources are separate.

For example, if your workload needs get permissions for thedeploymentsresource, but needslist andwatch on thedaemonsets resources, you shoulduse separate rules when creating a role. When you bind the RBAC role to yourworkload, it won't be able to usewatch ondeployments.

As another example, if your workload needsget andwatch on both thepodsresource and thedaemonsets resource, you can combine those into a singlerule, because the workload needs the same verbs on both resources.

In the following table, both rule designs work, but the split rules moregranularly restrict resource access based on your needs:

RecommendedNot recommended
-rules:apiGroups:["apps"]resources:["deployments"]verbs:["get"]-rules:apiGroups:["apps"]resources:["daemonsets"]verbs:["list","watch"]

Grantsget access for Deployments andwatch andlist access for DaemonSets. Subjects can't list Deployments.

-rules:apiGroups:["apps"]resources:["deployments","daemonsets"]verbs:["get","list","watch"]

Grants the verbs to both Deployments and DaemonSets. A subject who might not requirelist access ondeployments objects would still get that access.

-rules:apiGroups:["apps"]resources:["daemonsets","deployments"]verbs:["list","watch"]

Combines two rules because the subject needs the same verbs for both thedaemonsets anddeployments resources.

-rules:apiGroups:["apps"]resources:["daemonsets"]verbs:["list","watch"]-rules:apiGroups:["apps"]resources:["deployments"]verbs:["list","watch"]

These split rules would have the same result as the combined rule, but would create unnecessary clutter in your role manifest

Restrict access to specific resource instances

RBAC lets you use theresourceNames field in your rules to restrict access toa specific named instance of a resource. For example, if you're writing an RBACrole that needs toupdate theseccomp-high ConfigMap and nothing else, youcan useresourceNames to specify only that ConfigMap. UseresourceNameswhenever possible.

Note: You can't useresourceNames forlist andcreate verbs. For example,if you're writing a role that needs tolist all ConfigMaps in addition toupdating theseccomp-high ConfigMap, you need to split the rules.
RecommendedNot recommended
-rules:apiGroups:[""]resources:["configmaps"]resourceNames:["seccomp-high"]verbs:["update"]

Restricts the subject to only update theseccomp-high ConfigMap. The subject can't update any other ConfigMaps in the namespace.

-rules:apiGroups:[""]resources:["configmaps"]verbs:["update"]

The subject can update theseccomp-high ConfigMap and any other ConfigMap in the namespace.

-rules:apiGroups:[""]resources:["configmaps"]verbs:["list"]-rules:apiGroups:[""]resources:["configmaps"]resourceNames:["seccomp-high"]verbs:["update"]

Grantslist access to all ConfigMaps in the namespace, includingseccomp-high. Restrictsupdate access to only theseccomp-high ConfigMap. The rules are split because you can't grantlist for named resources.

-rules:apiGroups:[""]resources:["configmaps"]verbs:["update","list"]

Grantsupdate access for all ConfigMaps, along withlist access.

Don't let service accounts modify RBAC resources

Do not bindRole orClusterRole resources that havebind,escalate,create,update, orpatch permissions on therbac.authorization.k8s.ioAPI group to service accounts in any namespace.escalate andbind inparticular can let an attacker bypass theescalation prevention mechanisms built into RBAC.

Kubernetes service accounts

Create a Kubernetes service account for each workload

Create a separate Kubernetes service account for each workload. Bind aleast-privilegeRole orClusterRole to that service account.

Don't use the default service account

Kubernetes creates a service account nameddefault in every namespace. Thedefault service account is automatically assigned to Pods that don'texplicitly specify a service account in the manifest. Avoid binding aRole orClusterRole to thedefault service account. Kubernetes might assign thedefaultservice account to a Pod that doesn't need the access granted in those roles.

Don't automatically mount service account tokens

TheautomountServiceAccountToken field in the Pod specification tellsKubernetes to inject a credential token for a Kubernetes service account intothe Pod. The Pod can use this token to make authenticated requests to theKubernetes API server. The default value for this field istrue.

In all GKE versions, setautomountServiceAccountToken=false inthe Pod specification if your Pods don't need to communicate with the APIserver.

Prefer ephemeral tokens over Secret-based tokens

By default, the kubelet process on the node retrieves a short-lived,automatically rotating service account token for each Pod. The kubelet mountsthis token on the Pod as aprojected volumeunless you set theautomountServiceAccountToken field tofalse in the Podspecification. Any calls to the Kubernetes API from the Pod use this token toauthenticate to the API server.

If you're manually retrieving service account tokens, avoid using KubernetesSecrets to store the token. Secret-based service account tokens are legacycredentials that don't expire and aren't rotated automatically. If you needcredentials for service accounts, use theTokenRequest API to obtain short-lived tokens that are automatically rotated.

Continuously review RBAC permissions

Review your RBAC roles and access regularly to identify potential escalationpathways and redundant rules. For example, consider a situation where you don'tdelete aRoleBinding that binds aRole with special privileges to a deleteduser. If an attacker creates a user account in that namespace with the same nameas the deleted user, they'd be bound to thatRole and would inherit the sameaccess. Periodic reviews minimize this risk.

Checklist summary

What's next

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2025-12-15 UTC.