DEV Community

Cover image for Understanding Kubernetes RBAC: Roles, RoleBindings, and and Client Certificates
Adeoye Malumi
Adeoye Malumi

Posted on

Understanding Kubernetes RBAC: Roles, RoleBindings, and and Client Certificates

One thing I discovered today is that before Kubernetes can decide what you're allowed to do, it first needs to know who you are.

That means there are two separate steps:

  1. Authentication – Proving your identity using a client certificate.
  2. Authorization – Deciding what you're allowed to do using RBAC.

In this lab, I created my own Kubernetes user named adeoye, authenticated the user with a certificate, and then used RBAC to control what that user could access.


Step 1 – Generate a Private Key

openssl genrsa -out adeoye.key 2048
Enter fullscreen mode Exit fullscreen mode

This generates a 2048-bit RSA private key.

Think of this as your digital identity. It should never be shared.


Step 2 – Generate a Certificate Signing Request (CSR)

openssl req -new -key adeoye.key -out adeoye.csr -subj "/CN=adeoye"
Enter fullscreen mode Exit fullscreen mode

A Certificate Signing Request asks Kubernetes to issue a client certificate.

The important part is:

/CN=adeoye
Enter fullscreen mode Exit fullscreen mode

The Common Name (CN) becomes the Kubernetes username.


Step 3 – Base64 Encode the CSR

cat adeoye.csr | base64 | tr -d '\n'
Enter fullscreen mode Exit fullscreen mode

Kubernetes expects the CSR to be embedded inside a YAML manifest as Base64-encoded text. The tr -d '\n' removes line breaks so it becomes a single continuous string.


Step 4 – Submit the CSR

After inserting the encoded CSR into csr.yaml, I submitted it.

kubectl apply -f csr.yaml
Enter fullscreen mode Exit fullscreen mode

Initially, the request was in the Pending state so I had to approve the request.


Step 5 – Approve the Certificate

kubectl certificate approve adeoye
Enter fullscreen mode Exit fullscreen mode

Approving the request tells Kubernetes to trust the user and issue a signed client certificate.


Step 6 – Extract the Signed Certificate

kubectl get csr adeoye -o jsonpath='{.status.certificate}' | base64 -d > adeoye.crt
Enter fullscreen mode Exit fullscreen mode

Now I had:

  • adeoye.key
  • adeoye.crt

The key proves my identity, while the certificate proves Kubernetes trusts that identity.


Step 7 – Configure kubectl

kubectl config set-credentials adeoye \
--client-key=adeoye.key \
--client-certificate=adeoye.crt
Enter fullscreen mode Exit fullscreen mode

Then I created a context.

kubectl config set-context adeoye \
--cluster=kind-cka-cluster3 \
--user=adeoye
Enter fullscreen mode Exit fullscreen mode

A mistake I made: I initially typed lind-cka-cluster3 instead of kind-cka-cluster3, which caused API errors until I corrected the cluster name.

Finally, I switched to my new user.

kubectl config use-context adeoye
Enter fullscreen mode Exit fullscreen mode

At this point I was authenticated, but I still didn't have permissions to perform actions.


RBAC Time!

Authentication tells Kubernetes who I am.

RBAC tells Kubernetes what I'm allowed to do.

Creating the Role

role.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader

rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs:
    - get
    - watch
    - list
Enter fullscreen mode Exit fullscreen mode

This Role only allows reading Pods.

Apply it:

kubectl apply -f role.yaml
Enter fullscreen mode Exit fullscreen mode

Creating the RoleBinding

binding.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default

subjects:
- kind: User
  name: adeoye
  apiGroup: rbac.authorization.k8s.io

roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io
Enter fullscreen mode Exit fullscreen mode

This binds the adeoye user to the pod-reader Role.

kubectl apply -f binding.yaml
Enter fullscreen mode Exit fullscreen mode

Testing the Permissions

Verify permission:

kubectl auth can-i get pods
Enter fullscreen mode Exit fullscreen mode

Output:

yes
Enter fullscreen mode Exit fullscreen mode

List Pods:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

Output:

No resources found in default namespace.
Enter fullscreen mode Exit fullscreen mode

That means the command succeeded, but there were simply no Pods.

Next, I tried listing Deployments.

kubectl get deploy
Enter fullscreen mode Exit fullscreen mode

Output:

Error from server (Forbidden): deployments.apps is forbidden
Enter fullscreen mode Exit fullscreen mode

Why Could I Read Pods but Not Deployments?

The answer is in the Role.

It only grants access to:

resources:
- pods
Enter fullscreen mode Exit fullscreen mode

It does not include Deployments.

Deployments belong to the apps API group, while Pods belong to the core API group.

Since my RoleBinding connects adeoye to the pod-reader Role, Kubernetes only allows the permissions defined there.

No Deployment permissions were granted, so Kubernetes correctly denied access.

This is the Principle of Least Privilege in action.


What I Learned

  • Kubernetes first authenticates users using client certificates.
  • The certificate's Common Name becomes the Kubernetes username.
  • A Role defines permissions.
  • A RoleBinding assigns those permissions to users.
  • Authentication and authorization are separate processes.
  • RBAC only grants explicitly defined permissions.
  • Pods and Deployments are different resources, so access to one doesn't imply access to the other.

Final Thoughts

Today's lab tied together authentication and authorization. Creating my own client certificate, configuring a new kubectl context, and then restricting that user with RBAC made the concepts much easier to understand.

Security in Kubernetes isn't about giving users full access,but about giving them exactly the permissions they need, nothing more.

Top comments (0)