The Bind, Escalate and Impersonate Verbs For Privilege Escalation In The Kubernetes Cluster
Kubernetes role binding plays a critical role in controlling access to resources within a Kubernetes cluster, it grants the permissions defined in a role to a user or set of users. RBAC Role or ClusterRole consists of rules that define a specific set of permissions(in the forms of verbs) including some lesser-known ones like bind
, escalate
, and impersonate
. It's important to have awareness of these permissions and understand their implications in order to effectively manage access control. In this blog post, we'll explore the three specific permissions within RoleBindings/ClusterRoleBindings : bind
, impersonate
, and escalate
. By understanding the risks associated with these permissions and implementing best practices, you can enhance the security of your Kubernetes environment.
RBAC Role and ClusterRole
A RBAC Role is designed to define permissions within a specific namespace, while a ClusterRole, on the other hand, is not tied to any specific namespace and provides permissions across the entire cluster. This distinction is important to understand when defining and managing access control in Kubernetes.
Here’s an example Role in the “default” namespace that can be used to grant read access to pods:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]
A ClusterRole can be used to grant the same permissions as a Role, but it can be cluster-scoped. Below is an example of a ClusterRole that grants read access to secrets within a specific namespace or across all namespaces depending on how it is bound (using RoleBinding or ClusterRoleBinding):
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "watch", "list"]
In both above examples, the operations executed on available resources like Deployments, Services, and Pods in a Kubernetes cluster are called verbs. Other common examples are Create, Read, Update, or Delete (CRUD). There are also specialized verbs as we mentioned in the beginning, bind
, escalate
and impersonate
. bind
and escalate
verbs on roles and clusterroles resources for RBAC, impersonate
verb on users, group, and serviceaccounts, etc. for authentication.
Environment Setup
Now assume we already have an existing context named ‘admin’ in the kubectl configuration and we can use it to operate the kubernetes as a cluster administrator. Before we explore these Verbs, let’s set up a scenario in Kubernetes v1.24 where we create a “dev” service account without any associated roles.
$ kubectl create serviceaccount dev
In Kubernetes versions prior to v1.22, a token for the service account was automatically generated. However, in more recent versions, the default behavior is that newly created service accounts will not have an accompanying token.
$ kubectl describe serviceaccounts/dev
Name: dev
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: <none>
Tokens: <none>
Events: <none>
To create a token for the service account, we can execute the command kubectl create token
. Once the token is created, we can use it to set up a configuration context and utilize it for further operations.
$ TOKEN=$(kubectl create token dev --duration=12h)
$ kubectl config set-credentials dev --token=$TOKEN
$ kubectl config set-context dev --user=dev --cluster=kubernetes
Now we have two contexts for the kubectl configuration, one is enable us to operate the cluster as “admin”, the other is as “dev” service account.
$ kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* admin kubernetes 313846530892193692
dev kubernetes dev
We create a role name “pod-reader” for the tests in the subsequent sections. This “pod-reader” role can “read” the pods in the current (i.e. default) namespace.
kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pods
Please note that the “dev” service account does not have any roles bound to it. In the following demonstration, we will showcase the usage of the bind
, escalate
, and impersonate
permissions.
Bind Verb
To create or update a role binding, you either need to have all the permissions specified in the referenced role or the explicit authorization to use the bind
verb on the referenced role. In other words, to create or update a role binding, you need to meet either of the following conditions:
- You already possess all the permissions specified in the referenced role, at the same scope as the role binding. This means that you have the necessary privileges to perform the required actions defined by the role.
- You have been explicitly authorized to use the
bind
verb on the referenced role. This means that even if you don't have all the permissions in the role, you can still create or update the role binding by having the specific authority to bind the role to the user or group.
The bind privilege grants a user the ability to create/update bindings to cluster roles or roles, resulting in an elevation of the effective permissions within the cluster.
There is an diagram about how the bind
permission enable "dev" service account to bypass of Kubernetes in-built protections.
Next we will proceed to demonstrate the entire process in action.
createBinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: create-binding
rules:
- apiGroups:
- "rbac.authorization.k8s.io"
resources:
- clusterrolebindings
- rolebindings
verbs:
- create
The cluster administrator create the ClusterRole and bind it to “dev” service account.
$ kebectl apply -f createBinding.yaml
$ kubectl create rolebinding create-binding-dev --clusterrole=create-binding --serviceaccount=default:dev
Switch kubectl configuration to “dev” context and try if “dev” service account has the permission to bind the “pod-reader” role to itself.
$ kubectl config use-context dev
Switched to context "dev".
The “dev” service account attempts to bind the “pod-reader” to itself in order to escalate its permissions.
$ kubectl create rolebinding pod-reader-dev --role=pod-reader --serviceaccount=default:dev
error: failed to create rolebinding: rolebindings.rbac.authorization.k8s.io "pod-reader-dev" is forbidden: user "system:serviceaccount:default:dev" (groups=["system:serviceaccounts" "system:serviceaccounts:default" "system:authenticated"]) is attempting to grant RBAC permissions not currently held:
{APIGroups:[""], Resources:["pods"], Verbs:["get" "list" "watch"]}
We can observe that the operation has been rejected as the “dev” service account doesn’t have the necessary permission {APIGroups:[""], Resources:["pods"], Verbs:["get" "list" "watch"]}
to successfully complete the action.
Now let’s explore how the bind
permission can change the situation.
Switch kubectl configuration back to “default” context and create a ClusterRole with bind
permission and bind the clusterrole to "dev" service account.
bind.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: bind
rules:
- apiGroups:
- "rbac.authorization.k8s.io"
resources:
- clusterroles
- roles
verbs:
- bind
$ kubectl config use-context default
$ kubectl apply -f bind.yaml
$ kubectl create rolebinding bind-dev --clusterrole=bind --serviceaccount=default:dev
Now let see the what happens after the bind
permission has been bound to "dev" service account
$ kubectl config use-context dev
$ kubectl create rolebinding pod-reader-dev --role=pod-reader --serviceaccount=default:dev
rolebinding.rbac.authorization.k8s.io/pod-reader-dev created
The “dev” service account has successfully elevated its permissions by binding the “pod-reader” role to itself.
Clean up
Before we move to the next section, we need to run the following commands to delete the resources created in this section.
$ kubectl config use-context "admin"
$ kubectl delete rolebinding pod-reader-dev
$ kubectl delete rolebinding create-binding-dev
$ kubectl delete rolebinding bind-dev
$ kubectl delete clusterrole bind
$ kubectl delete clusterrole create-binding
Escalate Verb
To create or update a role in Kubernetes, one of the following conditions must be met:
- You possess all the permissions specified in the role definition, and these permissions are applicable to the same scope as the object being modified. For instance, if it’s a ClusterRole, you must have cluster-wide permissions. If it’s a Role, you need permissions within the same namespace or cluster-wide.
- You have been explicitly granted the authority to use the
escalate
verb on the "roles" or "clusterroles" resource within the "rbac.authorization.k8s.io" API group.
These conditions ensure that only authorized users with the necessary permissions or explicit escalation rights can create or update roles, maintaining proper access control within the Kubernetes cluster.
The escalate
privilege empowers a user to modify roles or cluster roles to which they are bound, effectively increasing their level of access and privileges.
In this section, we will explore the escalation
verb which allows "dev" service account to modify the role that is bound to itself. This modification will enable the account to elevate its permissions and gain the ability to create pods.
Firstly, as the admin, we need to assign two roles to the “dev” service account. The first role is named “role-editor” and it enables the service account to retrieve and modify roles. The second role is named “pod-reader” and it allows the service account to access information about pods.
$ kubectl config use-context "admin"
$ kubectl create rolebinding pod-reader-dev --role=pod-reader --serviceaccount=default:dev
$ kubectl create role role-editor --verb=get --verb=patch --resource=role
$ kubectl create rolebinding role-editor-dev --role=role-editor --serviceaccount=default:dev
After granted with these roles, the “dev” service account can view the roles, get pod information, but it can’t create pod.
$ kubectl config use-context "dev"
$ kubectl run nginx --image=nginx
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:dev" cannot create resource "pods" in API group "" in the namespace "default"
When the “dev” service account attempts to add the “create” permission to the existing “pod-reader” role, the action fails because it does not have the necessary permissions granted to perform this operation.
$ kubectl edit role pod-reader # add "create" into the list of verbs and save it
error: roles.rbac.authorization.k8s.io "pod-reader" could not be patched: roles.rbac.authorization.k8s.io "pod-reader" is forbidden: user "system:serviceaccount:default:dev" (groups=["system:serviceaccounts" "system:serviceaccounts:default" "system:authenticated"]) is attempting to grant RBAC permissions not currently held:
{APIGroups:[""], Resources:["pods"], Verbs:["create"]}
Now, the escalate
permission comes into the picture. If the "admin" creates the "escalate" role and assigns it to the "dev" service account due to some reason, the service account will gain the ability to escalate its permissions.
escalate.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: escalate
rules:
- apiGroups:
- "rbac.authorization.k8s.io"
resources:
- clusterroles
- roles
verbs:
- escalate
$ kubectl config use-context "admin"
$ kubectl apply -f escalate.yaml
$ kubectl create rolebinding escalate-dev --clusterrole=escalate --serviceaccount=default:dev
Let’s take a look at what the “dev” service account can do this time after being assigned the escalate
permission. With the escalate
permission, the "dev" service account has the ability to modify the "pod-reader" role and add the "create" verb to the list of permissions.
$ kubectl config use-context "dev"
$ kubectl edit role pod-reader # add "create" into the list of verbs and save it
role.rbac.authorization.k8s.io/pod-reader edited
The “dev” service account successfully expands its permission and is able to create a pod by utilizing the updated “pod-reader” role.
$ kubectl run nginx --image=nginx
pod/nginx created
Clean up
We have reached the end of the section highlighting the escalate
permission. To clean up and delete the resources created during this demonstration, please execute the following commands.
$ kubectl config use-context "admin"
$ kubectl delete rolebinding pod-reader-dev
$ kubectl delete role role-editor
$ kubectl delete rolebinding role-editor-dev
$ kubectl delete clusterrole escalate
$ kubectl delete rolebinding escalate-dev
Impersonate Verb
The impersonate
privilege gives an user the ability to assume the identity of other users, thereby acquiring their permissions within the cluster. For example, the impersonation feature in Kubernetes allows an admin to temporarily assume the identity of another user, including their associated roles and permissions. This can be useful for debugging and troubleshooting authorization policies
To quickly assumes another identity, such as the “dev” service account impersonating the built-in admin service account, you can utilize the --as
flag in the kubectl
command. There is no impersonate
permission associated with "dev" account initially, as a result, it is unable to perform the actions successfully.
$ kubectl get pods --all-namespaces
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:dev" cannot list resource "pods" in API group "" at the cluster scope
$ kubectl --as=system:serviceaccount:kube-system:admin get pods --all-namespaces
Error from server (Forbidden): serviceaccounts "admin" is forbidden: User "system:serviceaccount:default:dev" cannot impersonate resource "serviceaccounts" in API group "" in the namespace "kube-system"
To grant the “dev” service account the ability to impersonate other users, we need to create a custom role with the impersonate
permission and then bind it to the service account. Here are the steps to follow:
impersonate.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: impersonate
rules:
- apiGroups:
- ""
resources:
- serviceaccounts
verbs:
- impersonate
Create the ClusterRole and bind it to the “dev” service account.
$ kubectl config use-context "admin"
$ kubectl apply -f impersonate.yaml
$ kubectl create clusterrolebinding impersonate-dev --clusterrole=impersonate --serviceaccount=default:dev
Once these steps are completed, the “dev” service account will have the impersonate
permission and will be able to impersonate other users/service accounts, granting it the ability to perform actions on their behalf.
$ kubectl config use-context "dev"
$ kubectl --as=system:serviceaccount:kube-system:admin get pods --all-namespaces
The diagram in the below illustrates the complete process:
Clean up
To clean up and delete the resources created in this section, please execute the following commands.
$ kubectl config use-context "admin"
$ kubectl delete clusterrolebinding impersonate-dev
$ kubectl delete clusterrole impersonate
Conclusion
In this blog, we explored the intricacies of RBAC permissions in Kubernetes and shed light on the often overlooked bind
, escalate
, and impersonate
permissions. By understanding and properly configuring these permissions, you can ensure a secure and controlled access environment in your Kubernetes deployments. Remember, maintaining strong RBAC practices is crucial to protecting sensitive information, preventing unauthorized access, and safeguarding your Kubernetes clusters. Stay vigilant in managing and reviewing permissions to uphold the security of your infrastructure.