Control your role! Kubernetes RBAC explored
Tags: containersecurity, devsecops, kubernetes
Published: 2023-07-28 02:00:38

Role-based access control (RBAC) is an approach for controlling which actions and resources in a system are available to different users. Users are assigned roles that grant them permission to use particular system features.


Kubernetes has a robust built-in [RBAC implementation](https://kubernetes.io/docs/reference/access-authn-authz/rbac) for authorizing user interactions with your cluster. Setting up RBAC allows you to define the specific actions that users can perform on each Kubernetes object type. This provides an important first line of defense against the risks of [overprivileged user accounts](https://snyk.io/product/container-vulnerability-management), where anyone can use any function.


In this article, you'll learn what Kubernetes RBAC is, why you should use it, and how to configure it in your cluster.


What Is Kubernetes RBAC
-----------------------


The Kubernetes RBAC API provides a complete RBAC implementation that allows you to define custom roles and assign them to your users. Each role grants the user permission to perform one or more actions against a specific type of resource.


A permission is a combination of a resource and a verb, such as `get pods` or `list services`. These are the granular actions available to users. Roles collect permissions into logical groups that mirror the access levels your users require. For example, a `Developer` role could permit users to create and list pods, while a separate `Administrator` role facilitates privileged actions, such as deleting pods.


Permissions are always additive; users are granted the set of permissions accrued from all the roles they've been assigned. This means that a user with both the Developer and `Administrator` roles would be able to create, list, and delete pods.


### RBAC vs. ABAC


RBAC is the modern approach to Kubernetes authorization, but [attribute-based access control (ABAC) is available](https://kubernetes.io/docs/reference/access-authn-authz/abac) as an alternative solution. ABAC was the preferred option in Kubernetes releases prior to v1.6. However, it's now advisable to use RBAC instead of ABAC. Even though ABAC is still supported, it's [best to consider ABAC to be deprecated](https://kubernetes.io/blog/2017/04/rbac-support-in-kubernetes) and possibly dropped in the future.


ABAC mode requires you to configure authorization rules as policies in a JSON file, which is supplied to the Kubernetes API server. It looks at the attributes of each resource that's accessed, such as its namespace and type, to determine whether a user is authorized:




| **RBAC** | **ABAC** |
| --- | --- |
| Roles contain Permissions that allow actions to be performed against resources. | Authorization policies are based on attributes of the target resource. |
| Users are assigned roles by Role Bindings. | No need to assign policies directly to users; each policy identifies its subject. |
| Roles and Role Bindings are configured as API objects which can be added dynamically. | Policies can only be configured in JSON files supplied to the API server at startup. |


However, the Kubernetes implementation of ABAC leaves room for improvement. In its current form, Kubernetes ABAC authorization outcomes can’t be based on any attribute of a target resource, because you can only match based on API group, namespace, and resource type. Similarly, you're restricted to the user's name and groups for user-level attribute matching.


Consequently, RBAC is simpler to configure, test, and maintain, without losing any of the functionality of ABAC. Kubernetes doesn't have a built-in way to perform true ABAC-based authorization, which would permit different outcomes based on arbitrary resource attributes, such as labels, creation time, and images.


Exploring RBAC on Kubernetes
----------------------------


The Kubernetes RBAC implementation revolves around four main object types:


1. **Roles:** Role objects group a collection of permissions that can be assigned to your users. Roles are namespaced objects, so they only permit access to resources within the namespace they're a part of.
2. **RoleBindings:** RoleBindings assign roles to users. Each user named in the RoleBinding is assigned the permissions included in the bound role.
3. **ClusterRoles:** ClusterRoles are similar to Roles, but ClusterRoles are non-namespaced objects that exist at the cluster level. You can use them to manage access to global cluster resources, such as nodes, as well as cross-namespace access to namespaced objects, such as pods.
4. **ClusterRoleBindings:** ClusterRoleBindings are the cluster-level version of RoleBindings used to link ClusterRoles to your users. They can only refer to ClusterRoles, not namespaced Roles.


The distinction between Roles and ClusterRoles is enforced because Kubernetes requires that all objects are either namespaced or non-namespaced.


This means you should use a Role when you want to control access to resources within a specific namespace. Or you can create a ClusterRole instead if you plan to reuse a Role across multiple namespaces or you're trying to limit access to cluster-level objects, such as nodes that aren't part of any namespace.


### RBAC and Kubernetes user accounts


Implementing Kubernetes RBAC requires you to first create your Roles and ClusterRoles, then bind them to your user accounts so that users and applications can authenticate and receive the correct permissions. There are [two strategies](https://kubernetes.io/docs/reference/access-authn-authz/authentication) for user authentication: normal users and service accounts.


#### Normal users


A normal user represents a human who interacts with the Kubernetes API server. There's no built-in object to represent users or manage their credentials; instead, it's expected that administrators [define their own](https://kubernetes.io/docs/reference/access-authn-authz/authentication) authentication system, using either private keys, bearer tokens defined in a file, or an external OAuth integration with an identity service, such as those offered by Google and Microsoft.


It's not possible to add new normal users through the Kubernetes API. However, Kubernetes supports an [X509 client certificate authentication strategy](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) that permits clients to authenticate using any certificate signed by the cluster's certificate authority. This is usually the default [strategy](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authentication-strategies) that kubectl uses to connect to your cluster. The username for RBAC purposes is identified by the certificate's common name (`CN`) field.


#### Service accounts


Service accounts are [designed to be used](https://kubernetes.io/docs/concepts/security/service-accounts) by nonhuman applications. Unlike normal users, they're managed by the Kubernetes API, so you can create them with kubectl commands. Each service account has a unique token that authenticates your application to the API server. Unlike normal users, service accounts can be scoped to specific cluster namespaces.


Service accounts are simpler to get started with when you're exploring Kubernetes RBAC capabilities. You don't have to manage certificates or connect an external identity provider.


However, service accounts [are intended to be](https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#user-accounts-versus-service-accounts) used primarily by pods inside your cluster that need to interact with Kubernetes APIs. They're not recommended for long-term human use. You should connect your existing identity provider to manage human user accounts, which allows you to properly control authentication and auditing procedures.


Using RBAC in Kubernetes
------------------------


Now that you know how RBAC works, get started using it in Kubernetes with a simple demo. You need [kubectl installed](https://kubernetes.io/docs/tasks/tools/#kubectl) with a connection to an existing cluster.


In this tutorial, you'll set up a service account user for simple experimentation, but remember that an [OAuth integration](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens) is preferred in real-world scenarios.


### 1. Check that RBAC is enabled


RBAC is an optional Kubernetes feature. Although most distributions now ship with it enabled, it's worth checking before you go any further. Run the following `kubectl` command:



```
$ kubectl api-versions | grep rbac.authorization.k8s
```


```
rbac.authorization.k8s.io/v1
```

If you see `rbac.authorization.k8s.io/v1` displayed in your terminal, then RBAC is active. No output means the API is disabled — you need to adjust your distribution's configuration to start [the API server](https://kubernetes.io/docs/concepts/overview/components/#kube-apiserver) with the `--authorization-mode=RBAC` flag set.


### 2. Create a service account


Create a service account for the purposes of this tutorial. You assign roles to the service account so you can see how RBAC works. Copy the following YAML and save it to `service-account.yaml`:



```
apiVersion: v1
kind: ServiceAccount
metadata:
  name: demo

---

apiVersion: v1
kind: Secret
type:
  kubernetes.io/service-account-token
metadata:
  name: demo-sa-token
  annotations:
    kubernetes.io/service-account.name: demo
```

Then use `kubectl` to apply the YAML manifest to your cluster:



```
$ kubectl apply -f service-account.yaml
```


```
serviceaccount/demo created
secret/demo-sa-token created
```

The service account is created, along with a secret that contains its authentication token. Retrieve the value of this token:



```
$ SA_TOKEN=$(kubectl describe secret demo-sa-token | grep token: | awk '{print $2}')
```

The token's value is saved to your shell's `$SA_TOKEN` variable, ready to be referenced in the next step.


### 3. Create a new kubectl context


Next, you need to add a [`kubectl` context](https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters), which is used to authenticate your cluster as your service account.


Start by checking the name of your current context so you can switch back to it later:



```
$ kubectl config current-context
```


```
default
```

Now, run the following command to register your service account credentials in your kubectl config file:



```
$ kubectl config set-credentials demo-sa --token=$SA_TOKEN
```


```
User "demo-sa" set.
```

Then add a new context that authenticates to your existing Kubernetes cluster but with your new `demo-sa` service account credentials:



```
$ kubectl config set-context demo --cluster=default --user=demo-sa
```


```
Context "demo" created.
```

If your cluster's not called `demo` in your `KUBECONFIG` file, you need to change the value passed to the `--cluster` flag earlier. You can retrieve the list of clusters available in your file by running `kubectl config get-clusters`. (For example, if you are using a [kind](https://kind.sigs.k8s.io) cluster with its default naming, the cluster name would be `kind-kind`).


Finally, switch into your new context and try to run a simple `kubectl` command, such as listing your cluster's pods:



```
$ kubectl config use-context demo
```


```
Switched to context "demo".
```


```
$ kubectl get pods
```


```
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:demo" cannot list resource "pods" in API group "" in the namespace "default"
```

As you can see, access has been forbidden by the API server — the service account doesn't have any RBAC roles assigned yet, so it has no permission to perform any actions. Now, assign a role, but switch back to your regular `kubectl` context first to regain your cluster permissions:



```
$ kubectl config use-context default
```


```
Switched to context "default".
```

### 4. Create a role


Roles (and ClusterRoles) are Kubernetes objects that you can define using YAML manifests. Copy the following YAML and save it to `role.yaml` in your working directory:



```
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: sample-role
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "create"]

```

Roles specify the combination of resources, such as pods, services, and deployments, and verbs like `list`, `create`, and `delete` that the holder's permitted to perform. This sample Role permits the user to create pods and retrieve their details, but any other kind of interaction is forbidden.


Use `kubectl` to apply the role:



```
$ kubectl apply -f role.yaml
```


```
role.rbac.authorization.k8s.io/sample-role created
```

### 5. Bind your role


Now, the Role exists in your cluster, but you still need to bind it to your service account. This grants the user the permissions defined within the Role.


Copy the following YAML and save it to `role-binding.yaml`:



```
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: sample-role-binding
subjects:
  - kind: ServiceAccount
    name: demo
    apiGroup: ""
roleRef:
  kind: Role
  name: sample-role
  apiGroup: ""

```

The manifest binds the `sample-role` role to the service account called `demo`. The binding's subject identifies the users who are assigned the role named in the `roleRef` field. To bind to a normal user instead of a service account, set the subject's `kind` field to `User`.


For this example, apply your RoleBinding now to assign your role to your service account:



```
$ kubectl apply -f role-binding.yaml
```


```
rolebinding.rbac.authorization.k8s.io/sample-role-binding created
```

### 6. Test Your RBAC permissions


You can check whether a user or service account can perform a particular action using kubectl's `auth can-i` command. This should now return `yes` for your `demo` service account when you query actions covered by your role:



```
# permitted by the role
$ kubectl auth can-i get pods --as=system:serviceaccount:default:demo

```


```
yes
```


```
# not included in the role
$ kubectl auth can-i delete pods --as=system:serviceaccount:default:demo

```


```
no
```

Switch back to your new `kubectl` context that authenticates as your service account:



```
$ kubectl config use-context demo
```


```
Switched to context "demo".
```

Now you should be able to successfully retrieve your cluster's pods while authenticated as the service account:



```
$ kubectl get pods
```


```
No resources found in default namespace.
```

The Role has been successfully bound to the service account. You can perform the actions it permits, but any others, such as listing services, are still forbidden:



```
$ kubectl get services
```


```
Error from server (Forbidden): services is forbidden: User "system:serviceaccount:default:demo" cannot list resource "services" in API group "" in the namespace "default"
```

Role-based access conclusion
----------------------------



[Kubernetes RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac) is the standard mechanism for controlling access to your cluster. In this article, you've learned what RBAC is, what object types implement it in Kubernetes, and how to get started setting up your own Roles and RoleBindings in your cluster.


RBAC is an important layer of protection against stolen credentials and overprivileged user accounts. However, you must pair it with other Kubernetes [security best practices](https://snyk.io/blog/10-kubernetes-security-context-settings-you-should-understand) to maintain a fully hardened environment. [Get started with Snyk for Kubernetes](https://snyk.io/lp/kubernetes) to scan your cluster for vulnerabilities, find misconfigurations, and get automated fix suggestions in your CLI, IDE, and Git repositories.