Best practices for GKE RBAC


This page gives you good practices for planning your role-based access control (RBAC) policies. To learn how to implement RBAC in Google Kubernetes Engine (GKE), refer to Configure role-based access control.

RBAC is a core security feature in Kubernetes that lets you create fine-grained permissions to manage what actions users and workloads can perform on resources in your clusters. As a platform administrator, you create RBAC roles and bind those roles to subjects, which are authenticated users such as service accounts or Google Groups.

Before you begin

Ensure that you're familiar with the following concepts:

For a checklist of this guidance, see Checklist summary.

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 a ClusterRole to a user or a group for all namespaces in the cluster.
  • RoleBinding: bind a Role or a ClusterRole to a user or a group within a specific namespace.

You define permissions as rules in a Role or a ClusterRole. Each rules field in a role consists of an API group, the API resources within that API group, and the verbs (actions) allowed on those resources. Optionally, you can scope verbs to named instances of API resources by using the resourceNames field. For an example, see Restrict access to specific resource instances.

After defining a role, you use a RoleBinding or a ClusterRoleBinding to bind the role to a subject. Choose the type of binding based on whether you want to grant 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 privilege and grant the minimum permissions needed to perform a task. Using the principle of least privilege reduces the potential for privilege escalation if your cluster is compromised, and reduces the likelihood that excessive access results in a security incident.

When designing your roles, carefully consider common privilege escalation risks, such as escalate or bind verbs, create access for PersistentVolumes, or create access for Certificate Signing Requests. For a list of risks, refer to Kubernetes RBAC - privilege escalation risks.

Avoid default roles and groups

Kubernetes creates a set of default ClusterRoles and ClusterRoleBindings that you can use for API discovery and to enable managed component functionality. The permissions granted by these default roles might be extensive depending on the role. Kubernetes also has a set of default users and user groups, identified by the system: prefix. By default, Kubernetes and GKE automatically bind these roles to the default groups and to various subjects. For a full list of the default roles and bindings that Kubernetes creates, refer to Default roles and role bindings.

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

Name Type Description
cluster-admin ClusterRole Grants a subject permission to do anything on any resource in the cluster.
system:anonymous User

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:unauthenticated Group

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:authenticated Group

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 from system: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:masters Group

Kubernetes assigns the cluster-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 security posture. For example:

  • Binding the default cluster-admin ClusterRole to the system:unauthenticated group gives any unauthenticated users access to all resources in the cluster (including Secrets). These highly-privileged bindings are actively targeted by attacks such as mass malware campaigns.
  • Binding a custom Role to the system:unauthenticated group gives unauthenticated users the permissions granted by that Role.

When possible, use the following guidelines:

  • Don't add your own subjects to the system:masters group.
  • Don't bind the system:unauthenticated group to any RBAC roles.
  • Don't bind the system:authenticated group to any RBAC roles.
  • Don't bind the system:anonymous user to any RBAC roles.
  • Don't bind the cluster-admin ClusterRole to your own subjects or to any of the default users and groups. If your application requires many permissions, determine the exact permissions required and create a specific role for that purpose.
  • Evaluate the permissions granted by other default roles before binding subjects.
  • Evaluate the roles bound to default groups before modifying the members of those groups.

Detect and prevent usage of default roles and groups

You should assess your clusters to identify whether you've bound the system:anonymous user or the system:unauthenticated or system:authenticated groups using both ClusterRoleBindings and RoleBindings.

ClusterRoleBindings
  1. List the names of any ClusterRoleBindings with the subject system:anonymous, system:unauthenticated, or system:authenticated:

    kubectl get clusterrolebindings -o json \
      | 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 following for each additional binding. If your output doesn't contain non-default bindings, skip the following steps.

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

    kubectl get clusterrolebinding CLUSTER_ROLE_BINDING_NAME -o json \
        | jq ' .roleRef.name +" " + .roleRef.kind' \
        | sed -e 's/"//g' \
        | xargs -l bash -c 'kubectl get $1 $0 -o yaml'
    

    Replace CLUSTER_ROLE_BINDING_NAME with the name of the non-default ClusterRoleBinding.

    The output is similar to the following:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    ...
    rules:
    - apiGroups:
      - ""
      resources:
      - secrets
      verbs:
      - get
      - watch
      - list
    

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

  3. Delete an unsafe binding from your cluster:

    kubectl delete clusterrolebinding CLUSTER_ROLE_BINDING_NAME
    

    Replace CLUSTER_ROLE_BINDING_NAME with the name of the ClusterRoleBinding to delete.

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

    kubectl get rolebindings -A -o json \
      | 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 be blank. If the output contains any non-default bindings, do the following steps for each additional binding. If your output is blank, skip the following steps.

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

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

    Replace ROLE_BINDING_NAME with the name of the non-default RoleBinding.

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

    kubectl get rolebinding ROLE_BINDING_NAME --namespace ROLE_BINDING_NAMESPACE -o json \
        | jq ' .roleRef.name +" " + .roleRef.kind' \
        | sed -e 's/"//g' \
        | xargs -l bash -c 'kubectl get $1 $0 -o yaml --namespace ROLE_BINDING_NAMESPACE'
    

    Replace the following:

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

    The output is similar to the following:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    ...
    rules:
    - apiGroups:
      - ""
      resources:
      - secrets
      verbs:
      - get
      - watch
      - list
    

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

  3. Delete an unsafe binding from your cluster:

    kubectl delete rolebinding ROLE_BINDING_NAME --namespace ROLE_BINDING_NAMESPACE
    

    Replace the following:

    • ROLE_BINDING_NAME: the name of the RoleBinding to delete.
    • ROLE_BINDING_NAMESPACE: the namespace of the RoleBinding to delete.

Scope permissions to the namespace level

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

  • To grant access to resources in one namespace, use a Role with a RoleBinding.
  • To grant access to resources in more than one namespace, use a ClusterRole with a RoleBinding for each namespace.
  • To grant access to resources in every namespace, use a ClusterRole with a ClusterRoleBinding.

Grant permissions in as few namespaces as possible.

Don't use wildcards

The * character is a wildcard that applies to everything. Avoid using wildcards in your rules. Explicitly specify API groups, resources, and verbs in RBAC rules. For example, specifying * in the verbs field would grant get, list, watch, patch, update, deletecollection, and delete permissions on the resources. The following table shows examples of avoiding wildcards in your rules:

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

Grants get, list, and watch verbs specifically to the apps and extensions API groups.

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

Grants the verbs to deployments in any API group.

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

Grants only get, list, and watch verbs to deployments in the apps and extensions API groups.

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

Grants all verbs, including patch or delete.

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

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

  1. Draft separate RBAC rules for each verb on each resource that a subject needs to access.
  2. After drafting the rules, analyze the rules to check whether multiple rules have the same verbs 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 grant the same verbs to multiple resources are combined, and rules that grant different verbs to resources are separate.

For example, if your workload needs get permissions for the deployments resource, but needs list and watch on the daemonsets resources, you should use separate rules when creating a role. When you bind the RBAC role to your workload, it won't be able to use watch on deployments.

As another example, if your workload needs get and watch on both the pods resource and the daemonsets resource, you can combine those into a single rule, because the workload needs the same verbs on both resources.

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

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

Grants get access for Deployments and watch and list 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 require list access on deployments 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 the daemonsets and deployments 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 the resourceNames field in your rules to restrict access to a specific named instance of a resource. For example, if you're writing an RBAC role that needs to update the seccomp-high ConfigMap and nothing else, you can use resourceNames to specify only that ConfigMap. Use resourceNames whenever possible.

Recommended Not recommended
- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["seccomp-high"]
    verbs: ["update"]

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

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

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

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

Grants list access to all ConfigMaps in the namespace, including seccomp-high. Restricts update access to only the seccomp-high ConfigMap. The rules are split because you can't grant list for named resources.

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

Grants update access for all ConfigMaps, along with list access.

Don't let service accounts modify RBAC resources

Do not bind Role or ClusterRole resources that have bind, escalate, create, update, or patch permissions on the rbac.authorization.k8s.io API group to service accounts in any namespace. escalate and bind in particular can let an attacker bypass the escalation 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 a least-privilege Role or ClusterRole to that service account.

Don't use the default service account

Kubernetes creates a service account named default in every namespace. The default service account is automatically assigned to Pods that don't explicitly specify a service account in the manifest. Avoid binding a Role or ClusterRole to the default service account. Kubernetes might assign the default service account to a Pod that doesn't need the access granted in those roles.

Don't automatically mount service account tokens

The automountServiceAccountToken field in the Pod specification tells Kubernetes to inject a credential token for a Kubernetes service account into the Pod. The Pod can use this token to make authenticated requests to the Kubernetes API server. The default value for this field is true.

In all GKE versions, set automountServiceAccountToken=false in the Pod specification if your Pods don't need to communicate with the API server.

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 mounts this token on the Pod as a projected volume unless you set the automountServiceAccountToken field to false in the Pod specification. Any calls to the Kubernetes API from the Pod use this token to authenticate to the API server.

If you're manually retrieving service account tokens, avoid using Kubernetes Secrets to store the token. Secret-based service account tokens are legacy credentials that don't expire and aren't rotated automatically. If you need credentials for service accounts, use the TokenRequest API to obtain short-lived tokens that are automatically rotated.

Continuously review RBAC permissions

Review your RBAC roles and access regularly to identify potential escalation pathways and redundant rules. For example, consider a situation where you don't delete a RoleBinding that binds a Role with special privileges to a deleted user. If an attacker creates a user account in that namespace with the same name as the deleted user, they'd be bound to that Role and would inherit the same access. Periodic reviews minimize this risk.

Checklist summary

What's next