Asia/Kolkata
Posts

How to Create and Manage User Access in Kubernetes with RBAC

Step-by-step: create a Kubernetes user with certificate-based auth, define a Role, bind it, build a kubeconfig, and verify access — all with real commands.
June 15, 2023
Share
How to Create and Manage User Access in Kubernetes with RBAC
On this page
Kubernetes doesn't manage users. There's no kubectl create user. What Kubernetes does manage is authorisation — what an authenticated identity is allowed to do. That's RBAC. This is a complete walkthrough: create a user via client certificate, define what they can access with a Role, bind it, hand them a kubeconfig, and verify it works.
Authentication is handled outside Kubernetes — via client certificates, OIDC tokens, or service accounts. RBAC only kicks in after authentication to decide what that identity is allowed to do. For certificate-based users, the Common Name (CN) in the cert becomes the username. The O (Organisation) field becomes the group. That's it — no user objects, no password database.
Create a working directory and generate a private key and Certificate Signing Request for the user jane:
Bash
mkdir rbac && cd rbac

openssl genrsa -out jane.key 2048
openssl req -new -key jane.key -subj "/CN=jane/O=developers" -out jane.csr
The /O=developers part is optional but useful — it lets you bind permissions to the developers group later rather than individual users.
Encode the CSR and create a CertificateSigningRequest object:
Bash
cat jane.csr | base64 | tr -d '\n'
Paste that output into the request field below:
Yaml
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: jane
spec:
  request: <base64-encoded-csr>
  signerName: kubernetes.io/kube-apiserver-client
  expirationSeconds: 86400
  usages:
    - client auth
Bash
kubectl apply -f csr.yaml
kubectl certificate approve jane
Bash
kubectl get csr jane -o jsonpath='{.status.certificate}' | base64 -d > jane.crt
You now have jane.key (private key) and jane.crt (signed certificate). These are the credentials.
Create a jane.kubeconfig file so she can actually talk to the cluster:
Yaml
apiVersion: v1
kind: Config
clusters:
- cluster:
    certificate-authority-data: <base64 of /etc/kubernetes/pki/ca.crt>
    server: https://<api-server-ip>:<port>
  name: my-cluster
contexts:
- context:
    cluster: my-cluster
    user: jane
  name: jane-context
current-context: jane-context
users:
- name: jane
  user:
    client-certificate-data: <base64 of jane.crt>
    client-key-data: <base64 of jane.key>
To get the base64 values:
Bash
cat /etc/kubernetes/pki/ca.crt | base64 | tr -d '\n'
cat jane.crt | base64 | tr -d '\n'
cat jane.key | base64 | tr -d '\n'
Test it:
Bash
kubectl get pods --kubeconfig jane.kubeconfig
You'll get a Forbidden error — that's expected. Jane exists now, but has no permissions yet.
A Role scopes permissions to a namespace. A ClusterRole applies cluster-wide. Start narrow:
Yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader-role
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]
This is read-only access to pods and their logs in default. No exec, no delete, nothing else.
Yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-reader-binding
  namespace: default
subjects:
  - kind: User
    name: jane
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader-role
  apiGroup: rbac.authorization.k8s.io
Bash
kubectl apply -f role.yaml
kubectl apply -f bind.yaml
Verify they're created:
Bash
kubectl get role pod-reader-role
kubectl get rolebindings -n default
Bash
kubectl get pods --kubeconfig jane.kubeconfig
# works now

kubectl delete pod <name> --kubeconfig jane.kubeconfig
# Error from server (Forbidden)
Or test without switching kubeconfig:
Bash
kubectl auth can-i list pods --namespace=default --as=jane
# yes

kubectl auth can-i delete pods --namespace=default --as=jane
# no
auth can-i is the cleanest way to check permissions — no need to impersonate the user or use their credentials.
Don't use user certificates for applications. If a workload inside the cluster needs API access, use a ServiceAccount. Certificates are for humans accessing from outside. Bind to groups, not individuals. If you're managing more than a handful of users, binding roles to the O=developers group (or an OIDC group claim) scales much better than per-user bindings. RoleBinding vs ClusterRoleBinding. A ClusterRole scoped via RoleBinding to a namespace gives namespace admin rights with no blast radius outside it. A ClusterRoleBinding is cluster-wide — use it sparingly. Audit what you've granted. RBAC permissions are additive and easy to forget. Run this regularly:
Bash
kubectl get rolebindings,clusterrolebindings -A -o wide
Privilege creep is real. Someone gets temporary access for a task and it never gets revoked. Regular audits catch it before it becomes a security incident.
Originally published on Techbeatly.