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.
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/attachorpods/portforward. Nobody gets inside a container. - No
create,update,patchordeleteverbs. 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.