Lesson 7.1: RBAC (Role-Based Access Control)


RBAC (Role-Based Access Control) is a method of regulating access to resources in a Kubernetes cluster based on the roles of individual users or groups. It provides fine-grained control over who can perform specific actions (e.g., create, read, update, delete) on specific resources (e.g., Pods, Services, Deployments).

Key Components of RBAC

  • Subjects: Entities that need access to resources.
    • Users: Individual users (e.g., john, admin).
    • Groups: Collections of users (e.g., developers, admins).
    • ServiceAccounts: Accounts used by applications or Pods to interact with the Kubernetes API.
  • Roles:
    • Define a set of permissions (e.g., get, list, create, delete) for specific resources.
    • Namespaced: Applies to resources within a specific namespace.
  • ClusterRoles:
    • Similar to Roles, but applies to cluster-scoped resources (e.g., Nodes, PersistentVolumes) or across all namespaces.
  • RoleBindings:
    • Bind a Role to a Subject (user, group, or ServiceAccount) within a specific namespace.
  • ClusterRoleBindings:
    • Bind a ClusterRole to a Subject across the entire cluster.

How RBAC Works

  • Define Roles/ClusterRoles: Create Roles or ClusterRoles to specify the permissions for specific resources.
  • Bind Roles to Subjects: Use RoleBindings or ClusterRoleBindings to associate Roles or ClusterRoles with Subjects.
  • Enforce Access Control: Kubernetes enforces the permissions defined in the Roles or ClusterRoles when a Subject tries to access a resource.

Manage TLS Certificates

How to issue a certificate for a user

A few steps are required in order to get a normal user to be able to authenticate and invoke an API. First, this user must have a certificate issued by the Kubernetes cluster, and then present that certificate to the Kubernetes API.

Create private key

[root@master managetls]# openssl genrsa -out adam.key 2048
[root@master managetls]# openssl req -new -key adam.key -out adam.csr -subj "/CN=adam"
[root@master managetls]# ls
adam.csr  adam.key
 
[root@master managetls]# cat adam.csr 
-----BEGIN CERTIFICATE REQUEST-----
MIICVDCCATw...
-----END CERTIFICATE REQUEST-----
 
# Has to be in base64 encoded value of the CSR file content.
[root@master managetls]# cat adam.csr | base64 | tr -d "\n"
LS0tLS1CRUd...

Create a CertificateSigningRequest

[root@master managetls]# cat csr.yml 
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: adam
spec:
  request: LS0tLS1CRUd...
  signerName: kubernetes.io/kube-apiserver-client
  usages:
  - client auth
 
[root@master managetls]# kubectl apply -f csr.yml 
certificatesigningrequest.certificates.k8s.io/adam created
 
[root@master managetls]# kubectl get csr 
NAME   AGE   SIGNERNAME                            REQUESTOR          REQUESTEDDURATION   CONDITION
adam   7s    kubernetes.io/kube-apiserver-client   kubernetes-admin   <none>              Pending

Why is the status pending ?

The CertificateSigningRequest (CSR) you created is in the Pending state because it needs to be approved by a Certificate Authority (CA). In Kubernetes, the CA is typically managed by the Kubernetes control plane, and the approval process involves manual or automated steps. Let’s break down the process and explain why the CSR is pending, who approves it, and how the CA works.

When you create a CSR, it is submitted to the Kubernetes API server, but it is not automatically approved. The CSR must be reviewed and approved by an entity with the appropriate permissions (e.g., a cluster administrator or an automated approval process).

Who Approves the CSR?

The approval of CSRs is typically handled by:

  • Cluster Administrators: A human operator with the necessary permissions to approve CSRs. They can manually approve or deny CSRs using kubectl.
  • Automated Approvers: In some clusters, an automated process (e.g., a controller) may approve CSRs based on predefined rules.
[root@master managetls]# kubectl describe csr adam 
Name:         adam
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"certificates.k8s.io/v1","kind":"CertificateSigningRequest","metadata":{"annotations":{},"name":"adam"},"spec":{"request":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1ZEQ0NBVHdDQVFBd0R6RU5NQXNHQTFVRUF3d0VZV1JoYlRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRApnZ0VQQURDQ0FRb0NnZ0VCQUtaYzd5QjB0RWF2ZEFMeGRxbW4wTDdnREdkUnRndWFPMmVWZ2h4YWNEV2FCY3h2CjkvVWNHTFVkdEUvTStKaHVFazJuYzdVemtsd3ZBN2daUFBKV1pYUDJGZGYzMHNyaXo3bzQ5WmE1Q3VjejkwUHEKSjVLanI5KzZ1QTJhRXBSSUcxL05IUCtIZDdQN3VDRTkxOGt1QzkwRTNNdjFmL3d4L1BpMTlDbkRtZVdQTXoveAo5ZHpBeXhnbnFhWmdhRVVJVi9Sc01TRnFycmwrMmVlRzJvYUlPbUNYd29hckxSZ1VKMUJqcjFLVk9na01kZWZwCmZkanprdklNMHVUNzU1cnNFbnpFVWlLRlVnUzdJbXZPTGF0YnUxakZZLy9KT3RURU96cnVwZVBxZGsxcFZOMUcKcDAweklrMUhPaVNtRTg3OW9qRDFuSlQxdXk2cm5hUGtWWkVBWHFFQ0F3RUFBYUFBTUEwR0NTcUdTSWIzRFFFQgpDd1VBQTRJQkFRQk5RNW15K2M2RHF0UHdaMGhubzYyLzU1SVdvVEI0d2RGUk9nZml5bjRleU0rRmdTbkkzdXFHCjRkQnNFRTI4bXFYQVdkNzMwdXFIdWJhdHVlVHlzZlhiRHM0OVNqdUF5dmJ2bGJqUkhucGFXNy9vL0JWdVJ4ZkUKYTNDNXZrSjN3T0tOaFNtaXFhVmsvOTRPR0xBaTVucThDS3hLQmxETU9VWG5Sak5GeUgyZjNYRWxqRGlwaHpSTwpLa0g3T2JOYmJKMno3bzh0T2VEVEVMUm1Ob2V3VjZLeDVkNVJWdGVqWWZ3TTZXazhCRGJDclowQWdnaW9MUEFqCmRvSzF3OXVuV3VtQk9LZ0w1V3hvZ3Q5Vjg1aFdlS05JQmtxa3RXRzNObFFwanhVSUJjVjBZbmZsejhQaFg2b1kKcnBSditoL0FCVUNxSGlmWmhSaTdlUzdqUEdua0taMnoKLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0tCg==","signerName":"kubernetes.io/kube-apiserver-client","usages":["client auth"]}}
 
CreationTimestamp:  Fri, 07 Mar 2025 21:14:03 +0545
Requesting User:    kubernetes-admin
Signer:             kubernetes.io/kube-apiserver-client
Status:             Pending
Subject:
         Common Name:    adam
         Serial Number:  
Events:  <none>

How to Approve the CSR

To approve the CSR, you need to use the kubectl certificate approve command. Here’s how:

[root@master managetls]# kubectl certificate approve adam 
certificatesigningrequest.certificates.k8s.io/adam approved
 
# The CONDITION field will change from Pending to Approved,Issued.
[root@master managetls]# kubectl get csr 
NAME   AGE     SIGNERNAME                            REQUESTOR          REQUESTEDDURATION   CONDITION
adam   2m53s   kubernetes.io/kube-apiserver-client   kubernetes-admin   <none>              Approved,Issued

What Happens After Approval?

Once the CSR is approved:

  • The Kubernetes CA signs the certificate and issues it.
  • The signed certificate is added to the CSR object in the status.certificate field.
  • You can extract the signed certificate and use it for authentication.
# Two methods to create adam.crt
# METHOD-1 - Automatic creation
[root@master testrbac]# kubectl get csr adam -o jsonpath='{.status.certificate}' | base64 --decode > adam.crt
# METHOD-2 - Copy from describe
[root@master managetls]# echo "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM5VENDQWQyZ0F3SUJBZ0lSQUpjakowNnJBUEFFY3dqMGp0MUF6WEV3RFFZSktvWklodmNOQVFFTEJRQXcKRlRFVE1CRUdBMVVFQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TlRBek1EY3hOVEkyTXpaYUZ3MHlOakF6TURjeApOVEkyTXpaYU1BOHhEVEFMQmdOVkJBTVRCR0ZrWVcwd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3CmdnRUtBb0lCQVFDbVhPOGdkTFJHcjNRQzhYYXBwOUMrNEF4blViWUxtanRubFlJY1duQTFtZ1hNYi9mMUhCaTEKSGJSUHpQaVliaEpOcDNPMU01SmNMd080R1R6eVZtVno5aFhYOTlMSzRzKzZPUFdXdVFybk0vZEQ2aWVTbzYvZgp1cmdObWhLVVNCdGZ6UnovaDNleis3Z2hQZGZKTGd2ZEJOekw5WC84TWZ6NHRmUXB3NW5sanpNLzhmWGN3TXNZCko2bW1ZR2hGQ0ZmMGJERWhhcTY1ZnRubmh0cUdpRHBnbDhLR3F5MFlGQ2RRWTY5U2xUb0pESFhuNlgzWTg1THkKRE5MaysrZWE3Qko4eEZJaWhWSUV1eUpyemkyclc3dFl4V1AveVRyVXhEczY3cVhqNm5aTmFWVGRScWROTXlKTgpSem9rcGhQTy9hSXc5WnlVOWJzdXE1Mmo1RldSQUY2aEFnTUJBQUdqUmpCRU1CTUdBMVVkSlFRTU1Bb0dDQ3NHCkFRVUZCd01DTUF3R0ExVWRFd0VCL3dRQ01BQXdId1lEVlIwakJCZ3dGb0FVZ3N5VjUyaEVZU1FZbGZ4NnFuNkMKQ21XQkRUSXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRmQ5TjdoU3drV2RFK3NtK1NhVzhha3hJcVBiVE5GTQo2NTVnSG5MU3NGQkxPYXY0VHFRMjFnN0JxdVM1ZjJ1R3lXSG9YeVdZVVFIc21nOTN5dC9DQ3Y5LzBNSFJ1eEZ2CjE4NENIMFJ5Z2NhNDVCZTB6R2xGS3FySFhiQzFud0lvRnNPajBVY0lXU1lNMG5jQW1lV2JFdUFNUTZrVVB1d3cKT2JaTkJDZG5ydEhYTzJZOTJSeTV6Ymo0Snh0VTNWT3BhcFNhQkdHKzIyVFpRRHptM2lyanpDNHF5R3pzbVR2QQo3Ui9HMEp0QzN2Y3Fyb2dlS2lGb2JwTkZhREpTTUFBVzFxdHVMZXFpRGFvblE4ZDBMRXcwTVNyMldsREZGaFdsClFaWFh0TVd5ak9zZkNscHdoS0thR0FhSnYvbUpsalhmKzZkT3VxSEtORmtURmkyTFd5SmtPK1U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" | base64 -d > adam.crt
 
# Output
[root@master managetls]# cat adam.crt
-----BEGIN CERTIFICATE-----
MIIC9TCCAd2gAwIBAgIRAJcjJ06rAPAEcwj0jt1AzXEwDQYJKoZIhvcNAQELBQAw
FTETMBEGA1UEAxMKa3ViZXJuZXRlczAeFw0yNTAzMDcxNTI2MzZaFw0yNjAzMDcx
NTI2MzZaMA8xDTALBgNVBAMTBGFkYW0wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCmXO8gdLRGr3QC8Xapp9C+4AxnUbYLmjtnlYIcWnA1mgXMb/f1HBi1
HbRPzPiYbhJNp3O1M5JcLwO4GTzyVmVz9hXX99LK4s+6OPWWuQrnM/dD6ieSo6/f
urgNmhKUSBtfzRz/h3ez+7ghPdfJLgvdBNzL9X/8Mfz4tfQpw5nljzM/8fXcwMsY
J6mmYGhFCFf0bDEhaq65ftnnhtqGiDpgl8KGqy0YFCdQY69SlToJDHXn6X3Y85Ly
DNLk++ea7BJ8xFIihVIEuyJrzi2rW7tYxWP/yTrUxDs67qXj6nZNaVTdRqdNMyJN
RzokphPO/aIw9ZyU9bsuq52j5FWRAF6hAgMBAAGjRjBEMBMGA1UdJQQMMAoGCCsG
AQUFBwMCMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUgsyV52hEYSQYlfx6qn6C
CmWBDTIwDQYJKoZIhvcNAQELBQADggEBAFd9N7hSwkWdE+sm+SaW8akxIqPbTNFM
655gHnLSsFBLOav4TqQ21g7BquS5f2uGyWHoXyWYUQHsmg93yt/CCv9/0MHRuxFv
184CH0Rygca45Be0zGlFKqrHXbC1nwIoFsOj0UcIWSYM0ncAmeWbEuAMQ6kUPuww
ObZNBCdnrtHXO2Y92Ry5zbj4JxtU3VOpapSaBGG+22TZQDzm3irjzC4qyGzsmTvA
7R/G0JtC3vcqrogeKiFobpNFaDJSMAAW1qtuLeqiDaonQ8d0LEw0MSr2WlDFFhWl
QZXXtMWyjOsfClpwhKKaGAaJv/mJljXf+6dOuqHKNFkTFi2LWyJkO+U=
-----END CERTIFICATE-----

Configure kubeconfig:

The user (adam) must be configured in the kubeconfig file and granted permissions via RBAC.

Add new credentials

[root@master testrbac]# kubectl config set-credentials adam --client-certificate=adam.crt --client-key=adam.key
User "adam" set.
[root@master testrbac]# kubectl config get-users | grep sanjeeb
sanjeeb

Add the context

[root@master testrbac]# kubectl config set-context sanjeeb --cluster=kind-cka-cluster2 --user=sanjeeb
Context "sanjeeb" created.
[root@master testrbac]# kubectl config get-contexts | grep sanjeeb
sanjeeb             kind-cka-cluster2   sanjeeb  

RBAC

Role and ClusterRole

  • An RBAC Role or ClusterRole contains rules that represent a set of permissions. Permissions are purely additive (there are no "deny" rules).
  • A Role always sets permissions within a particular namespace; when you create a Role, you have to specify the namespace it belongs in.
  • ClusterRole, by contrast, is a non-namespaced resource. The resources have different names (Role and ClusterRole) because a Kubernetes aobject always has to be either namespaced or not namespaced; it can't be both.
  • ClusterRoles have several uses. You can use a ClusterRole to:
    • define permissions on namespaced resources and be granted access within individual namespace(s)
    • define permissions on namespaced resources and be granted access across all namespaces
    • define permissions on cluster-scoped resources
  • If you want to define a role within a namespace, use a Role; if you want to define a role cluster-wide, use a ClusterRole.

In this section we will allow the user sanjeeb to access resources (e.g., Pods) in the Kubernetes cluster, you need to:

  • Define a Role (or ClusterRole) that specifies the permissions.
  • Bind the Role to the User using a RoleBinding (or ClusterRoleBinding).

Here are some commonly used resources in Roles and ClusterRoles:

APIGroup Resources
Core ("")pods, services, configmaps, secrets, nodes, persistentvolumes
appsdeployments, statefulsets, daemonsets, replicasets
rbac.authorization.k8s.ioroles, rolebindings, clusterroles, clusterrolebindings
networking.k8s.ionetworkpolicies, ingresses
storage.k8s.iostorageclasses, volumeattachments
batchjobs, cronjobs
autoscalinghorizontalpodautoscalers

Role

A Role defines a set of permissions (rules) within a specific namespace. It specifies what actions (verbs) can be performed on which resources (e.g., pods, services) in that namespace.

Creating a Role that allows get, list, and watch on Pods in the default namespace.

[root@master rbac]# cat simple-role.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
 
[root@master rbac]# kubectl apply -f simple-role.yaml 
role.rbac.authorization.k8s.io/pod-reader created

ClusterRole

  • "namespace" omitted since ClusterRoles are not namespaced
[root@master rbac]# cat simple-clusterrole.yml 
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: secret-reader
rules:
- apiGroups: [""] 
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]
 
[root@master rbac]# kubectl apply -f simple-clusterrole.yml 
clusterrole.rbac.authorization.k8s.io/secret-reader created

Key Differences in Binding

| Feature | Role | ClusterRole | | --- | --- | | Scope | Namespaced | Cluster-wide | | Resources | Namespaced resources only | Namespaced and cluster-wide resources | | Binding | RoleBinding (namespaced) | RoleBinding (namespaced) or ClusterRoleBinding (cluster-wide) | | Use Case | Fine-grained access within a namespace | Broad access across the cluster |

RoleBinding with Role

A role binding grants the permissions defined in a role to a user or set of users. It holds a list of subjects (users, groups, or service accounts), and a reference to the role being granted. A RoleBinding grants permissions within a specific namespace

[root@master rbac]# cat simple-rolebinding-with-role.yaml 
apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "jane" to read pods in the "default" namespace.
# You need to already have a Role named "pod-reader" in that namespace.
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
# You can specify more than one "subject"
- kind: User
  name: jane # "name" is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  # "roleRef" specifies the binding to a Role / ClusterRole
  kind: Role #this must be Role or ClusterRole
  name: pod-reader # this must match the name of the Role or ClusterRole you wish to bind to
  apiGroup: rbac.authorization.k8s.io
 
[root@master rbac]# kubectl apply -f simple-rolebinding-with-role.yaml 
rolebinding.rbac.authorization.k8s.io/read-pods created
[root@master rbac]# kubectl auth can-i get pods --as=jane -n=default
yes
[root@master rbac]# kubectl auth can-i watch pods --as=jane -n=default
yes
[root@master rbac]# kubectl auth can-i list pods --as=jane -n=default
yes
[root@master rbac]# kubectl auth can-i update pods --as=jane -n=default
no
[root@master rbac]# kubectl auth can-i delete pods --as=jane -n=default
no

RoleBinding with ClusterRole

A RoleBinding may reference any Role in the same namespace. Alternatively, a RoleBinding can reference a ClusterRole and bind that ClusterRole to the namespace of the RoleBinding. If you want to bind a ClusterRole to all the namespaces in your cluster, you use a ClusterRoleBinding.

[root@master rbac]# cat simple-rolebinding-with-clusterrole.yaml 
apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "dave" to read secrets in the "development" namespace.
# You need to already have a ClusterRole named "secret-reader".
kind: RoleBinding
metadata:
  name: read-secrets
  #
  # The namespace of the RoleBinding determines where the permissions are granted.
  # This only grants permissions within the "development" namespace.
  namespace: development
subjects:
- kind: User
  name: dave # Name is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io
 
[root@master rbac]# kubectl apply -f simple-rolebinding-with-clusterrole.yaml 
rolebinding.rbac.authorization.k8s.io/read-secrets created

Verification

[root@master rbac]# 
[root@master rbac]# kubectl auth can-i get secrets --as=dave -n=development
yes
[root@master rbac]# kubectl auth can-i watch secrets --as=dave -n=development
yes
[root@master rbac]# kubectl auth can-i list secrets --as=dave -n=development
yes
[root@master rbac]# kubectl auth can-i create secrets --as=dave -n=development
no
[root@master rbac]# kubectl auth can-i update secrets --as=dave -n=development
no
[root@master rbac]# kubectl auth can-i delete secrets --as=dave -n=development
no
 
# Vertification using user jane
[root@master rbac]# kubectl auth can-i get secrets --as=jane -n=development
no
 
#Adding user jane to the rolebinding with clusterrole 
[root@master rbac]# cat simple-rolebinding-with-clusterrole.yaml 
apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "dave" to read secrets in the "development" namespace.
# You need to already have a ClusterRole named "secret-reader".
kind: RoleBinding
metadata:
  name: read-secrets
  #
  # The namespace of the RoleBinding determines where the permissions are granted.
  # This only grants permissions within the "development" namespace.
  namespace: development
subjects:
- kind: User
  name: dave # Name is case sensitive
  apiGroup: rbac.authorization.k8s.io
- kind: User
  name: jane
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io
[root@master rbac]# kubectl apply -f simple-rolebinding-with-clusterrole.yaml 
rolebinding.rbac.authorization.k8s.io/read-secrets configured
 
# Verification as user jane
[root@master rbac]# kubectl auth can-i get secrets --as=jane -n=development
yes
[root@master rbac]# kubectl auth can-i watch secrets --as=jane -n=development
yes
[root@master rbac]# kubectl auth can-i list secrets --as=jane -n=development
yes
[root@master rbac]# kubectl auth can-i create secrets --as=jane -n=development
no

For instance, even though the following RoleBinding refers to a ClusterRole, "dave" & "jane" will only be able to read Secrets in the "development" namespace, because the RoleBinding's namespace (in its metadata) is "development".

# Verification using namespace default which is added 'development'
[root@master rbac]# kubectl auth can-i get secrets --as=jane -n=development
yes
[root@master rbac]# kubectl auth can-i get secrets --as=dave -n=development
yes
# Verification using namespace default which is not added 'default'
[root@master rbac]# kubectl auth can-i get secrets --as=dave -n=default
no
[root@master rbac]# kubectl auth can-i get secrets --as=jane -n=default
no

ClusterRoleBinding with ClusterRole

In the provided configuration, Sarah (the user sarah) has been granted access to get, watch, and list secrets in all namespaces across the cluster. This is because the ClusterRoleBinding (read-secrets-global) binds the ClusterRole (secret-reader) to Sarah cluster-wide.

[root@master rbac]# cat simple-clusterrolebinding.yml 
apiVersion: rbac.authorization.k8s.io/v1
# This cluster role binding allows anyone in the "manager" group to read secrets in any namespace.
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager # Name is case sensitive
  apiGroup: rbac.authorization.k8s.io
- kind: User
  name: sarah
  apiGroup: rbac.authorization.k8s.io  
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io
 
[root@master rbac]# kubectl apply -f simple-clusterrolebinding.yml 
clusterrolebinding.rbac.authorization.k8s.io/read-secrets-global created
[root@master rbac]# kubectl auth can-i get secrets --as=sarah -n=default
yes
[root@master rbac]# kubectl auth can-i get secrets --as=sarah -n=development
yes
[root@master rbac]# kubectl auth can-i get secrets --as=sarah --namespace=kube-system
yes
All systems normal

© 2025 2023 Sanjeeb KC. All rights reserved.