Start your free 14-day ContainIQ trial

Kubernetes RBAC: Implementation and Best Practices

Role-based access control lets you use organizational user roles to restrict access to resources. In this guide, you’ll learn how to apply this in Kubernetes and the best practices for doing so.

June 27, 2022
James Walker
Software Engineer

Role-based access control (RBAC) is a security methodology that restricts user interactions to specifically allocated features. User accounts are granted roles; those roles permit access to distinct capabilities of the software.

In the context of Kubernetes, RBAC lets you limit what individual users can change inside your cluster. You can restrict sensitive operations to the people that really need them, reducing your attack surface in case an account is compromised.

In this article, you’ll learn how to set up a simple RBAC implementation inside your Kubernetes cluster. You’ll also cover some best practices for managing RBAC policies, so you can enforce strong security and protect your applications.

Why Does RBAC Matter?

RBAC mechanisms offer granular control of user permissions in software applications. You can precisely allocate features to the individuals that need them, avoiding over-privileged accounts.

Giving users too many capabilities poses a risk if credentials are lost or stolen. As an example, developers probably shouldn’t be able to delete your production deployments. Even if you trust your employees implicitly, a phishing or social engineering attack could give people outside of the company control of the overpowered account. With RBAC in Kubernetes, you can create a policy that prevents users from deleting pods.

RBAC only works when the application is designed to support it. Each logical capability should be backed by its own role. You can then assign users the roles that make sense for their responsibilities, mixing and matching to obtain the desired set of privileges.

Using RBAC With Kubernetes

Kubernetes has extensive support for RBAC. It permeates the system’s architecture and supports role delineation by resource and verb. For example, each of the following actions can be expressed as distinct RBAC rules:

  • Listing pods
  • Creating a pod
  • Viewing the data inside secrets
  • Deleting a deployment
  • Adding more users

Rules are combined into roles that cover multiple actions. The rules shown above could be used to create a developer role that’s able to list and create pods, and a separate administrator role that can list and create pods, but is also able to initiate deletions and manage users. You have fine-grained control over the capabilities available to each user.

Four fundamental objects scaffold the Kubernetes RBAC implementation:

  • Role: A collection of one or more rules defining the actions that users can take.
  • RoleBinding: An association between a role and one or more users or service accounts, called subjects. Each subject targeted by the binding can perform the actions covered by the role.
  • ClusterRole and ClusterRoleBinding: These are variants of Role and RoleBinding that exist at the cluster level. They’re used to create rules that target cluster-level resources such as nodes.

Now let’s see how to use these objects to set up RBAC in your own Kubernetes cluster.

K8s Metrics, Logging, and Tracing
Monitor the health of your cluster and troubleshoot issues faster with pre-built dashboards that just work.
Start Free Trial Book a Demo

Implementing RBAC In Your Kubernetes Cluster

Most new clusters will start you with a fully privileged user account that can perform any Kubernetes action. RBAC is an optional feature that can be turned off altogether. Run the following command to see if it’s enabled:


$ kubectl api-versions | grep rbac.authorization.k8s
rbac.authorization.k8s.io/v1

The command above has produced a line of output which shows <terminal inline>RBAC<terminal inline> is available. If you don’t see any output, that indicates that RBAC is turned off in your cluster. You can enable the feature by starting the Kubernetes API server with the RBAC authorization mode included:


$ kube-apiserver --authorization-mode=RBAC

You should refer to the documentation included with your Kubernetes distribution to get detailed guidance on starting the API server.

Manual RBAC enablement shouldn’t be necessary if you’re using a Kubernetes cluster deployed from a managed cloud service. RBAC is usually on by default for clusters provisioned by major providers.

Creating a Test User

Next, you’ll create a test user account that you’ll assign your RBAC roles to. Kubernetes recognizes two types of user: a normal user and a service account. Normal users can’t be added using regular API calls, so we’ll create a service account for this tutorial. The Kubernetes documentation includes a description of the differences between the user types and explanations of when each one is appropriate.

Run the following command to add a new service account called <terminal inline>demo<terminal inline>:


$ kubectl create serviceaccount demo
serviceaccount/demo created

Find the name of the secret that stores the service account’s token:


$ kubectl describe serviceaccount demo
Name:               demo
Namespace:          default
Labels:             <none>
Annotations:        <none>
Image pull secrets:  <none>
Mountable secrets:   demo-token-znwmb
Tokens:             demo-token-znwmb
Events:             <none>

The secret’s name is <terminal inline>demo-token-znwmb<terminal inline>. Retrieve the token’s value from the secret using the following command:


$ TOKEN=$(kubectl describe secret demo-token-znwmb | grep token: | awk '{print $2}')
$ echo $TOKEN
eyJbGciOi...

With the token extracted, you can add your user as a new kubectl context:


$ kubectl config set-credentials demo --token=$TOKEN
User "demo" set.

$ kubectl config set-context demo --cluster=kubernetes --user=demo
Context "demo" created.

If your current cluster connection is not called <terminal inline>kubernetes<terminal inline>, you should change the value of the <terminal inline>--clusterflag<terminal inline>.

Now you can switch to your <terminal inline>demo<terminal inline> context to run kubectl commands as your new service account user. Run the <terminal inline>current-context<terminal inline> command first to check the name of your currently selected context—you’ll be switching back to it in a moment.


$ kubectl config current-context
default

$ kubectl config use-context demo
Switched to context "demo".

Try to retrieve the pods in your cluster:


$ 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"

Kubernetes has issued a <terminal inline>Forbidden<terminal inline> error. The <terminal inline>demo<terminal inline> service account has been recognized, but because no roles have been created or assigned, it lacks permission to run the <terminal inline>get pods<terminal inline> command.

Creating a Role

Switch back to your original context before continuing, so you regain your administrative privileges:


$ kubectl config use-context default
Switched to context "default".

Kubernetes role objects are created in the standard way. You write a YAML file that defines the role’s rules, then use kubectl to add it to your cluster.

To keep things simple, you’ll use the “Developer” role example from earlier:


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

Users who are assigned this role will be able to run the <terminal inline>create pod<terminal inline>, <terminal inline>get pod<terminal inline>, <terminal inline>get pods<terminal inline>, and <terminal inline>describe pod<terminal inline> commands. They won’t be able to update or delete pods, or perform actions that target other resource types.

Use kubectl to add the role to your cluster:


$ kubectl apply -f role.yaml
role.rbac.authorization.k8s.io/Developer created

Creating a RoleBinding

A role on its own has no effect on your cluster’s access control. It needs to be bound to users with a RoleBinding. Create this object now to grant your <terminal inline>demo<terminal inline> service account the permissions associated with your <terminal inline>Developer<terminal inline> role:


apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: default
  name: DeveloperRoleBinding
subjects:
  - kind: ServiceAccount
    name: demo
    apiGroup: ""
roleRef:
  kind: Role
  name: Developer
  apiGroup: ""

The <terminal inline>subjects<terminal inline> field lists the objects that will be granted the permissions included in the role. In this example, you’re adding a single <terminal inline>ServiceAccount<terminal inline> subject to represent your <terminal inline>demo<terminal inline> user. You can target a <terminal inline>User<terminal inline> or <terminal inline>Group<terminal inline> instead by adjusting the subject’s <terminal inline>Kind<terminal inline> accordingly.

The <terminal inline>roleRef<terminal inline> field identifies the role that will be bound to the subjects. The <terminal inline>kind<terminal inline> can be either <terminal inline>Role<terminal inline> or <terminal inline>ClusterRole<terminal inline>, depending on the type of role you’ve created. In this example, the <terminal inline>Developer<terminal inline> role is a regular role.

Add the RoleBinding to your cluster using kubectl:


$ kubectl apply -f role-binding.yaml
rolebinding.rbac.authorization.k8s.io/DeveloperRoleBinding created

Testing Your RBAC Policy

The RoleBinding should have granted your <terminal inline>demo<terminal inline> service account the permissions included in the <terminal inline>Developer<terminal inline> role. Verify this by switching back to your <terminal inline>demo<terminal inline> context:


$ kubectl config use-context demo
Switched to context "demo".

Repeat the <terminal inline>get pods<terminal inline> command:


$ kubectl get pods
No resources found in default namespace.

This time the command succeeds. The <terminal inline>get pods<terminal inline> operation is permitted by the <terminal inline>Developer<terminal inline> role, which is now assigned to the <terminal inline>demo<terminal inline> service account you’re authenticated as.

Create a new pod:


$ kubectl run nginx --image=nginx
pod/nginx created

This operation is permitted, too. Now, try to delete the superfluous pod:


$ kubectl delete pod nginx
Error from server (Forbidden): pods "nginx" is forbidden: User "system:serviceaccount:default:demo" cannot delete resource "pods" in API group "" in the namespace "default"

Kubernetes rejects the operation because the <terminal inline>demo<terminal inline> service account doesn’t have a role that provides a suitable permission. This demonstrates how you can use Role and RoleBinding objects to restrict how users interact with your cluster.

Kubernetes RBAC Best Practices

As we’ve seen above, a basic Kubernetes RBAC implementation is simple to set up. However, it’s important to adhere to some best practices for maximum effectiveness and security.

  • Avoid over-privileged roles: Role creation is a balancing act. On the one hand, a large number of roles with a handful of permissions each are hard to manage and assign. However, roles that include permissions because of a vague “might be needed one day” mindset can be a security weakness. Target your roles to specific use cases, and don’t be afraid to add a new one if it seems to be the best option.
  • Regularly review your RBAC roles: RBAC policies need to be regularly audited, so you remain aware of who can do what inside your cluster. RBAC strengthens your security, but forgotten or unnecessary role assignments can mask lurking threats.
  • Be careful with wildcards: Roles let you use wildcards like <terminal inline>*<terminal inline> in their <terminal inline>resources<terminal inline> and <terminal inline>verbs<terminal inline> fields. This can be a tempting shortcut, but it makes it harder to effectively audit and maintain your policies. It also opens the possibility that users could receive automatic access to new resources added in future Kubernetes releases.
  • Don’t forget users and service accounts: Authentication is important, too. There’s little point in using RBAC if the same account is reused across multiple services and individuals. Set up a dedicated service account for each integration, then assign it the individual roles it needs.
  • Remove unused roles: Unused roles and RoleBindings can clutter your cluster and cause confusion. Only create roles that are needed in your workflows and processes. Too many redundant roles will make it difficult to understand how capabilities are being distributed throughout your cluster.

Keeping these best practices in mind will help you set up a secure Kubernetes environment that makes good use of granular RBAC policies. Give each user the minimum level of privileges they require, then review each role regularly to ensure the level of access remains appropriate.

Final Thoughts

Role-based access control (RBAC) is a feature-driven method of permissions enforcement. It decouples actions from the users capable of performing them, letting you create unique policies that match each individual’s responsibilities.

Within Kubernetes, you can create precise RBAC rules for each verb and resource combination in your cluster. A developer role might permit only “create pods,” list pods," and “view logs,” reducing the risks associated with an account compromise. Once you’ve created your roles, you can associate them with users via RoleBinding objects.

While RBAC is a powerful security tool, it’s only one part of safe and secure Kubernetes management. You also need effective monitoring procedures so you can track activity in your cluster and spot potential issues. ContainIQ provides an easy way to visualize your cluster and the traffic flowing through it, keeping you informed of any emerging problems.

Start your free 14-day ContainIQ trial
Start Free TrialBook a Demo
No card required
James Walker
Software Engineer

James Walker is the founder of Heron Web, a UK-based digital agency providing bespoke software development services to SMEs. He has experience managing complete end-to-end web development workflows with DevOps, CI/CD, Docker, and Kubernetes. James also writes technical articles on programming and the software development lifecycle, using the insights acquired from his industry career. He's currently a regular contributor to CloudSavvy IT and has previously written for DigitalJournal.com, OnMSFT.com, and other technology-oriented publications.

READ MORE