case-rbac

A read-only Kubernetes role: no Secrets, no exec, no changes

Designing a Kubernetes RBAC role that lets teams see their pods and logs but can't read Secrets, exec into containers or change anything.

  • Kubernetes
  • RBAC
  • Rancher
  • security

Teams should be able to see the state of their own applications: is the pod running, how many replicas are there, what do the logs say? But handing out cluster-admin or edit just for that also gives the same people the ability to read Secrets, get a shell inside containers and change the live environment.

Isn't the default view role enough?

Kubernetes' built-in view ClusterRole is a good starting point, and it doesn't allow reading Secrets. Still, writing your own role has two advantages: you can see at a glance exactly what you're allowing, and you can deliberately leave out resources nobody needs (e.g. ConfigMaps).

The role

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: team-readonly
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/log", "services", "endpoints", "events", "namespaces"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["apps"]
    resources: ["deployments", "replicasets", "statefulsets", "daemonsets"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["batch"]
    resources: ["jobs", "cronjobs"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["networking.k8s.io"]
    resources: ["ingresses"]
    verbs: ["get", "list", "watch"]

What's missing here matters as much as what's included:

  • No secrets. Application passwords and tokens stay invisible.
  • No pods/exec, pods/attach or pods/portforward. Nobody gets inside a container.
  • No create, update, patch or delete verbs. Nothing can be changed.

Binding

It's safer to bind the role in the team's namespace rather than cluster-wide:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: team-a-readonly
  namespace: team-a
subjects:
  - kind: Group
    name: team-a-developers
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: team-readonly
  apiGroup: rbac.authorization.k8s.io

If you use Rancher, you can define the same rules as a custom project role and assign it to the team at the project level. The logic is the same.

Verification

Don't guess permissions, test them. Use kubectl auth can-i and impersonate the user:

kubectl auth can-i list pods        -n team-a [email protected] --as-group=team-a-developers   # yes
kubectl auth can-i get pods/log     -n team-a [email protected] --as-group=team-a-developers   # yes
kubectl auth can-i get secrets      -n team-a [email protected] --as-group=team-a-developers   # no
kubectl auth can-i create pods/exec -n team-a [email protected] --as-group=team-a-developers   # no
kubectl auth can-i delete pods      -n team-a [email protected] --as-group=team-a-developers   # no

Lesson

Visibility and authority are not the same thing. Teams can see everything they need; Secrets, exec and changes remain a separate permission that is justified and logged.

Organisation and environment details have been removed; command and configuration examples are generalised. Test in your own environment first.

This note is the long form of one of the case files on this site. Drafted with Claude. Also available in Turkish.

ls ~/notes

More notes

All notes

Got an infrastructure question, a project or a route tip?

Email is the fastest way. If I'm on the road, I'll reply from the next Wi-Fi.

Send an email