Role-Based Access Control (RBAC)
Role-Based Access Control (RBAC) is a method of regulating access to resources based on the roles of individual users. In Kubernetes, RBAC is used to control who can access the Kubernetes API and what permissions they have.
How Kubernetes Uses RBAC
Kubernetes RBAC works through four main resources:
- Roles/ClusterRoles: Define permissions for a set of resources
- RoleBindings/ClusterRoleBindings: Bind roles to users, groups, or service accounts
- ServiceAccounts: Represent the identity of a workload in the cluster
- Resources: The Kubernetes objects that can be accessed (pods, services, etc.)
Operator RBAC Configuration
When you generate installation files for your operator (via the CLI or as part of the build), KubeOps automatically creates the necessary RBAC configurations for the operator's service account. These configurations define what resources and operations your operator is allowed to perform and are applied as part of the deployment.
Automatic RBAC Scope
KubeOps derives the generated RBAC scope from the existing operator configuration. A compile-time constant namespace
produces a namespaced Role and RoleBinding:
builder.Services.AddKubernetesOperator(settings =>
settings.Namespace = "tenant-a");
When Namespace is not configured, KubeOps generates the existing cluster-wide ClusterRole and
ClusterRoleBinding. No separate RBAC option is required.
The CLI performs static analysis and does not execute application startup code. Namespace values loaded from runtime
configuration or assigned through an external configuration method cannot be resolved safely. In those cases, the
CLI prints a warning and retains cluster-wide RBAC for compatibility. String literals, const string values, direct
property assignment, and WithNamespace are supported.
Statically resolved namespaces must be valid Kubernetes DNS-1123 labels: at most 63 lowercase alphanumeric characters
or -, starting and ending with an alphanumeric character. Invalid values produce a warning and retain cluster-wide
RBAC instead of generating invalid namespaced manifests.
The initial namespaced generation support deploys the operator into the watched namespace. If the CLI --namespace
option is supplied, it must match OperatorSettings.Namespace. Cluster-scoped entities and non-resource URL rules
cannot be granted by a namespaced Role and cause generation to fail. For string-based GenericRbacAttribute rules,
make sure every referenced resource is namespaced.
During local development, you typically use an admin account that has full cluster access. Therefore, RBAC restrictions don't apply, and you don't need to worry about permissions. However, it's still good practice to define the required RBAC rules for production use.
RBAC Verbs
KubeOps provides a set of RBAC verbs that can be used to define permissions:
| Verb | Description |
|---|---|
None | No permissions on the resource |
All | All possible permissions |
Get | Retrieve the resource from the API |
List | List resources on the API |
Watch | Watch for events on resources |
Create | Create new instances of the resource |
Update | Update existing resources |
Patch | Patch resources |
Delete | Delete resources on the API |
AllExplicit | All possible permissions (defined explicitly) |
RBAC Attributes
KubeOps provides two main attributes for defining RBAC rules:
EntityRbacAttribute
Use this attribute to define RBAC rules for specific entity types. It's typically used on controllers to specify what operations they need to perform on their managed entities.
[EntityRbac(typeof(V1DemoEntity), Verbs = RbacVerb.All)]
public class DemoController : IEntityController<V1DemoEntity>
{
// Controller implementation
}
GenericRbacAttribute
Use this attribute to define RBAC rules for any Kubernetes resource. It's useful when your operator needs to interact with built-in Kubernetes resources.
[GenericRbac(
Groups = new[] { "apps" },
Resources = new[] { "deployments" },
Verbs = RbacVerb.Get | RbacVerb.List | RbacVerb.Watch
)]
public class DemoController : IEntityController<V1DemoEntity>
{
// Controller implementation
}
Default RBAC Rules
KubeOps automatically adds default RBAC rules for:
-
Lease Resources: Required for leader election
[GenericRbac(Groups = new[] { "coordination.k8s.io" },Resources = new[] { "leases" },Verbs = RbacVerb.Get | RbacVerb.List | RbacVerb.Watch |RbacVerb.Create | RbacVerb.Update | RbacVerb.Patch)] -
Events: Required for creating Kubernetes events
[GenericRbac(Groups = new[] { "" },Resources = new[] { "events" },Verbs = RbacVerb.Create | RbacVerb.Patch | RbacVerb.Update)]
Best Practices
-
Principle of Least Privilege:
- Only grant the permissions your operator actually needs
- Use specific verbs instead of
Allwhen possible - Review and update RBAC rules when adding new features
-
Entity-Specific Rules:
- Use
EntityRbacAttributefor your custom resources - Define rules at the controller level
- Consider the operations each controller needs to perform
- Use
-
Generic Rules:
- Use
GenericRbacAttributefor built-in resources - Be specific about which resources and operations are needed
- Document why each rule is necessary
- Use
-
Testing:
- Test against a cluster using the generated service account, not your admin kubeconfig — locally everything works because admin bypasses RBAC (see the note above), so missing rules only surface in-cluster
- A quick check without deploying:
kubectl auth can-i watch <resource> --as=system:serviceaccount:<ns>:<operator-sa> - Watch the operator logs for
Forbiddenerrors after every RBAC change (see Troubleshooting — 403 Forbidden)
Common Pitfalls
-
Missing Permissions:
- Operator fails to perform required operations
- Watch operations don't work
- Leader election fails (the lease permissions are part of the default RBAC rules; see also Leader Election — Troubleshooting)
-
Excessive Permissions:
- Operator has more access than needed
- Security risks from broad permissions
- Hard to audit and maintain
- A dynamic namespace configuration cannot be resolved during manifest generation and therefore falls back to cluster-wide RBAC
-
Incorrect Resource Definitions:
- Wrong API groups
- Incorrect resource names
- Missing subresources