What Is Kubernetes RBAC?
Feb 23, 2022
First, let’s recap quickly what RBAC is in the context of a Kubernetes cluster. RBAC determines whether a certain entity (whether a user or a pod already running inside the cluster) is allowed to perform a certain action on a given resource. This process is divided into three steps, which we will explore now.
First of all, the entity must be authenticated. There are three types of entities in Kubernetes: a user (which is usually a human), a group (which is usually a set of humans), and a service account (which is used by pods inside the cluster). Users need to be created using X.509 certificates. How this is done is outside the scope of this document, but there are many tutorials on the internet detailing how to do this.
Pods that are running inside the cluster don’t need such a certificate. Instead, a service account can be assigned to them. When you create a pod without specifying any account for it, the “default” service account is automatically assigned to the pod. Service accounts are like users, but for processes that run inside pods. You don’t create an X.509 certificate for them; instead, you link pod and service accounts, which is typically done in manifest files. Such a manifest file would look like so:
Once the entity is authenticated, the next step is authorization. RBAC controls whether this entity can call the Kubernetes API to perform a certain action on a certain resource. Such authorization is based on rules, which we will describe in the next section.
The last step is called “admission control.” This is optional and allows the cluster’s administrators to add some custom validations to requests and even manipulate them. This is an advanced topic, and most Kubernetes deployments don’t need to concern themselves with these. Consequently, they won’t be covered in this article.
It is important to note that RBAC is not necessary for Kubernetes resources specified in manifest files. I will give a typical example to clarify this concept. Let’s say you want to create a pod and a secret and give the pod access to the secret. The pod specification in the manifest file specifies that the secret must be “mounted” one way or another (through environment variables or files actually mounted inside the pod). In this example, the pod will be able to access the secret no matter what RBAC permissions you give or don’t give to the pod’s service account. This is because the mounting of such secrets does not depend on RBAC.
Anatomy of RBAC Rules
An RBAC rule is, in essence, made up of three elements: the API group, a verb (i.e., an action), and a target (either a resource name(s) or an API URL). RBAC rules are specified in roles and cluster roles (the difference between the two is that roles are scoped to a given namespace, while cluster roles apply to the whole cluster). This is what an RBAC rule looks like, as part of a YAML file specifying a role or a cluster role:
- apiGroups: [“”]
verbs: [get, list]
The API group identifies which API group to target. This is necessary because different API groups can have the same verbs. Additionally, Kubernetes is highly extensible and allows for the addition of new APIs that can have verbs and resource names that clash with other APIs. In the manifest file, this is a list, although usually only one API group is specified here. The API group “” (empty string) represents the core Kubernetes API.
The verb indicates the action to take. For example: get, list, create, delete, update, etc. Again, this item in the manifest file is a list, which allows you to specify more than one action and thus avoid repeating very similar rules over and over again.
The third part is the verb’s target, which can be either a resource or a URL. In the former case, the type of resource must be specified. This is again a list (the “resources” field), which allows you to specify more than one resource type. Examples of resource types are: pod, networkpolicy, service, etc. Additionally, further restrictions can be applied by using resource names.
The second option for the verb’s target is to specify the URL path. This is the request’s URL path and can contain an ending wildcard, which can be used to give access to certain parts of the API. An RBAC rule must specify a target that is either a resource or a URL, but not both. Also, please note that a URL path can be specified as a target only for cluster roles.
Examples of RBAC Rules
RBAC with a Resource Target
Example YAML manifest file:
rules: - apiGroups: [“”]
verbs: [get, list]
The role in this file has only one RBAC rule, which targets the core API (the empty string in “apiGroups”). The rule allows for the actions “get” and “list” on a secret named “mysupersecret.”
RBAC Manifest File with a URL Path
Example of YAML manifest file:
rules: - apiGroups: [“rbac.authorization.k8s.io”]
verbs: [get, list]
In this case, the rule gives “get” and “list” access to any request made on the “/apis/rbac.authorization.k8s.io/v1alpha1” path. Please note that the role here is a ClusterRole, as only cluster roles can use path-based permissions.
Accessing Cloud Services
If you want your pods to access some cloud services, RBAC is not enough because it only manages Kubernetes resources. In AWS, for example, you will need to link a Kubernetes service account with an IAM role. How this is achieved is somewhat complex but is well-documented by AWS. In short, you will need to add annotations to your service account manifests that point to the IAM role you want the service account to assume with respect to accessing AWS resources. For example:
- eks.amazonaws.com/role-arn: <ROLE_ARN>
However, you will need to first create an OIDC provider (one per EKS cluster).
Other cloud vendors require similar gymnastics in order to access their resources.
RBAC Best Practices
RBAC best practices are part of a greater K8s security best practices.
The first rule of RBAC is the same as for any permission system: Apply the principle of least privilege to service accounts.
In theory, you should also apply the principle of least privilege to humans (i.e., users and groups), but in practice, it is often too costly. A typical scenario is when a given user can’t perform a certain operation required for their work, so the user informs the admin. Now the user can’t do anything until the admin grants that specific permission, after which, they can then go a bit further in their work; however, soon after, the user requires yet another permission, etc. This can very quickly become a major strain and time sink for both the user and the admin.
Secondly, you should ensure that you create an RBAC strategy, starting from defining where you are at the moment (which can be a blank slate if this is a greenfield project), followed by establishing your goals and creating a plan to go from where you are to where you want to be.
Thirdly, you should use some form of script, infrastructure-as-code or templating such as Helm charts, to take advantage of templating and variables and to keep a trail of what changes you made to your system. Having a history of such changes is very important to help you understand certain problems that can arise (for example, when something was working before but not working after a certain change) and also for auditing purposes if your project has to comply with certain regulations.
Fourthly, always test your permissions: Ensure that the pod can do what you want but also that it is not allowed to do what you don’t want it to do. Testing such permissions is quite time-consuming, but it is extremely important to do, otherwise you might be left with vulnerabilities in your system.
Finally, you should have a system in place to periodically review existing RBAC and service account objects to ensure that they are up to date, as well as delete those that are not needed anymore. Again, this is to decrease your attack surface. It should be noted that this requirement might be fulfilled when you use some kind of infrastructure-as-code for all or part of your system.
Once again, it is important to understand that RBAC is necessary only if your pods want to call the Kubernetes API directly. This is not the case for most applications today. A typical pod that would require this type of permission is a controller or an operator, and it is quite rare that you would be working on such things.
RBAC is actually not complicated, but it is important to set it up according to the principle of least privilege, at least for service accounts. If you want your pods to access cloud resources, you will need to follow the instructions provided by your cloud vendor, which will usually tie a service account to an IAM role.
ARMO provides an assessment tool for RBAC configuration during the posture scan and provides a visualization on current RBAC configuration status and enable advanced query functions during runtime.