Introduction
In Kubernetes, everything looks easy — until multiple teams start using the same cluster.
Without proper access control:
- Developers can delete production workloads
- Apps can access secrets they shouldn’t
- One mistake can impact the entire cluster
That’s where Namespaces + RBAC come in.
👉 Namespace = Isolation
👉 RBAC = Control who can do what
In this blog, we’ll build a real working example step-by-step.
What We Are Building
We will:
✅ Create a namespace
✅ Create a service account (user)
✅ Create a Role (permissions)
✅ Bind it using RoleBinding
✅ Verify access
Step 1: Create a Namespace
Namespaces isolate resources in Kubernetes.
kubectl create namespace dev-team
Verify:
kubectl get ns
Step 2: Create a Service Account
In real setups, pods or applications use ServiceAccounts.
kubectl create serviceaccount dev-user -n dev-team
👉 ServiceAccounts act as identities inside Kubernetes
👉 They are used in RBAC as “subjects”
Step 3: Create a Role (Permissions)
A Role defines what actions are allowed in a namespace
Roles are always namespace-scoped
✅ Example: Allow user to read pods
Create file: role-pod-reader.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: dev-team
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
Apply it:
kubectl apply -f role-pod-reader.yaml
🧠 What this means
- apiGroups: “” → core resources (pods, services)
- resources: pods → target resource
- verbs: get, list, watch → read-only
👉 If not explicitly allowed → access is denied (default deny model)
Step 4: Create a RoleBinding
Role alone does nothing.
👉 You must attach it to a user/service account using RoleBinding
Create file: rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-user-binding
namespace: dev-team
subjects:
- kind: ServiceAccount
name: dev-user
namespace: dev-team
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Apply:
kubectl apply -f rolebinding.yaml
🧠 What this does
- Links dev-user → pod-reader Role
- Only inside
dev-teamnamespace
👉 RoleBinding grants permissions within a namespace only.
Step 5: Verify RBAC Permissions
Now comes the most important part ✅
Check what user can do
kubectl auth can-i get pods \
--as system:serviceaccount:dev-team:dev-user \
-n dev-team
👉 Expected output:
yes
Try restricted action
kubectl auth can-i delete pods \
--as system:serviceaccount:dev-team:dev-user \
-n dev-team
👉 Expected output:
no
👉 This proves:
- User can read pods ✅
- User cannot delete pods ❌
Step 6: Validate with Real Resource
Create a test pod:
kubectl run test-pod --image=nginx -n dev-team
Now test from the user perspective:
kubectl get pods \
--as system:serviceaccount:dev-team:dev-user \
-n dev-team
👉 Works ✅
Try delete:
kubectl delete pod test-pod \
--as system:serviceaccount:dev-team:dev-user \
-n dev-team
👉 Fails ❌ (expected)
✅ Step 1: Create a User Certificate
We simulate a user: dev-user
Generate private key
openssl genrsa -out dev-user.key 2048
Generate CSR (Certificate Signing Request)
openssl req -new -key dev-user.key -out dev-user.csr -subj "/CN=dev-user/O=dev-team"
👉 CN = username
👉 O = group
✅ Step 2: Create CSR in Kubernetes
Create file: csr.yaml
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: dev-user-csr
spec:
request: <base64-encoded-csr>
signerName: kubernetes.io/kube-apiserver-client
usages:
- client auth
Encode CSR
cat dev-user.csr | base64 | tr -d '\n'
👉 Paste output into request: field
Apply:
kubectl apply -f csr.yaml
✅ Step 3: Approve the Certificate
kubectl certificate approve dev-user-csr
Get certificate:
kubectl get csr dev-user-csr -o jsonpath='{.status.certificate}' | base64 -d > dev-user.crt
✅ Step 4: Create kubeconfig for User
kubectl config set-credentials dev-user \
--client-certificate=dev-user.crt \
--client-key=dev-user.key
kubectl config set-context dev-user-context \
--cluster=<cluster-name> \
--namespace=dev-team \
--user=dev-user
Switch user:
kubectl config use-context dev-user-context
👉 Now user exists BUT has no permissions yet ❌
✅ Step 5: Create Role (Same as before)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: dev-team
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
Apply:
kubectl apply -f role.yaml
✅ Step 6: Bind Role to User (IMPORTANT DIFFERENCE)
Now instead of ServiceAccount → we bind to User
Create file: rolebinding-user.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: user-access
namespace: dev-team
subjects:
- kind: User
name: dev-user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Apply:
kubectl apply -f rolebinding-user.yaml
✅ Step 7: Test Access
Now as dev-user:
kubectl get pods -n dev-team
👉 ✅ Should work
Try something restricted:
kubectl delete pods --all -n dev-team
👉 ❌ Should fail
✅ Important Difference (ServiceAccount vs Certificate User)
| Type | Used for |
|---|---|
| ServiceAccount | Apps/pods |
| Certificate User | Real users (developers/admins) |
✅ Key Concepts You Just Learned
| Concept | Meaning |
|---|---|
| Namespace | Logical isolation |
| Role | Defines permissions |
| RoleBinding | Assigns permissions |
| ServiceAccount | Identity |
| RBAC | Authorization layer |
⚠️ Common Mistakes (Very Important)
❌ Using cluster-admin everywhere
Gives full permissions → dangerous
❌ Not using namespaces
Everything becomes shared → no control
❌ Giving too many verbs
Always follow:
👉 Least privilege principle
Only give minimum required permissions
🚀 Real-World Use Case
Imagine:
- Dev team → access only dev namespace
- QA team → access staging
- CI/CD → update deployments only
RBAC ensures:
👉 No cross-access
👉 No accidental deletion
👉 Secure multi-team setup
🚀 InfraDecode Takeaway
Namespaces isolate — RBAC protects.
Every request in Kubernetes is checked by RBAC.
If you don’t define access → everything is denied.
If you overdefine access → everything is at risk.
Balance is the key.
Discover more from
Subscribe to get the latest posts sent to your email.
