It's a tale as old as time. You have a sparkling new Kubernetes cluster, and you want to give some of your users access to it. But you don't want to give them the keys to the kingdom. You want to give them just enough access to do what they need to do, and no more. This is where Role-Based Access Control (RBAC) comes in. RBAC is a method of regulating access to computer or network resources based on the roles of individual users within your organization. It's a way to ensure that only the right people have access to the right resources at the right times. In this post, we'll take a look at how RBAC works in Kubernetes, and how you can use it to secure your cluster.
In Kubernetes, roles come in two flavors: Role and ClusterRole. On the surface they are very similiar, but they have on major distinciton: scope. A Role is specific to a namespace, while a ClusterRole grants access cluster-wide. Let's take a look at a simple example of a Role. Let's say our developer friend needs only needs to get access to exec into pods in their namespace exciting-app to debug code related issues. We can create a Role that grants them just the access they need and no more. As an example we could create a Role that looks like this:
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: exciting-app
name: pod-exec
rules:
- apiGroups: [""]
resources: ["pods", "pods/logs"]
verbs:
- get
- list
- watch
- apiGroups: [""]
resources: ["pods/exec"]
verbs:
- create
This role grants the developer access to get, list, and watch pods and pod logs, as well as exec into pods. Since specifed the namespace as exciting-app, this role will only apply to that namespace. If we wanted to grant the developer access to exec into pods cluster-wide, we would use a ClusterRole instead. Now that we have the role we can bind it to a user or group using a RoleBinding or ClusterRoleBinding. These users and groups will have been created in your organization's identity provider, such as Active Directory or LDAP or added to the cluster using something like EKS's auth-config configMap. Let's take a look at an example of a RoleBinding that binds our previously created group exciting-app-developers to the pod-exec role we created earlier.
A few good thing to note about the Role and ClusterRole is the apiGroups field. This field is used to specify the API group that the resources belong to. How do you know what API group a resource belongs to? You can get a nice list of all the API groups and resources in your cluster by running the following command. It will give you an exhaustive list of all the resources in your cluster, including their API group, if they are namespaced, shortnames and the verbs you can use on them. This is one of my favorite commands to run when I'm working with a new cluster. It's a great way to get a lay of the land.
$ k api-resources -o wide
NAME SHORTNAMES APIGROUP NAMESPACED KIND VERBS
...
pods po true Pod [create delete deletecollection get list patch update watch]
resourcequotas quota true ResourceQuota [create delete deletecollection get list patch update watch]
secrets true Secret [create delete deletecollection get list patch update watch]
serviceaccounts sa true ServiceAccount [create delete deletecollection get list patch update watch]
validatingwebhookconfigurations admissionregistration.k8s.io false ValidatingWebhookConfiguration [create delete deletecollection get list patch update watch]
customresourcedefinitions crd,crds apiextensions.k8s.io false CustomResourceDefinition [create delete deletecollection get list patch update watch]
controllerrevisions apps
...
This is a highly truncated list of the resources in a cluster, but it gives you a good idea of how to read the output. The NAME column is the name of the resource, the SHORTNAMES column is a list of shortnames for the resource, but the APIGROUP and VERBS columns are the most important. The APIGROUP column gives you the information you'll need to use to fill in the apiGroups field in your Role or ClusterRole. The VERBS column gives you the last bit of the information you need to configure the role. Now that we know where all the information is coming from, let's take a look at the RoleBinding.
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-exec-binding
namespace: exciting-app
subjects:
- kind: Group
name: exciting-app-developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-exec
apiGroup: rbac.authorization.k8s.io
The RoleBinding is pretty straightforward. We're binding the exciting-app-developers group to the pod-exec role. There is no real magic here. It's just a way to bind a user or group to a Role or ClusterRole. If we wanted to bind the group to a ClusterRole, we would change the kind from RoleBinding to a ClusterRoleBinding. Once we apply these resources to the cluster, the exciting-app-developers group will have the access they need to exec into pods in the exciting-app namespace. And that's it! That's all there is to it. You've now granted secure access to your developers to exec into pods in their namespace.
Let's try to take this one step further. We've configured the Role and RoleBinding, but how can we test that it's working? Without
forcing the end user to try and exec into a pod, we can use the kubectl auth can-i
command to
test if the user has the access they need. Here is how we as admins can test if the exciting-app-developers group really has
the access they need to do their jobs. Let's run the command and verify access before we hand it off to the developers.
$ k auth can-i exec pod -n exciting-app --as exciting-app-developers
yes
Awesome, now we know that we can hand off our configuration with confidence. We've tested it and we know it works.
Argocd app of apps definition.
Argo Workflows up and running
Automating workflows with github actions.
Simple Api using gin-gonic.
Building K8s manifests with Helm3
A collection of kubectl commands that have helped me a ton.
Configuring Role Based Access Control in Kubernetes.
Making images smaller with multi stage builds.
Keeping secrets out of your container images even while using private modules