How to Configure Kubernetes RBAC on RHEL 7

Role-Based Access Control (RBAC) is the standard authorization mechanism in Kubernetes, allowing administrators to define precisely which users, groups, and service accounts can perform which actions on which resources. Without RBAC, any authenticated user could potentially read secrets, delete deployments, or escalate their privileges across namespaces. On RHEL 7 enterprise environments where Kubernetes clusters often serve multiple teams and applications, a well-designed RBAC policy is a security requirement rather than an optional enhancement. This tutorial covers the four core RBAC objects — Role, ClusterRole, RoleBinding, and ClusterRoleBinding — and walks through practical examples including creating read-only access for pods, binding roles to users and service accounts, and integrating RBAC with Helm deployments.

Prerequisites

  • RHEL 7 system with a running Kubernetes cluster
  • kubectl configured with admin-level access (system:masters or cluster-admin)
  • Basic familiarity with kubectl get and YAML manifests
  • OpenSSL available for generating user certificates (pre-installed on RHEL 7)
  • RBAC enabled on the API server (enabled by default in kubeadm clusters)

Step 1: Understand RBAC Object Types

Before writing any YAML, it is important to understand the four RBAC primitives and when to use each one.

Role vs ClusterRole

A Role grants permissions within a single namespace. If you want a developer to read pods only in the dev namespace, a Role is the correct object. A ClusterRole grants permissions cluster-wide, across all namespaces, and can also be used to control access to non-namespaced resources like nodes, persistent volumes, and custom resource definitions.

RoleBinding vs ClusterRoleBinding

A RoleBinding binds a Role or ClusterRole to a subject within a single namespace. A ClusterRoleBinding binds a ClusterRole to a subject cluster-wide. You can bind a ClusterRole with a namespaced RoleBinding to grant cluster-level role definitions scoped to one namespace, which is a common pattern for reusable roles across teams.

Step 2: Create a Read-Only Role for Pods

Create a namespace for this demonstration, then define a role that allows only reading pods and their logs:

kubectl create namespace dev-team

Save the following to pod-reader-role.yaml:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: dev-team
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list", "watch"]
kubectl apply -f pod-reader-role.yaml
kubectl get role pod-reader -n dev-team

The empty string in apiGroups refers to the core API group, which includes pods, services, configmaps, and secrets. The verbs list determines allowed operations. The values get, list, and watch are read-only. Never add create, update, patch, or delete unless explicitly required by the use case.

Step 3: Create a User Certificate and Bind the Role

Kubernetes does not manage user accounts internally. The most common approach on self-managed clusters is to issue client certificates signed by the cluster CA.

Generate a private key and certificate signing request for a user named alice:

openssl genrsa -out alice.key 2048

openssl req -new -key alice.key -out alice.csr 
  -subj "/CN=alice/O=dev-team"

Sign the CSR with the Kubernetes cluster CA, typically located at /etc/kubernetes/pki/ on kubeadm clusters:

openssl x509 -req -in alice.csr 
  -CA /etc/kubernetes/pki/ca.crt 
  -CAkey /etc/kubernetes/pki/ca.key 
  -CAcreateserial 
  -out alice.crt 
  -days 365

Add the user to your kubeconfig and create a context for them:

kubectl config set-credentials alice 
  --client-certificate=alice.crt 
  --client-key=alice.key

kubectl config set-context alice-context 
  --cluster=kubernetes 
  --namespace=dev-team 
  --user=alice

Now bind the pod-reader role to alice in the dev-team namespace. Save to alice-rolebinding.yaml:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: alice-pod-reader
  namespace: dev-team
subjects:
- kind: User
  name: alice
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io
kubectl apply -f alice-rolebinding.yaml

Step 4: Create and Bind a ServiceAccount

Applications running inside pods authenticate using ServiceAccounts rather than user certificates. Create a ServiceAccount and bind the pod-reader role to it:

kubectl create serviceaccount pod-watcher -n dev-team

Save the binding to pod-watcher-binding.yaml:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-watcher-binding
  namespace: dev-team
subjects:
- kind: ServiceAccount
  name: pod-watcher
  namespace: dev-team
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io
kubectl apply -f pod-watcher-binding.yaml

To use this ServiceAccount in a pod, reference it in the pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: audit-pod
  namespace: dev-team
spec:
  serviceAccountName: pod-watcher
  containers:
  - name: kubectl
    image: bitnami/kubectl:latest
    command: ["sleep", "3600"]

Step 5: Verify Permissions with kubectl auth can-i

The kubectl auth can-i command is the fastest way to audit what a user or service account can do without actually performing the action:

# Check as yourself (admin)
kubectl auth can-i delete pods -n dev-team

# Check as alice
kubectl auth can-i list pods -n dev-team --as=alice
kubectl auth can-i delete pods -n dev-team --as=alice

# Check as a service account
kubectl auth can-i list pods -n dev-team 
  --as=system:serviceaccount:dev-team:pod-watcher

Expected output for allowed actions is yes and for denied actions is no. Use the --list flag to enumerate every verb allowed for a given subject:

kubectl auth can-i --list -n dev-team --as=alice

Step 6: Create a ClusterRole and ClusterRoleBinding

Some resources are cluster-scoped and cannot be accessed via namespaced Roles. To grant a user read-only access to nodes and persistent volumes:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cluster-resource-reader
rules:
- apiGroups: [""]
  resources: ["nodes", "persistentvolumes", "namespaces"]
  verbs: ["get", "list", "watch"]
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: alice-cluster-reader
subjects:
- kind: User
  name: alice
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-resource-reader
  apiGroup: rbac.authorization.k8s.io
kubectl apply -f cluster-reader-role.yaml
kubectl apply -f alice-clusterrolebinding.yaml

Step 7: RBAC for Helm Deployments

Helm 3 runs with the permissions of the kubeconfig user that executes it. When Helm runs from inside a CI/CD pod, that pod’s ServiceAccount needs appropriate permissions to create, update, and delete Kubernetes resources. Create a ServiceAccount for Helm with namespace-scoped deployment rights:

kubectl create serviceaccount helm-deployer -n dev-team

kubectl create rolebinding helm-deployer-binding 
  --clusterrole=admin 
  --serviceaccount=dev-team:helm-deployer 
  --namespace=dev-team

Using the built-in admin ClusterRole scoped to one namespace via a namespaced RoleBinding is a pragmatic pattern for CI/CD pipelines. It grants full namespace control without cluster-admin privileges, preventing a compromised CI runner from affecting other namespaces.

Step 8: Switch kubeconfig Context and Test

Switch to the alice context to test that permissions are working as expected:

kubectl config use-context alice-context
kubectl get pods -n dev-team        # Should succeed
kubectl get secrets -n dev-team     # Should be denied
kubectl delete pod audit-pod -n dev-team   # Should be denied

Switch back to the admin context when done:

kubectl config use-context kubernetes-admin@kubernetes

If a command that should be denied returns an error like Error from server (Forbidden), your RBAC policies are working correctly. That is the expected and desired behavior.

Conclusion

You have configured Kubernetes RBAC on RHEL 7 by creating namespaced Roles and cluster-wide ClusterRoles, binding them to users and ServiceAccounts using RoleBindings and ClusterRoleBindings, and verified the results using kubectl auth can-i. RBAC policies should always follow the principle of least privilege: start with the minimum read-only access and expand permissions only when a specific operational need is demonstrated and approved. Audit your RBAC configuration regularly using kubectl auth can-i --list or third-party tools such as rbac-lookup and kubectl-who-can to identify over-privileged accounts before they become security vulnerabilities in your RHEL 7 Kubernetes environment.