Kubernetes basics · Lesson 10 of 12 · about 25 minutes

Namespaces and access control

Namespaces split one cluster into separate spaces. Access control decides who can do what in each one. Together they keep apps, teams and tools from stepping on each other.

By the end you'll know

  • What a namespace is, and how to work in one
  • Why the same name can exist twice in one cluster
  • How Kubernetes decides who is allowed to do what
  • How to give an app exactly the access it needs, and prove it

Namespaces: folders for your cluster

A cluster can run many apps, for many teams. If everything sat in one big list, names would clash, and anyone could change anything. A namespace is a folder inside the cluster that groups related things together.

You've been using one all along. Everything you created so far went into the namespace called default, and in Lesson 2 you saw the control plane's pods in kube-system. See them all:

BASH
kubectl get namespaces
TEXT
NAME                 STATUS   AGE
default              Active   5m
kube-node-lease      Active   5m
kube-public          Active   5m
kube-system          Active   5m
local-path-storage   Active   5m

Two things to know about namespaces:

  • Names only have to be unique inside a namespace. Two teams can each have a Deployment called web, as long as they're in different namespaces.
  • They're a boundary for access, not a wall for traffic. Pods in different namespaces can still talk to each other by default. Namespaces are about organizing and permissions. (Locking down traffic between them is a topic for later.)
Think of an office building. Namespaces are the floors. Each team has its own floor, two teams can both have a room called "Meeting Room A", and your keycard decides which floors you can get into. You can still phone someone on another floor.

Try it: work in namespaces

Start a cluster and create two namespaces:

BASH
kind create cluster --name learn
kubectl create namespace shop
kubectl create namespace blog

Now create a Deployment with the same name in each one. -n (short for --namespace) says which namespace a command acts on:

BASH
kubectl create deployment web --image=nginx:1.28 -n shop
kubectl create deployment web --image=nginx:1.28 -n blog

Both work, because each web lives in its own namespace. Look at them:

BASH
kubectl get deployments -n shop
kubectl get deployments -A

-A (short for --all-namespaces) shows everything, with a NAMESPACE column so you can tell them apart.

Now try without -n:

BASH
kubectl get deployments

No resources found in default namespace. Without -n, kubectl uses default. This catches everyone at least once: the thing you're looking for exists, just not where you're looking. If you'll be working in one namespace for a while, you can change the default:

BASH
kubectl config set-context --current --namespace=shop

Switch back with --namespace=default when you're done.

Remember the DNS names from Lesson 8? This is where the namespace part comes in. From a pod in blog, the short name web finds the blog Service, and web.shop reaches the one in shop.

Be careful deleting a namespace

kubectl delete namespace shop deletes the namespace and everything inside it: every Deployment, pod, Service and Secret, with no "are you sure?". On a practice cluster that's a handy way to clean up. On a real one, double-check the name and your current context first.

Access control: who can do what

Namespaces organize things. Access control decides who may touch them. Kubernetes uses a system called RBAC (role-based access control), built from three pieces:

  • Who: a user, or a ServiceAccount. A ServiceAccount is an identity for an app rather than a person, like a dashboard or a deploy tool that talks to the cluster.
  • What they may do: a Role, a list of allowed actions, such as "get and list pods". Nothing is allowed unless a Role says so.
  • The link: a RoleBinding, which gives a Role to a who, inside one namespace.
How access control worksInside the shop namespace, a RoleBinding links the ServiceAccount viewer to the Role pod-reader, which allows getting, listing and watching pods. So viewer can read pods in shop. It can't delete them, because the Role doesn't allow that, and it can't read pods in the blog namespace, because the RoleBinding only applies in shop.Namespace: shopWhoServiceAccountviewerThe linkRoleBindingviewer-reads-podsWhat it may doRole: pod-readerget, list, watch podsread pods in shopalloweddelete pods in shopdenied: the Role doesn't allow itThe binding lives in shop, so the access only applies in shop.Namespace: blogread pods in blogdenied: no binding hereNothing is allowed unless a Role says so, and a RoleBinding gives it to someone, in one namespace.
A RoleBinding connects a who to a Role, inside one namespace. Anything the Role doesn't list, and anywhere the binding doesn't reach, is denied.

There's also a cluster-wide version of each: a ClusterRole and a ClusterRoleBinding work across every namespace, for things like nodes that don't belong to any namespace. They follow the same idea.

Try it: give an app read-only access

Create a ServiceAccount called viewer in shop:

BASH
kubectl create serviceaccount viewer -n shop

Before giving it anything, ask Kubernetes what it's allowed to do. kubectl auth can-i answers yes or no, and --as asks on behalf of someone else:

BASH
kubectl auth can-i list pods -n shop --as=system:serviceaccount:shop:viewer

no. A new ServiceAccount starts with no access at all. Now save this as access.yaml. It's two objects in one file, split by --- (Lesson 4):

YAML
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: shop
rules:
  - apiGroups: [""]                    # "" means the core group, where pods live
    resources: ["pods"]
    verbs: ["get", "list", "watch"]    # read only: no create, update or delete
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: viewer-reads-pods
  namespace: shop
subjects:                              # who gets the access
  - kind: ServiceAccount
    name: viewer
    namespace: shop
roleRef:                               # which Role they get
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Apply it, and ask again, three ways:

BASH
kubectl apply -f access.yaml
kubectl auth can-i list pods -n shop --as=system:serviceaccount:shop:viewer
kubectl auth can-i delete pods -n shop --as=system:serviceaccount:shop:viewer
kubectl auth can-i list pods -n blog --as=system:serviceaccount:shop:viewer

yes, no, no. It can read pods in shop, it can't delete them, and it has no access in blog. That's exactly what we asked for, and you proved it without running the app at all.

In my real cluster

My dashboard, PodYard, runs on my home server and shows every pod in my production cluster. It logs in with its own ServiceAccount, podyard, whose ClusterRole can only get, list and watch the things the dashboard displays (pods, nodes, events, Deployments and Argo CD's app status) and read pod logs. Nothing can be created, changed or deleted, and Secrets aren't readable at all. Before PodYard ever ran, I checked it with kubectl auth can-i: yes to listing pods, no to deleting them, no to reading Secrets. If the dashboard, or my home server, were ever compromised, the most anyone could do with it is look.

My production cluster also uses namespaces the way this lesson describes: civicgrid for the app, monitoring for Prometheus and Alertmanager, argocd for deployments, cloudflared for the tunnel, and so on. Each tool keeps to its own floor.

Break it on purpose

Change the RoleBinding's namespace to blog, in both metadata.namespace and the subjects entry, and apply it again. Then ask:

BASH
kubectl auth can-i list pods -n blog --as=system:serviceaccount:shop:viewer

Still no. The binding is now in blog, but it points at a Role called pod-reader in blog, which doesn't exist. A RoleBinding can only use a Role from its own namespace. When access doesn't work, check these three things in order: is the binding in the right namespace, does it name the right Role, and does it name the right who. Change it back to shop when you're done.

Clean up

BASH
kind delete cluster --name learn

Check yourself

  1. Two teams each want a Deployment called web. What makes that possible?

  2. You run kubectl get pods and see nothing, but you know the pods exist. What's the most likely reason?

  3. What does kubectl delete namespace shop do?

  4. A brand-new ServiceAccount tries to list pods. What happens?

  5. Which command checks what a ServiceAccount is allowed to do, without running anything as it?

  6. A dashboard only needs to show pods. What's the safest access to give it?

Key terms

  • Namespace: a folder inside a cluster that groups related things. Names only have to be unique within one.
  • RBAC: role-based access control, the system Kubernetes uses to decide who can do what.
  • ServiceAccount: an identity for an app or tool, rather than a person.
  • Role: a list of allowed actions, inside one namespace. A ClusterRole is the cluster-wide version.
  • RoleBinding: gives a Role to a user or ServiceAccount, inside one namespace. A ClusterRoleBinding does it cluster-wide.
  • kubectl auth can-i: asks Kubernetes whether an action is allowed, for you or, with --as, for someone else.

Go deeper