This post is for platform teams running multi-tenant Kubernetes on GCP, where several teams share one cluster. RBAC is the isolation control you should design first, because it protects all the others. Below is a default binding pattern and a list of grants to audit. The short version is to scope tenant access to namespaces with RoleBindings that point at a small set of shared ClusterRoles. Then check for escalation paths, not just for who can read what.
A scope note: this post covers upstream Kubernetes RBAC only. How it interacts with your cloud provider’s identity layer is a separate topic.
Why RBAC comes first
Kubernetes has no first-class concept of a tenant. You build tenancy out of namespaces, quotas, network policies and RBAC. The same documentation calls authorization the most important type of control-plane isolation. If tenants can modify each other’s API resources, they can change or disable every other policy you’ve set. A network policy is only as strong as the rule that stops a tenant from deleting it.
The documentation also makes namespaces the natural unit for this, because RBAC Roles are namespace-scoped. It describes a common practice of giving every workload its own namespace, even when one team operates several workloads. I’d follow that. It gives each workload its own identity and lets you configure an appropriate security policy for it.
The default pattern
The RBAC reference gives you the building blocks:
- Role: a set of permissions that always lives in one namespace.
- ClusterRole: the same kind of permission set, but not namespaced.
- RoleBinding: grants a Role or ClusterRole to subjects within a single namespace.
- ClusterRoleBinding: grants a ClusterRole across the whole cluster.
The key detail is that a RoleBinding can reference a ClusterRole and grant it only inside the binding’s own namespace. You can define your tenant roles once, as ClusterRoles, and bind them namespace by namespace. I’d keep that set small, for example one role for tenants who deploy and one for tenants who only read. Then every review asks the same question: which group is bound to which standard role in which namespace.
Two properties of RBAC shape this design. First, permissions are purely additive, with no deny rules. You can’t grant broad access and then carve out an exception, so start narrow and add. Second, a binding’s roleRef can’t be changed after creation. To switch the role a binding grants, you delete the binding and create a new one, which forces you to check its subject list again.
The RBAC good practices page supports the rest of the pattern. Use RoleBindings rather than ClusterRoleBindings where possible. Avoid wildcard permissions. Kubernetes is extensible, so a wildcard also covers object types created in the future. I’d reserve ClusterRoleBindings for the platform team and the cluster’s own components, and require a justification for any other one.
Grants that are bigger than they look
I’d look first at grants whose effects aren’t obvious. The good practices page lists several.
- Listing secrets. List and watch on Secrets reveal their contents, just as get does.
- Creating workloads. Permission to create Pods, or resources that manage Pods, implicitly grants access to the Secrets, ConfigMaps and PersistentVolumes in that namespace. It also grants the API access of any service account there, because a Pod can run as any of them.
- Creating PersistentVolumes. This includes hostPath volumes, which give a Pod access to the node’s filesystem.
- nodes/proxy. Get on this subresource is not read-only. It reaches kubelet APIs that can execute commands in pods, and it bypasses audit logging and admission control.
- system:masters. Members of this group bypass RBAC entirely, and removing bindings won’t revoke their access.
Workload creation matters most for tenancy. A role that lets a team deploy also lets that team mount the namespace’s Secrets, ConfigMaps and PersistentVolumes into Pods, and act with the API permissions of any service account in that namespace. The documentation concludes that boundaries within a namespace should be considered weak, so never put two levels of trust in one namespace. Where you don’t fully trust a team to create safe pods, the docs say to enforce the Baseline or Restricted Pod Security Standard. They also suggest setting automountServiceAccountToken: false so pods don’t receive tokens they don’t need.
Finally, review your RBAC on a schedule. The same page warns that someone who creates a user with the same name as a deleted user can inherit that user’s rights. That makes stale bindings a real risk, not just untidiness.
When this isn’t enough
Everything above assumes tenants who share some trust, like internal teams. The multi-tenancy documentation also describes “hard” multi-tenancy, where tenants don’t trust each other. In that case the data plane needs extra attention because its attack surface is much larger, and sometimes the right answer is a dedicated cluster per tenant. RBAC is still necessary there, but not sufficient. The multi-customer case is also different. There, customers never touch the API, so your RBAC design is mostly about your own automation’s service accounts.
If you run a shared cluster today, I’d start with an inventory. List every ClusterRoleBinding and every member of system:masters, and remove what you can’t justify. Next, move tenants onto per-namespace RoleBindings to a small set of shared ClusterRoles. Last, check each tenant role for secrets access, workload creation, PersistentVolume creation and nodes/proxy, and make a deliberate decision about each one you grant.
