Skip to main content

Control your role! Kubernetes RBAC explored

Written by:
James Walker

James Walker

blog-feature-snyk-container-custom-base-image-recommendations

July 27, 2023

0 mins read

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 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, 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 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 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 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 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 that permits clients to authenticate using any certificate signed by the cluster's certificate authority. This is usually the default strategy 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 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 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 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 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 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, 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 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 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 to maintain a fully hardened environment. Get started with Snyk for Kubernetes to scan your cluster for vulnerabilities, find misconfigurations, and get automated fix suggestions in your CLI, IDE, and Git repositories.

blog-feature-snyk-container-custom-base-image-recommendations

8 Expert Tips to Secure Your Pipelines

Find security issues in the pipeline before you push to production with these 8 actionable scanning and integration tips.