Kubernetes security can sound like a separate profession from Kubernetes operations. Sometimes it is. But beginners still need a practical foundation, because the first mistakes are usually simple: everything in default, every Pod using the default ServiceAccount, broad permissions copied from a tutorial, and Secrets treated like harmless YAML.

Namespaces and RBAC are not advanced topics to postpone until “real production.” They are the grammar of a shared cluster. Namespaces help divide the cluster into understandable areas. RBAC decides what users and workloads are allowed to do. Security basics keep an error in one place from becoming a cluster-wide problem.

The goal is not to become a security engineer in one blog post. The goal is to build a mental model that makes the next kubectl apply less naive.

Namespaces are a scope, not a fortress

A namespace is a way to divide many Kubernetes resources into named spaces. Pods, Services, Deployments, ConfigMaps, Secrets, Roles, and RoleBindings usually live in a namespace. Nodes, PersistentVolumes, StorageClasses, ClusterRoles, and Namespaces themselves are cluster-scoped.

You can see the difference with:

kubectl api-resources --namespaced=true
kubectl api-resources --namespaced=false

Namespaces are useful because they give teams and environments a place to work:

kubectl create namespace payments-dev
kubectl get pods -n payments-dev
kubectl get deploy,svc,cm,secret -n payments-dev

They also reduce accidents. Deleting a Deployment in payments-dev is bad. Deleting one in payments-prod is worse. Being explicit about namespaces makes mistakes more visible.

But a namespace is not a hard security boundary by itself. It does not automatically encrypt traffic, block network access, prevent all cross-namespace reads, or stop a privileged user from touching other namespaces. It is a scope that other controls can use.

This distinction matters. If someone says “we are safe because every team has a namespace,” ask what enforces that safety. RBAC? NetworkPolicy? ResourceQuota? Admission policies? Separate clusters for stronger isolation? The namespace is the address. The guardrails are separate.

What belongs in a namespace

For beginners, namespaces work well for:

  • Separating environments such as dev, staging, and prod
  • Separating teams or applications
  • Applying quotas and limits to a group of workloads
  • Scoping RBAC so a team can manage its own objects
  • Scoping NetworkPolicies

A small namespace setup might look like this:

apiVersion: v1
kind: Namespace
metadata:
  name: demo
  labels:
    purpose: learning
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: demo-quota
  namespace: demo
spec:
  hard:
    requests.cpu: "2"
    requests.memory: 4Gi
    limits.cpu: "4"
    limits.memory: 8Gi
    pods: "20"

The quota is not security in the narrow sense, but it is operational safety. It prevents one namespace from consuming unlimited resources by accident.

Common misconception: “I can use namespaces instead of labels.” You still need labels. Services select Pods by labels. Deployments manage Pods by labels. NetworkPolicies select Pods by labels. Namespaces organize the room; labels identify things inside the room.

RBAC answers a plain question

RBAC means Role-Based Access Control. The name sounds formal, but the core question is plain:

Who can do what to which resources?

In Kubernetes, “who” can be a human user, a group, or a ServiceAccount. “What” is a verb such as get, list, watch, create, update, patch, or delete. “Which resources” means Pods, Deployments, Secrets, ConfigMaps, and so on.

There are four main RBAC objects:

Role. A namespaced set of permissions.

RoleBinding. Grants a Role to a subject in a namespace.

ClusterRole. A cluster-scoped set of permissions. It can apply to cluster resources or be reused in namespaces.

ClusterRoleBinding. Grants a ClusterRole across the cluster.

Start with Role and RoleBinding when you can. Reach for ClusterRoleBinding only when the subject truly needs cluster-wide access.

Here is a read-only Role for a namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: workload-reader
  namespace: demo
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/log", "services", "configmaps"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["apps"]
    resources: ["deployments", "replicasets"]
    verbs: ["get", "list", "watch"]

And a binding for a ServiceAccount:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: dashboard
  namespace: demo
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: dashboard-reads-workloads
  namespace: demo
subjects:
  - kind: ServiceAccount
    name: dashboard
    namespace: demo
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: workload-reader

This does not let the ServiceAccount delete Pods, read Secrets, or modify Deployments. That is the point.

Test permissions, do not guess

kubectl auth can-i is one of the most useful beginner commands for RBAC:

kubectl auth can-i get pods -n demo
kubectl auth can-i delete pods -n demo
kubectl auth can-i get secrets -n demo

Admins can impersonate a subject to test its permissions:

kubectl auth can-i list pods -n demo \
  --as=system:serviceaccount:demo:dashboard

kubectl auth can-i get secrets -n demo \
  --as=system:serviceaccount:demo:dashboard

If the first returns yes and the second returns no, the Role above is doing what it should.

This matters because RBAC failures can look like application bugs. A controller may log forbidden: User "system:serviceaccount:demo:dashboard" cannot list resource "pods". The fix is not to make it cluster-admin. The fix is to understand what it actually needs.

The default ServiceAccount is easy to forget

Every namespace has a default ServiceAccount. If a Pod does not specify one, it uses that.

kubectl get serviceaccount -n demo
kubectl get pod web-abc123 -n demo -o jsonpath='{.spec.serviceAccountName}'; echo

For simple workloads that do not need the Kubernetes API, using a ServiceAccount token at all may be unnecessary. In many clusters, projected tokens are mounted only when needed or can be disabled per Pod:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: web
  namespace: demo
---
apiVersion: v1
kind: Pod
metadata:
  name: web
  namespace: demo
spec:
  serviceAccountName: web
  automountServiceAccountToken: false
  containers:
    - name: app
      image: nginx:1.27

Do not copy this blindly into controllers or operators. They often need API access. For a normal web app that never talks to the Kubernetes API, it is a good question to ask.

The safer habit is to create named ServiceAccounts for workloads that need permissions and bind only what they need. Avoid adding broad permissions to default, because then every forgotten Pod inherits them.

Secrets are encoded, not magically safe

Kubernetes Secrets are often misunderstood. The values in a Secret manifest are base64-encoded, not encrypted by the manifest format.

echo -n 'not-a-real-password' | base64
echo 'bm90LWEtcmVhbC1wYXNzd29yZA==' | base64 --decode; echo

Cluster storage may encrypt Secrets at rest if configured. Access to Secrets is controlled by RBAC. But anyone who can get secrets in a namespace can usually read their contents.

That means:

  • Do not grant Secret read access casually.
  • Do not commit real Secret values to Git.
  • Prefer external secret managers or sealed/encrypted secret workflows when available.
  • Rotate credentials when exposure is possible.

A beginner-friendly check:

kubectl auth can-i get secrets -n demo
kubectl get secrets -n demo

If many users and workloads can read Secrets “just in case,” the namespace is not as isolated as it looks.

Network access is a separate layer

RBAC controls access to the Kubernetes API. It does not control whether one Pod can connect to another Pod over the network.

A developer may have no permission to get secrets, while their compromised application Pod can still connect to a database Service if network rules allow it. Different layer, different control.

NetworkPolicy is the common Kubernetes-native tool for Pod network restrictions, assuming the cluster CNI enforces it. A simple ingress policy might allow only Pods labeled app: api to reach Pods labeled app: database on port 5432:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-to-database
  namespace: demo
spec:
  podSelector:
    matchLabels:
      app: database
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: api
      ports:
        - protocol: TCP
          port: 5432

This policy is namespaced. The podSelector under from selects Pods in the same namespace unless you also use a namespaceSelector. That detail matters in real clusters.

NetworkPolicy is not the first thing I would teach before Pods and Services, but it belongs in the security basics because namespace separation without network thought is incomplete.

Workload defaults are security too

Security is not only who can use kubectl. It is also what a container is allowed to do when it runs.

A basic container security context can remove some unnecessary power:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: app
          image: nginx:1.27
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]

This may break images that expect to write to the root filesystem or need Linux capabilities. That is not a reason to ignore it. It is a reason to test and understand your image.

Cluster-level admission controls can enforce these patterns. On newer Kubernetes clusters, Pod Security Admission can apply restricted defaults by namespace labels. Managed platforms and OpenShift may add their own policies. Beginners do not need every detail on day one, but they should know that “the Pod starts” is not the same as “the Pod has a safe runtime profile.”

A practical beginner checklist

For a small team or learning cluster, I would start with these habits:

  1. Create namespaces intentionally. Do not put every tutorial and app in default.
  2. Use labels consistently for apps, components, and environments.
  3. Give each workload that needs API access its own ServiceAccount.
  4. Use Role and RoleBinding before ClusterRoleBinding.
  5. Test permissions with kubectl auth can-i.
  6. Avoid granting get secrets unless there is a clear need.
  7. Add ResourceQuotas and LimitRanges where shared environments can be exhausted.
  8. Learn whether NetworkPolicy is enforced in your cluster before relying on it.
  9. Review container securityContext defaults before production.

None of this makes a cluster perfectly secure. Perfect is not the beginner goal. The beginner goal is to stop making the cluster one flat room where every workload, user, and secret is closer than expected.

Namespaces give structure. RBAC gives permissions. Network and workload policies reduce blast radius. Together they turn Kubernetes from “everything can probably touch everything” into a system where access has to be named, reviewed, and tested.