Skip to content

Creating User and Role in Kubernetes and Binding Them via RBAC

In Kubernetes there are no “users” in the traditional sense — there are ServiceAccounts and certificates, bound to roles through RBAC. Without proper configuration, anyone holding a kubeconfig gets full access to the cluster. Below is the complete cycle: create a ServiceAccount, define permissions, bind them, and verify.

Creating ServiceAccount and Generating kubeconfig

Start by creating a ServiceAccount in the target namespace:

kubectl create serviceaccount devops-sa -n staging

To generate a kubeconfig, extract the token from secrets and build the config file:

SA_NAME=devops-sa
SA_NS=staging

TOKEN=$(kubectl -n $SA_NS get secret $(kubectl -n $SA_NS get sa $SA_NAME -o jsonpath='{.secrets[0].name}') -o jsonpath='{.data.token}' | base64 --decode)

kubectl config set-credentials $SA_NAME --token=$TOKEN
kubectl config set-context devops-staging --cluster=$(kubectl config current-context) --user=$SA_NAME --namespace=$SA_NS
kubectl config use-context devops-staging
Warning

A ServiceAccount token is a plain bearer token. If the kubeconfig leaks, an attacker gains access with that SA’s privileges. Store the file with 600 permissions.

Defining Role and ClusterRole

A Role is namespace-scoped; a ClusterRole is cluster-scoped. The difference is critical: a Role grants no rights outside its namespace, a ClusterRole does.

Example Role allowing read and create operations on pods in the staging namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: staging
  name: pod-reader-writer
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "watch", "create", "delete"]

Example ClusterRole for managing ingresses across the entire cluster:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: ingress-manager
rules:
  - apiGroups: ["networking.k8s.io"]
    resources: ["ingresses"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
Tip

An empty string "" in apiGroups refers to the core API group (v1). For apps, networking, batch — specify the corresponding groups. The full list of groups is available via kubectl api-resources.

Creating RoleBinding and ClusterRoleBinding

A RoleBinding binds a Role to a subject within a namespace. A ClusterRoleBinding binds a ClusterRole cluster-wide.

Binding a Role to a ServiceAccount in the staging namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: devops-pod-access
  namespace: staging
subjects:
  - kind: ServiceAccount
    name: devops-sa
    namespace: staging
roleRef:
  kind: Role
  name: pod-reader-writer
  apiGroup: rbac.authorization.k8s.io

Binding a ClusterRole to the same SA (now with cluster-wide ingress rights):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: devops-ingress-access
subjects:
  - kind: ServiceAccount
    name: devops-sa
    namespace: staging
roleRef:
  kind: ClusterRole
  name: ingress-manager
  apiGroup: rbac.authorization.k8s.io
BindingScopeRole typeWhen to use
RoleBindingSingle namespaceRole or ClusterRoleRead/write in a specific ns
ClusterRoleBindingEntire clusterClusterRoleGlobal rights (node, pv, dns)

Verifying and Debugging Access Rights

After applying the YAML, confirm the SA actually received the intended permissions:

kubectl auth can-i get pods --as=system:serviceaccount:staging:devops-sa -n staging
kubectl auth can-i create pods --as=system:serviceaccount:staging:devops-sa -n staging
kubectl auth can-i get ingresses --as=system:serviceaccount:staging:devops-sa

The last command checks via ClusterRoleBinding — it returns yes if the binding is correct.

If something doesn’t work, check the audit log or use kubectl auth reconcile with --dry-run=server for a preview:

kubectl auth reconcile role-binding.yaml --dry-run=server

Another useful trick is to find which RoleBindings are attached to a specific SA:

kubectl get rolebindings,clusterrolebindings --all-namespaces -o json | \
  jq '.items[] | select(.subjects[]?.name=="devops-sa") | {name: .metadata.name, kind: .kind, namespace: .metadata.namespace}'
Note

kubectl auth can-i checks permissions only — it does not account for NetworkPolicy or PodSecurityPolicy. If a pod fails to start, the issue may lie there.

Bottom line: RBAC in Kubernetes works as a chain — SA → Role/ClusterRole → Binding. Each link can be verified independently, which greatly simplifies debugging. Start with minimum privileges and expand as needed; never grant cluster-admin without a compelling reason.