TestForge Blog
← All Posts

Kubernetes Dev & Ops in Practice 2 — Namespace & RBAC Design

Namespace isolation strategies and RBAC design principles for multi-team, multi-environment Kubernetes operations. A practical guide to maintaining least privilege while maximizing developer productivity.

TestForge Team ·

Why Namespace and RBAC Must Come First

When you spin up a cluster, only the default namespace exists. Teams often start by deploying everything there, but as teams grow and services multiply, this structure inevitably causes problems.

  • Team A accidentally runs kubectl delete pods --all and takes down Team B’s services
  • A developer modifies production Secrets by mistake
  • One team’s workload monopolizes cluster resources

Namespace and RBAC are the first structural line of defense against these problems.


1. Namespace Isolation Strategies

Environment-Based (Small Teams)

cluster
├── development
├── staging
└── production
apiVersion: v1
kind: Namespace
metadata:
  name: development
  labels:
    environment: development
    team: platform
---
apiVersion: v1
kind: Namespace
metadata:
  name: staging
  labels:
    environment: staging
---
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    environment: production

Team + Environment (Medium to Large Teams)

cluster
├── team-a-dev
├── team-a-prod
├── team-b-dev
├── team-b-prod
└── platform       # shared infra (monitoring, ingress, etc.)

Per-team namespaces enable resource isolation and independent deployments between teams.

Service-Based (Microservices)

cluster
├── auth-service
├── payment-service
├── notification-service
└── platform

Per-service namespaces let you use NetworkPolicy to precisely control inter-service communication.


2. ResourceQuota — Per-Namespace Resource Caps

Without per-namespace resource caps, one team’s workload can consume the entire cluster.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-a-quota
  namespace: team-a-dev
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    pods: "20"
    services: "10"
    persistentvolumeclaims: "5"
    secrets: "20"
    configmaps: "20"

LimitRange — Enforce Defaults

Without explicit resource declarations, Pods run without limits. LimitRange enforces defaults.

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: team-a-dev
spec:
  limits:
    - type: Container
      default:
        cpu: "500m"
        memory: 256Mi
      defaultRequest:
        cpu: "100m"
        memory: 128Mi
      max:
        cpu: "2"
        memory: 2Gi

3. RBAC Design Principles

RBAC defines who (Subject) can do what (Verb) to which resources (Resource).

Role Hierarchy

RoleTargetPermissions
cluster-adminPlatform teamEntire cluster
namespace-adminTeam leadFull namespace
developerDeveloperRead + Pod ops + logs
viewerAuditors, internsRead-only

Developer Role

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: developer
  namespace: team-a-dev
rules:
  - apiGroups: [""]
    resources: ["pods", "services", "configmaps", "endpoints", "events"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["pods/exec", "pods/log", "pods/portforward"]
    verbs: ["get", "create"]
  - apiGroups: ["apps"]
    resources: ["deployments", "replicasets", "statefulsets"]
    verbs: ["get", "list", "watch", "patch"]
  # List secrets but never read their contents
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["list"]

Viewer Role

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: viewer
  namespace: team-a-dev
rules:
  - apiGroups: ["", "apps", "batch"]
    resources: ["*"]
    verbs: ["get", "list", "watch"]

RoleBinding — Assign Roles to Users

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: developer-binding
  namespace: team-a-dev
subjects:
  - kind: User
    name: alice@company.com
    apiGroup: rbac.authorization.k8s.io
  - kind: Group
    name: team-a-developers
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: developer
  apiGroup: rbac.authorization.k8s.io

4. ServiceAccount Least Privilege

If a Pod doesn’t need to call the Kubernetes API, disable the default ServiceAccount token mount.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app
  namespace: team-a-dev
automountServiceAccountToken: false

When API access is required, create a dedicated ServiceAccount with minimal permissions.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: resource-watcher
  namespace: platform
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: resource-watcher
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: resource-watcher
subjects:
  - kind: ServiceAccount
    name: resource-watcher
    namespace: platform
roleRef:
  kind: ClusterRole
  name: resource-watcher
  apiGroup: rbac.authorization.k8s.io

5. Validating RBAC

# Check a specific user's permissions
kubectl auth can-i get pods --namespace team-a-dev --as alice@company.com
kubectl auth can-i delete secrets --namespace production --as alice@company.com

# List all your current permissions
kubectl auth can-i --list --namespace team-a-dev

# Check ServiceAccount permissions
kubectl auth can-i get deployments \
  --as system:serviceaccount:platform:resource-watcher

6. Namespace Initialization Script

#!/bin/bash
# init-namespace.sh <team> <env>

TEAM=$1
ENV=$2
NS="${TEAM}-${ENV}"

kubectl create namespace $NS
kubectl label namespace $NS team=$TEAM environment=$ENV

kubectl apply -f - <<EOF
apiVersion: v1
kind: ResourceQuota
metadata:
  name: quota
  namespace: $NS
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    pods: "30"
EOF

kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: developer-binding
  namespace: $NS
subjects:
  - kind: Group
    name: ${TEAM}-developers
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: developer
  apiGroup: rbac.authorization.k8s.io
EOF

echo "Namespace $NS initialized."

Summary

Design ElementCore Principle
NamespaceSeparate by team or environment, define defaults
ResourceQuotaCap CPU/memory/objects per namespace
LimitRangeEnforce defaults on Pods without resource specs
RBACLeast privilege; no Secret reads by default
ServiceAccountDisable token mount when API access isn’t needed

Next: Workload Pattern Selection — when to use Deployment, StatefulSet, DaemonSet, and Job/CronJob.