Applying the principle of least privilege to Kubernetes using RBAC
Jekayin-Oluwa Olabemiwo
29 de agosto de 2022
0 minutos de leituraWhat 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
calledsecret-reader
and a namespace,development
, to which the RoleBinding applies.Specified
sola
as aUser
in thesubjects
section. You can also specify multiple subjects.Attached the respective
Role
to the binding in theroleRef
section. Bear in mind that after creating a binding, the Role or ClusterRole attached to the binding in theroleRef
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:
Developer-first container security
Snyk finds and automatically fixes vulnerabilities in container images and Kubernetes workloads.