Understanding the Kubernetes RBAC API

Understanding the Kubernetes RBAC API

Posted in

If you’re starting your Kubernetes journey with a small cluster of a few team members and just a few nodes, you might not have a structured approach to access control. It is common for administrators to give everyone in their team admin rights to the entire cluster.

However, this approach is no longer acceptable as you scale up your deployment. Not all users should have unrestricted permissions to create, modify, and delete resources. As the number of cluster nodes, applications, and team members grows, you need to limit the resources that team members and applications can access and what they can do.

You can do this using Kubernetes’ role-based access control (RBAC) framework. For example, you can allow developers to deploy only specific applications to specific namespaces or give your infrastructure management team view-only permissions to monitor their work.

Why Is RBAC Important?

The RBAC system allows companies to protect their data and critical business processes through rules and roles established by their organization. RBAC also allows administrators to gain deeper insights into how cloud tools and IT systems are accessed. This is especially important for DevOps teams, as many teams need different levels of control over specific workloads or groups of resources.

RBAC is used to define roles and actions for cluster resources and other resources in the continuous integration and continuous delivery (CI/CD) process. It ensures the separation of duties and protects CI/CD pipelines deployed in production to ensure only authorized personnel can access them. Developers generally do not allow access outside of development and non-production environments.

It is essential to consider how RBAC and governance can help developers and DevOps engineers focus on their core responsibilities. With proper access control, teams can collaborate and drive processes more efficiently.

Understanding the Kubernetes API

The Kubernetes API is a RESTful programming interface provided over HTTP. It supports basic resource discovery, creation, update, and deletion via common HTTP verbs: POST, PUT, PATCH, DELETE, and GET.

For some resources, the API allows for fine-grained authorization by breaking a resource into sub-resources. In addition, it can display information about resources in different representations for convenience.

Kubernetes supports notification of changes to resources — this is done through the concept of “watches.” Kubernetes also provides a list command for every resource, allowing API clients to efficiently cache, track, and synchronize resource states.

Here are key concepts in the Kubernetes API:

  • The resource type is the name used in the URL (pod, namespace, or service).
  • Each resource type has a concrete representation (object schema) called a type.
  • A list of resource instances is called a collection.
  • A single instance of a resource type is called a resource and usually also exists as an object in the Kubernetes cluster.
  • For some resource types, the API includes one or more sub-resources represented as URI paths under the parent resource.

The Kubernetes Authorization API

Kubernetes requires authentication (login) before accepting a request. It manages authentication using attributes common to REST APIs. This means you can use Kubernetes authorization with existing access control systems, either managed by your organization or cloud providers, which can handle the Kubernetes API together with other APIs.

Kubernetes uses an API server to authorize API requests. The API server evaluates all request attributes against all authorization policies and either allows or denies each request. Permission is denied by default —any part of the API request must be explicitly allowed by a policy to proceed.

If there is more than one authorization module configured, each module is checked in turn. If a module approves or rejects the request, the decision is returned to the client immediately, and no other authorizers are consulted. If all modules do not match the request, it is rejected. In any case of denial, the API server returns an HTTP status code 403.

Kubernetes checks these API request attributes:

  • user: The user string provided at authentication.
  • group: The list of names to which the user belongs.
  • extra: An arbitrary key map with string values taken from the authentication layer.
  • API: Indicates if the request targets an API resource.
  • Request path: A path to an endpoint other than a resource such as /api or /healthz.
  • API request verbs: Resource requests can use the following API verbs: create, list, get, update, watch, patch, delete, and delete collection.

These attributes only apply to resource requests:

  • resource: The name or ID of the resource being accessed. For resource requests that use get, update, delete, or patch verbs, the recourse name must be specified.
  • subresource: The subresource being accessed.
  • API group: The API group to access. An empty string indicates a core API group.
  • namespace (for namespace resource requests only): The namespace of the object being accessed.

This attribute only applies to non-resource requests:

  • HTTP request verbs (for non-resource requests): Lowercase HTTP methods such as get, put, post, and delete can be used for a non-resource request.

Using Kubernetes RBAC Authorization

Role-based access control is a process of providing and controlling access to network or computer resources to an organization’s users based on their roles. RBAC uses the rbac.authorization.k8s.io API group to make authorization decisions. Hence, you can configure policies dynamically through the Kubernetes API.

Enabling RBAC Authorization

Use the following command to start the API server:

kube-apiserver --authorization-mode=Demo,RBAC --other-options --more-options

Here are the four objects that the API describes:

  • Role
  • ClusterRole
  • RoleBinding
  • ClusterRoleBinding

You can describe or amend objects just like any other Kubernetes objects through tools like kubectl.

Defining Role and ClusterRole

A Role or ClusterRole consists of rules which represent a collection of permissions. While A Role sets permissions within a certain namespace, a ClusterRole is a non-namespaced resource. Hence, both Role and Cluster Role exist as different objects since a Kubernetes object can either be namespaced or not namespaced.

You may use a Role when you want to specify a role within a certain namespace and a ClusterRole when you want to define a role across a cluster.

Here is an example of a Role in the default namespace that grants read access to pods:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: demo-pod-reader-role
rules:
- apiGroups: [""] 
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

Here is an example of a ClusterRole that can give read access to secrets in a specific namespace or across all namespaces.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: demo-cluster-role
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

Setting RoleBinding and ClusterRoleBinding

A RoleBinding endows permissions defined for a certain role to a user or a collection of users. It stores a list of subjects, such as users, groups, or service accounts, and references the role whose permissions are being granted. While A RoleBinding provides permissions in a certain namespace, a ClusterRoleBinding provides access across a cluster.

A RoleBinding can include a reference to any Role in the same namespace or reference a specific ClusterRole and attach it to the RoleBinding namespace. Use a ClusterRoleBinding for binding a ClusterRole to all namespaces in the cluster.

Here is an example showing a RoleBinding that gives the demo-pod-reader-role Role to the demo-user within the default namespace. Hence, demo-user can read pods within the default namespace.

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: demo-role-binding
  namespace: default
subjects:
- kind: User
  name: demo-user
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role 
  name: demo-role
  apiGroup: rbac.authorization.k8s.io

Here is an example of a ClusterRoleBinding that allows a user in the demo-manager group to read the secrets in the namespace.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: demo-read-secrets
subjects:
- kind: Group
  name: demo-manager
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: demo-secret-reader
  apiGroup: rbac.authorization.k8s.io

Referring to Resources

RBAC references the resources using the same name in the URL for the relevant API’s endpoint. However, certain Kubernetes APIs involve a subresource, like logs for a pod. For example, here is what the request for a pod’s logs would look like:

GET /api/v1/namespaces/{namespace}/pods/{name}/log

Here, pods is the namespaced resource for pod resources and log is the pods’ subresource. Representing this within an RBAC role requires using a slash to delimit the resource and subresource. Here is how to give a user permission to read pods and access each pod’s log subresource:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-and-pod-logs-reader
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list"]

You can also refer to the resources by name using the resourceNames list for certain requests. It allows restricting requests to separate instances of a resource. For example, here is a configuration that restricts its subject to either only get or update a ConfigMap called my-demo-configmap:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: demo-configmap
rules:
- apiGroups: [""]
ConfigMap
  resources: ["configmaps"]
  resourceNames: ["my-demo-configmap"]
  verbs: ["update", "get"]

Conclusion

In this article, I explained the importance of RBAC for Kubernetes security and showed how to use the Kubernetes API to configure RBAC permissions. In particular, I showed how to:

  • Enable authorization when starting the API Server.
  • Define Role and ClusterRole (these are the basic RBAC roles).
  • Sett RoleBinding and ClusterRoleBinding (permissions defined for a certain role to a user or a collection of users).
  • Map roles to Kubernetes resources.

I hope this will be useful as you improve access control and security for your Kubernetes clusters.