During a recent audit of a mid-sized Indian FinTech's EKS environment, we found 14 different ServiceAccounts with cluster-admin privileges. Most of these were legacy leftovers from a "move fast" phase where developers granted broad permissions to Jenkins runners and monitoring agents to bypass 403 Forbidden errors. This pattern is common in Indian EdTech and FinTech sectors, where the pressure to deploy often overrides the implementation of Hardening K8s Deployments and the Principle of Least Privilege (PoLP).
In these environments, "Flat Clusters"—where staging and production workloads share the same control plane—are frequently used to optimize costs. Without granular Kubernetes RBAC configuration, a single compromised pod in a staging namespace can facilitate lateral movement to production databases, violating the logical isolation requirements mandated by the RBI's Master Direction on IT Outsourcing and the DPDP Act 2023.
What is Role-Based Access Control (RBAC) in Kubernetes?
RBAC is the primary mechanism for regulating access to the Kubernetes API. It allows us to define who (Subject) can do what (Verb) to which resources (API Objects). The API server evaluates every request against these rules before execution. If no rule explicitly allows the action, the API server denies it by default. For a deeper dive into the framework, refer to the official Kubernetes Security Docs.
We categorize RBAC into four primary objects: Roles, ClusterRoles, RoleBindings, and ClusterRoleBindings. Understanding the distinction between these is the difference between a secure cluster and a wide-open vulnerability.
The Importance of RBAC for Cluster Security and Compliance
Effective RBAC is not just about security; it is a compliance requirement. Under the DPDP Act 2023, organizations must implement "reasonable security safeguards" to prevent personal data breaches. In a Kubernetes context, this means ensuring that a developer or an automated service cannot accidentally or maliciously access a namespace containing PII (Personally Identifiable Information).
Failure to scope RBAC leads to critical vulnerabilities like CVE-2020-8554, which is documented in the NIST NVD. This man-in-the-middle vulnerability allows users with create services permissions to hijack traffic to external IP addresses. If your RBAC is too broad, any user who can create a service can effectively redirect traffic intended for an external payment gateway to a malicious endpoint.
Roles vs. ClusterRoles: Scoping Your Permissions
A Role is always bound to a specific namespace. Use this for application-specific permissions. If I want a developer to manage pods only in the payments-api namespace, I define a Role within that namespace.
A ClusterRole is non-namespaced. It is used for cluster-wide resources like nodes, persistentvolumes, or namespaces. It is also used to define permissions that can be applied across multiple namespaces using RoleBindings. We observed that many teams mistakenly use ClusterRoles for everything, which inadvertently grants global visibility to accounts that only need local access.
RoleBindings vs. ClusterRoleBindings: Connecting Identities
A RoleBinding grants permissions defined in a Role or ClusterRole to a user or a set of users within a specific namespace. If you bind a ClusterRole using a RoleBinding, the permissions are only effective within the namespace of that RoleBinding.
A ClusterRoleBinding grants permissions at the cluster level. This is where most security failures occur. Granting a ClusterRoleBinding to the system:authenticated group essentially gives every user with a valid certificate the ability to perform the actions defined in that role across the entire cluster.
Identifying Subjects: Users, Groups, and ServiceAccounts
Kubernetes does not have a "User" object in its database. Instead, it relies on external identity providers (OIDC, LDAP, or Certificates). However, ServiceAccounts are managed directly by the Kubernetes API. These are primarily used for in-cluster processes, such as a pod that needs to talk to the API server to discover other services.
Checking which ServiceAccounts have high-level bindings
kubectl get rolebindings,clusterrolebindings --all-namespaces -o custom-columns='KIND:kind,NAMESPACE:metadata.namespace,NAME:metadata.name,SERVICE_ACCOUNT:subjects[?(@.kind=="ServiceAccount")].name'
Defining API Groups, Resources, and Verbs
When writing an RBAC manifest, we must be specific about the apiGroups. The core group is denoted by "". Other groups include apps, batch, and networking.k8s.io. Mixing these up often leads to roles that appear correct but fail to authorize the intended actions.
The verbs are the actions allowed. Common verbs include get, list, watch, create, update, patch, and delete. For high-security environments, we also watch for impersonate, which allows a user to act as another user, and bind or escalate, which are used to bypass RBAC restrictions.
Creating a Namespace-Level Role Manifest
We recently implemented a restricted role for an Indian FinTech's inventory service. The goal was to allow the service to read its own pod status and logs for debugging but prevent it from modifying any resources.
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: finance-app name: limited-pod-reader rules:
- apiGroups: [""]
resources: ["pods", "pods/log"] verbs: ["get", "list", "watch"]
Applying RoleBindings to Grant Access
The RoleBinding links the ServiceAccount to the Role defined above. This ensures that the inventory-svc-sa can only perform the actions specified within the finance-app namespace.
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: read-pods-binding namespace: finance-app subjects:
- kind: ServiceAccount
name: inventory-svc-sa namespace: finance-app roleRef: kind: Role name: limited-pod-reader apiGroup: rbac.authorization.k8s.io
Verifying Permissions with 'kubectl auth can-i'
Never assume a manifest works as intended. We use the auth can-i command to impersonate the subject and test the API response. This is the most effective way to verify that your Least Privilege implementation is actually functioning.
Test if the service account can delete pods (should return 'no')
$ kubectl auth can-i delete pods --as=system:serviceaccount:finance-app:inventory-svc-sa -n finance-app no
Test if the service account can list pods (should return 'yes')
$ kubectl auth can-i list pods --as=system:serviceaccount:finance-app:inventory-svc-sa -n finance-app yes
Check all permissions for the service account
$ kubectl auth can-i --list --as=system:serviceaccount:finance-app:inventory-svc-sa -n finance-app
Practical Kubernetes RBAC Examples
Example: Creating a Read-Only Role for Developers
In many Indian startups, developers need access to see what is running without the ability to change the state. Providing secure SSH access for teams alongside a read-only ClusterRole ensures that engineers can troubleshoot effectively without risking production stability.
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: developer-readonly rules:
- apiGroups: ["", "apps", "batch", "extensions"]
resources: ["pods", "deployments", "services", "jobs", "configmaps"] verbs: ["get", "list", "watch"]
Example: Granting Full Admin Access to a Specific Namespace
Sometimes a team needs full control over a sandbox environment. We use a RoleBinding to the built-in admin ClusterRole. This limits their "blast radius" to that specific namespace.
kubectl create rolebinding team-alpha-admin-binding \ --clusterrole=admin \ [email protected] \ --namespace=alpha-sandbox
Example: Restricting ServiceAccount Permissions for Pods
By default, every pod gets a default ServiceAccount token mounted at /var/run/secrets/kubernetes.io/serviceaccount/token. If an attacker gains shell access to a pod, they use this token to query the API. We recommend setting automountServiceAccountToken: false in the PodSpec unless API access is explicitly required.
Identify pods with automounted tokens that might be over-privileged
kubectl get pods -o jsonpath='{.items[?(@.spec.automountServiceAccountToken!=false)].metadata.name}'
Example: Cluster-Wide View Access for Monitoring Tools
Monitoring tools like Prometheus or Datadog need to scrape metrics from across the cluster. They require a ClusterRoleBinding to the view ClusterRole. For comprehensive visibility, integrating these with a centralized log monitoring solution is essential for threat detection.
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: prometheus-global-view subjects:
- kind: ServiceAccount
name: prometheus-sa namespace: monitoring roleRef: kind: ClusterRole name: view apiGroup: rbac.authorization.k8s.io
Kubernetes RBAC Best Practices for Production Environments
Implementing the Principle of Least Privilege (PoLP)
Every entity should have the minimum set of permissions required to perform its function. If a CI/CD runner only needs to update a deployment image, do not give it * (wildcard) permissions on deployments. Give it patch permissions on that specific resource. This approach is a core component of Zero Trust SSH and overall infrastructure hardening.
Avoiding the Use of Wildcards in Resource Definitions
Wildcards ("") are a security nightmare. They allow access to current and future* resources. If a new API resource is added to a group, a wildcard permission automatically grants access to it. We always explicitly list resources: ["pods", "services", "deployments"].
Managing and Auditing Default Service Accounts
The default ServiceAccount in every namespace should have zero permissions. We have seen attackers exploit the default SA to move laterally because a lazy administrator added a ClusterRoleBinding to system:serviceaccounts (a group containing every SA in the cluster).
Regularly Reviewing and Pruning Unused Permissions
Permissions tend to accumulate. We use automated tools to find unused RoleBindings. If a developer leaves the company or a project is decommissioned, their RBAC entries must be purged immediately to prevent credential reuse.
Using Tools like RBAC Tool or Rakess for Visualization
Visualizing RBAC is difficult with kubectl alone. We use rbac-tool to generate a matrix of who can do what. This makes it easier to spot anomalies, such as a developer having delete rights on nodes.
Install and use rbac-tool to visualize permissions
kubectl krew install rbac-tool kubectl rbac-tool viz
Common Kubernetes RBAC Configuration Mistakes to Avoid
Over-Privileging the 'system:authenticated' Group
This group includes any user or service account that has successfully authenticated. In many clusters, we find that system:authenticated is bound to the view role. While this seems harmless, it allows any pod in the cluster to see the configuration of every other pod, including environment variables that might contain secrets or database connection strings.
Audit for dangerous bindings to unauthenticated or authenticated groups
kubectl get clusterrolebindings -o json | jq '.items[] | select(.subjects[]?.name == "system:unauthenticated" or .subjects[]?.name == "system:anonymous" or .subjects[]?.name == "system:authenticated")'
Confusing RoleBindings with ClusterRoleBindings
A common mistake is using a ClusterRoleBinding when a RoleBinding was intended. This results in the subject gaining permissions across all namespaces. For example, giving a log-shipper ClusterRoleBinding to read secrets across the whole cluster when it only needs to read logs in its own namespace.
Ignoring API Group Versioning in Manifests
RBAC rules apply to specific API groups. If you define a rule for extensions/v1beta1 (now deprecated), it may not cover resources moved to apps/v1. Always ensure your manifests use the current API group for the resource you are securing.
Privilege Escalation via Pod Creation
If a user has create permissions for pods, they can effectively become a cluster-admin. They can create a pod that mounts the host's filesystem or uses the host's network namespace. This is how CVE-2021-25741 is exploited. RBAC must be combined with Pod Security Admissions (PSA) to be effective.
Check who can create pods in the cluster
kubectl krew install who-can kubectl who-can create pods --all-namespaces
Logical Isolation in Indian Infrastructure Patterns
In the Indian context, cost optimization often leads to high-density clusters. When running multi-tenant workloads (e.g., different clients of a SaaS platform on one cluster), RBAC is the only thing standing between one client's data and another's.
We recommend using Namespace-based isolation combined with NetworkPolicies. RBAC handles the API plane, while NetworkPolicies handle the data plane. If you are handling financial data in INR, ensuring that the reconciliation-service cannot access the customer-support-namespace is a critical requirement for passing an RBI-mandated security audit.
To audit your current exposure, run the following command to identify which users have the ability to "exec" into pods, which is a common path for privilege escalation:
kubectl who-can create pods/exec --all-namespaces
