Skip to main content

Applying the principle of least privilege to Kubernetes using RBAC

Written by:
Jekayin-Oluwa Olabemiwo

Jekayin-Oluwa Olabemiwo

wordpress-sync/feature-kubernetes-polp

August 29, 2022

0 mins read

What is the principle of least privilege?

The principle of least privilege (PoLP) is a defensive strategy in the software development world. Alternatively called the principle of minimal privilege or the principle of least authority, PoLP ensures that users can only access the systems, processes, networks, and files required to complete their assigned tasks.

When properly configured, unauthorized users can’t navigate to restricted application functions or switch roles. Additionally, they can’t access certain data like files and API secrets — except when necessary to complete an assigned function. This helps to safeguard against intentional and unintentional consequences of unauthorized actions.

In on-site facilities, access to certain areas like server rooms is only available to employees who are authorized to work in those facilities. Some critical locations in such facilities are only accessible by experienced or senior team members in related departments.

The same mindset applies to PoLP. For example, in employee management software, personnel records would likely only be accessed by HR officials. Similarly, certain actions — like deploying application versions to production — can be configured so that only senior engineers can execute them.

PoLP is also present in software as a service (SaaS) applications, where users have different roles within an application, depending on their specific tasks.

Protecting software systems requires a balance between ensuring that a system doesn't become too restrictive for the stakeholders and preventing it from being too vulnerable to threat actors. PoLP helps us achieve this balance by keeping the system safe while minimizing access-related friction.

Additionally, PoLP helps to maintain efficient, reliable operational performance. Systems become less susceptible to downtime from malware attacks or risky user activities, like pushing insecure code to production or changing admin passwords.

In this article, we’ll explore how Kubernetes (K8s) supports PoLP by implementing role-based access control.

Applying the principle of least privilege to Kubernetes access

Kubernetes handles container orchestration workloads. It provides a platform for organizations and developers to automate the deployment, management, and scaling of containerized applications. This means various development, operations, and IT team members need to access a Kubernetes environment when creating or maintaining deployed applications.

To ensure optimal security and resource protection, organizations need to operate on PoLP when using Kubernetes, regardless of the current production stage. Fortunately, Kubernetes has provisions for authentication and authorization to enable access control. Teams should explicitly define who has access to which operational resources and permissions when interacting with the Kubernetes API. Additionally, organizations should disable unauthenticated access, whether by human or service accounts.

How Kubernetes manages access with RBAC

Role-based access control (RBAC) is a method used in many systems to define resource permissions based on the roles of individuals in the environment. Kubernetes has an extensive native RBAC mechanism, enabling us to configure permissions that specify how a user or group of users can interact with any Kubernetes object in a cluster. Using this RBAC framework is the first step toward securing a cluster and its containerized applications.

Kubernetes objects are persistent entities that specify which containerized applications are running, their resources, and the policies guiding how those applications operate. Essentially, they define the desired state of the cluster. We can create, change, or delete objects using the Kubernetes API.

In Kubernetes, there are two types of accounts: those for users and those for services. User accounts are for live/human users, while service accounts are for the processes that access the Kubernetes API. The Kubernetes RBAC governs these accounts’ access to resources, including pods, secrets, controllers, and custom resource definitions (CRDs).

Permissions in Kubernetes are defined with Role or ClusterRole objects. Then, the defined permissions are assigned as rules with the RoleBinding and ClusterRoleBinding objects. The RBAC API declares the four following kinds of Kubernetes objects:

  • Role — manages the permissions that apply to the resources within an individual namespace

  • ClusterRole — manages the permissions that apply to a whole cluster

  • RoleBinding — assigns a Role to an account or group within a particular namespace

  • ClusterRoleBinding — assigns a ClusterRole to an account or group for all the namespaces in the cluster

In the next two sections, we’ll explore how RBAC policies align with PoLP in limiting access to resources in clusters.

Defining permissions

To establish permissions properly, you need to configure Kubernetes to reflect each team member’s role, bearing PoLP practices in mind. Using Roles and ClusterRoles is an extremely efficient way to do this. Roles apply to resources within one namespace, while ClusterRoles apply to resources in the entire cluster.

In this section, we’ll explore how to restrict access to certain parts of a system with Role and ClusterRole.

Assume that a company has a new senior DevOps engineer who must be able to view all environment variables and other secrets, while their team members cannot. Furthermore, as a member of the operations team, this engineer might also need to view pods created for the company’s application workload so that they can monitor the health of containers that hold the client-side applications.

To accomplish this, start by defining the Role and ClusterRole objects in your YAML file. The following is an example of how to define a Role to enable read access to secrets in the development namespace:

1apiVersion: rbac.authorization.k8s.io/v1
2kind: Role
3metadata:
4  namespace: development
5  name: secret-reader
6rules:
7- apiGroups: [""]
8  resources: ["secrets"]
9  verbs: ["get", "watch", "list"]

The code above specifies the API Version. RBAC uses the rbac.authorization.k8s.io API group to configure authorization policies through the Kubernetes API. It then specifies the definition type as Role, and mentions the namespace to which it will apply — development. This assumes that development is the namespace group for resources the development team uses. Next, the Role receives the name secretpod-reader.

The rules section contains the resources to which the rule applies and the actions that the Role can perform on these resources. This section also features some rule verbs. Kubernetes uses rule verbs like write, watch, read, and delete to manage permissions in broad classes.

The following shows how to define a ClusterRole to enable read access to the front-end pods in the cluster:

1apiVersion: rbac.authorization.k8s.io/v1
2kind: ClusterRole
3metadata:
4  name: pod-reader
5rules:
6- apiGroups: [""]
7  resources: ["pods"]
8  verbs: ["get", "watch", "list"

Notice that the above code does not specify the namespace in the metadata. This is because ClusterRoles are not namespaced. So, you only specified the name of the ClusterRole as pod-reader in the metadata section.

Next, you can bind the Role to the group of resources namespaced for development, and bind the ClusterRole to the cluster. Now, your team — and no one else — can view the necessary secrets in development resources. Applying PoLP in this way enables your team to access what they need to perform developmental tasks but prevents everyone else within your organization from accessing these pods and resources.

You have now created a Role to enable the DevOps engineer to access secrets, and a ClusterRole for the entire DevOps team to read the pods. In the next section, you will assign the permissions to the Role and ClusterRole.

Assigning permissions

Role binding is beneficial when, for example, a specific employee needs access to the secret keys used in an application’s configuration. These might be API key pairs or environment variables.

Additionally, we need to use cluster binding to grant permissions across a whole cluster. Cluster binding can allow any user in the specified cluster role to read the pods anywhere within the cluster, regardless of namespace.

If the DevOps engineer is a user called Sola, you can assign the Role created above to a user called sola in the development namespace. Therefore, sola will be able to read the secrets in that namespace.

Note that the name sola is case sensitive. The RoleBinding definition is as follows:

1apiVersion: rbac.authorization.k8s.io/v1
2kind: RoleBinding
3metadata:
4  name: secret-reader
5  namespace: development
6subjects:
7- kind: User
8  name: sola
9  apiGroup: rbac.authorization.k8s.io
10roleRef:
11  kind: Role
12  name: secret-reader 
13  apiGroup: rbac.authorization.k8s.io

In the above code, you:

  • Specified the RoleBinding type.

  • Specified a Role called secret-reader and a namespace, development, to which the RoleBinding applies.

  • Specified sola as a User in the subjects section. You can also specify multiple subjects.

  • Attached the respective Role to the binding in the roleRef section. Bear in mind that after creating a binding, the Role or ClusterRole attached to the binding in the roleRef section cannot be changed.

The above RoleBinding example serves use cases that require a certain development team member to have access to the secret keys used in application configuration — such as API key pairs and environment variables.

Going forward, you have to use ClusterBinding to grant permissions across a whole cluster. In the following example, you will see how ClusterBinding can be used to allow any user in the ops group to read the pods in any namespace within the cluster. Note that the name ops is case-sensitive:

1apiVersion: rbac.authorization.k8s.io/v1
2kind: ClusterRoleBinding
3metadata:
4  name: read-pods-global
5subjects:
6- kind: Group
7  name: ops
8  apiGroup: rbac.authorization.k8s.io
9roleRef:
10  kind: ClusterRole
11  name: pod-reader
12  apiGroup: rbac.authorization.k8s.io

Cluster binding is beneficial in situations like the above example, where we would need to grant all team members permission to access all of the pods in the cluster. With each specific permission properly set, the users in the group can view the pods and assess their condition, ensuring that they can complete their required reports and monitoring responsibilities.Simultaneously, the definitions of the ClusterRole ensure that the users cannot modify the pods or the applications contained within them.

Securing your K8 configurations

Role-based access can play a substantial role in aligning with the principle of least privilege. Fortunately, the Kubernetes native RBAC framework offers significant functionality in this space. The ability to create and bind roles is extremely powerful in ensuring that only the minimum number of users has the required access to any needed resources. This limits exposure to potential bad actors without impeding the job functions of any team member. For an example of Kubernetes RBAC usage, check out how we use it at Snyk.

To learn more about how to properly and securely implement PoLP in your Kubernetes environment, visit Snyk and check out other posts on the Snyk blog.

Kubernetes Resources:

wordpress-sync/feature-kubernetes-polp

How to Build a Security Champions Program

Snyk interviewed 20+ security leaders who have successfully and unsuccessfully built security champions programs. Check out this playbook to learn how to run an effective developer-focused security champions program.