Technotes

Technotes for future me

Kubernetes Role Based Access Control RBAC

What is RBAC and why do I need it?

Role based access control RBAC in Kubernetes can be used to define fine-grained permissions in Kubernetes clusters.

So, how does RBAC work exactly? Let’s do a quick revision.

Step 1 — Define a role (Role or ClusterRole) with permissions

  • A Role exists in the Kubernetes namespace it was created, and can give access to resources in this namespace.
  • A ClusterRole exists outside a Kubernetes namespace, and can give access to resources in different namespaces and to cluster-scoped resources.

Step 2 — Create a service account, or use an existing user or group

​​​​​​​Service accounts are Kubernetes objects, which can be created and managed in Kubernetes. Every pod has a service account assigned. As soon as the pod is started, the Service account token is mounted inside the pod, which can then be used to communicate with the kube-apiserver. Users and groups are not Kubernetes objects, and have to be managed outside of Kubernetes.

Step 3: Bind one or more roles to the service account, user or group

A RoleBinding is used to bind a Role to a user or a service account. A RoleBinding exists in the same namespace like the Role it binds. RoleBindings can also bind a ClusterRole. In that case, the permissions granted by the ClusterRole are valid in the namespace the RoleBinding exists. A ClusterRolesBinding is used, if the permissions defined in the ClusterRole should be valid for all namespaces. Now you know how RBAC works. So far so easy, right? ​​​​​​​But let’s discuss this in more detail, because there is more to it than what meets the eye.

Additional information about RBAC can be found on the official Kubernetes documentation:

Using RBAC Authorization

Recommendations for using RBAC

Less is more — ensure least privileges
The most important point to consider in regard to RBAC is least privilege. Every service account or user should only have the minimum rights needed for the job to be done.

This is especially true for permissions to manage Kubernetes secrets, and for permissions to create new pods (which enables access to secrets, but this is another story).

Also be very careful when you assign a ClusterRole with a ClusterRoleBinding to a pod, because this way, you give the pod permissions in every namespace in the whole cluster.

Check permissions

To check the defined permissions, you can use the following tools:

  • To check if a specific user or service account has a specific permission, use the Kubernetes native tool kubectl auth can-i
  • Example: check if service account “demo-sa” from namespace “demo” has the right to get secrets in the namespace “demo”
$ kubectl auth can-i get secrets -n demo --as=system:serviceaccount:demo:demo-sa
yes
  • To check who has a specific permission, use the tool kubectl-who-can from Aqua Security
  • Example: check which service accounts, users or groups can get secrets in the namespace “demo”
$ kubectl who-can get secrets -n demo
ROLEBINDING                    NAMESPACE  SUBJECT    TYPE            SA-NAMESPACE
rb-role-read-secret-demo-sa-1  demo       demo-sa-1  ServiceAccount  demo

Why the default service account is not your friend

What happens, if you do not define a service account for your pod? ​​​​​​​Then the default service account in the namespace where the pod exists is assigned automatically. No problem, right?

Now you need some rights in the Kubernetes cluster, so you bind a role to the default service account. Still no harm done.

But what happens, if someone else creates a new pod in the same namespace, without defining a dedicated service account? The new pod also gets the default service account assigned, and by doing so, all the rights from the role that is bound to it. And that is a problem.

So do not use the default service account. Instead create a dedicated service account for pods that need to have permissions in the Kubernetes cluster.

Service Account example:

service_account.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: demo-sa
  namespace: demo

No rights needed in the Kubernetes cluster

If you have one or more pods that do not need to access the kube-apiserver, and you are sure that this will not change in the future, you can do the following.

We already talked about not using the default service account, and this is also true if your pod does not need access to the kube-apiserver. ​​​​​​​So instead of using the default service account, create one dedicated service account, which you can assign to all pods that do not need to access the kube-apiserver.

*The recommendation for pods that need to access the kube-apiserver is still, to create a dedicated service account for every pod.
Then disable automounting of the service account token.

You can disable automounting of a service account token either in the service account specification, or in the pod specification. If you specify different automountServiceAccountToken values in the service account specification and the pod specification, the pod specification takes precedence.​​​​​​​

  • Disable automounting of a service account token for a service account:

disable_automounting_for_sa.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: no-access-sa
automountServiceAccountToken: false
  • Disable automounting of a service account token for a pod:

disable_automounting_for_pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  serviceAccountName: no-access-sa
  automountServiceAccountToken: false

Role definition best practices

Now we have discussed some important points in regard the RBAC. But there is still one missing: the best way to define roles.

Names are important

The fist recommendation is to give the role a well designed name, which makes it very obvious what the role is used for.

  • E.g. a role that should be able to read secrets, could be named — now please guess what I am going to say — yes, you are right: “read-secrets”. I think you get the point.

Define roles for different permissions, not for different service accounts

The second recommendation is to define roles, and bind all needed roles to a service account, instead of creating one role for every service account.

Let us illustrate this with the following example:

  • We want to give two pods the right to get information about other pods and one of the pods the get secrets. The service account of the first pod is “sa1”, and the service account of the second pod is “sa2”.
  • First, we create the roles “read-pod” and “read-secret”:

read_pod_role.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: read-pod
  namespace: demo
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

read_secret_role.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: read-secret
  namespace: demo
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]
  • Then we bind the role “read-pods” to both service accounts (sa1 and sa2) and bind the role “read-secret” to the second service account (sa2):

read_pod_rolebinding.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pod
  namespace: demo
subjects:
- kind: ServiceAccount
  name: sa1
  namespace: demo
- kind: ServiceAccount
  name: sa2
  namespace: demo
roleRef:
  kind: Role 
  name: read-pod
  apiGroup: rbac.authorization.k8s.io

read_secret_rolebinding.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-secret
  namespace: demo
subjects:
- kind: ServiceAccount
  name: sa2
  namespace: demo
roleRef:
  kind: Role 
  name: read-secret
  apiGroup: rbac.authorization.k8s.io

In the end we check the permissions of the two service accounts:

kubectl auth can-i get pods -n demo --as=system:serviceaccount:demo:sa1
yes
kubectl auth can-i get secrets -n demo --as=system:serviceaccount:demo:sa1
no
kubectl auth can-i get pods -n demo --as=system:serviceaccount:demo:sa2
yes
kubectl auth can-i get secrets -n demo --as=system:serviceaccount:demo:sa2
yes

Summary

In this blog post we discussed what has to be considered when using RBAC in Kubernetes. We talked about the following:

  • Ensure least privilege by giving every service account or user only the minimum rights needed for the job to be done.
  • Do not use the default service account, instead define a dedicated service account for each of your pods that need to access the kube-apiserver.
  • If your pods do not need to access the kube-apiserver, define a service account for these pods and disable automounting of the service account token.
  • Create roles with well-defined names.
  • Define roles for different permissions, not for different service accounts. When you follow the recommendations on this page, you have done an important first step towards a secure Kubernetes cluster.

Source:
https://engineering.dynatrace.com/blog/kubernetes-security-part-1-role-based-access-control-rbac/

Last updated on 18 May 2023
Published on 18 May 2023
Edit on GitHub