Skip to content

Roles

A permission grants a role on a cluster, over a namespace or the whole cluster. A role is either one of the six system roles, the tiers kubelatch ships (System roles), or a custom role an administrator composes in Roles. This page explains what a custom role can hold, what kubelatch always refuses and why. How to build, edit and grant one is in Manage roles.

"Role" here is what a permission grants. It has nothing to do with the administrator role, which lets a person manage kubelatch (Users and bots).

System roles and custom roles

System role Custom role
Defined by kubelatch: viewer, developer, debugger, secrets-reader, admin, cluster-admin An administrator, in Roles
Can change No. All but admin and cluster-admin can be duplicated into a custom role Yes, live: every permission that uses it changes when you save
Where it's granted Fixed per tier (Allowed scopes) Any namespace; the whole cluster only if the role says so; every cluster or only some
Expiry cluster-admin: 8 h at most No limit
Clusters Any registered cluster Clusters with the current bootstrap (below)

A custom role is global to the kubelatch instance. Its Identifier (2 to 39 lowercase letters, digits or hyphens, starting with a letter) is fixed once it's created and is never used again, not even after the role is deleted, so past permissions and the audit log always point to one role. It can't be the name of a system role.

A custom role is made of two things: capabilities, curated sets of rules, and advanced rules for what no capability covers. What it grants is the union of both.

Capabilities

A capability is a named set of rules from kubelatch's catalogue. Reading means get, list and watch; writing means create, update, patch and delete. The builder groups them by domain:

Domain Capability What it allows Marks Whole cluster
Workloads Read workloads (workloads.read) Read Deployments, StatefulSets, DaemonSets, ReplicaSets, Jobs, CronJobs, Pods and HorizontalPodAutoscalers. Allowed
Workloads Deploy workloads (workloads.deploy) Write Deployments, StatefulSets, DaemonSets, ReplicaSets, Jobs, CronJobs and Pods. Sensitive, needs PSA No
Workloads Scale (workloads.scale) get, update and patch on the scale subresource of Deployments, StatefulSets and ReplicaSets. No
Workloads Delete Pods (pods.delete) delete on Pods, so their controller starts them again. No
Debugging Read logs (pods.logs) get and list on Pods and pods/log. Allowed
Debugging Exec and attach (pods.exec) get and create on pods/exec and pods/attach; get and list on Pods. Sensitive No
Debugging Port-forward (pods.portforward) get and create on pods/portforward; get and list on Pods. Sensitive No
Debugging Ephemeral containers (pods.ephemeral) patch on pods/ephemeralcontainers; get and list on Pods. Sensitive, needs PSA No
Configuration and Secrets Read ConfigMaps (config.read) Read ConfigMaps. Allowed
Configuration and Secrets Write ConfigMaps (config.write) Write ConfigMaps. No
Configuration and Secrets Read Secrets (secrets.read) Read Secrets, ServiceAccount tokens included. Sensitive No
Configuration and Secrets Write Secrets (secrets.write) Write Secrets. Sensitive No
Network Read networking (network.read) Read Services, Endpoints, EndpointSlices, Ingresses and NetworkPolicies. Allowed
Network Write Services and Ingresses (network.write) Write Services and Ingresses. No
Storage Read volume claims (storage.read) Read PersistentVolumeClaims. Allowed
Storage Write volume claims (storage.write) Write PersistentVolumeClaims. No
Events and autoscaling Read events (events.read) Read Events, from both the core API and events.k8s.io. Allowed
Events and autoscaling Write autoscalers (autoscaling.write) Write HorizontalPodAutoscalers. No
Cluster-wide Read nodes (nodes.read) Read Nodes. Required
Cluster-wide Read namespaces (namespaces.read) Read Namespaces. Required
Cluster-wide Read volumes (volumes.read) Read PersistentVolumes and StorageClasses. Required
Cluster-wide Read CRDs (crds.read) Read CustomResourceDefinitions. Required

Whole cluster says whether the capability fits a role that can be granted to a whole cluster: Allowed, No (it writes, or reads Secrets or a subresource that acts), or Required (it reads resources that only exist cluster-wide). See Roles granted to a whole cluster.

exec, attach and port-forward take get as well as create because kubectl 1.31 and later tries a WebSocket (a GET) before SPDY (a POST).

There's no capability for custom resources: add them as advanced rules.

System roles as capabilities

Four system roles have an equivalent in capabilities, which Roles shows and Duplicate copies:

System role Capabilities
viewer Read workloads, Read logs, Read ConfigMaps, Read networking, Read volume claims, Read events, Read nodes, Read namespaces, Read volumes, Read CRDs; can be granted to a whole cluster
developer Everything in viewer except the four cluster-wide ones, plus Deploy workloads, Scale, Write ConfigMaps, Write Services and Ingresses, Write volume claims, Write autoscalers
debugger Read logs, Exec and attach, Port-forward, Ephemeral containers
secrets-reader Read Secrets

For viewer and developer it's an approximation: on each cluster they also aggregate Kubernetes' view role and any ClusterRole labelled for them (How it's materialized), and a copy inherits neither. admin and cluster-admin bind Kubernetes' own roles: they have no capabilities to show and can't be duplicated.

Advanced rules

An advanced rule is a Kubernetes RBAC rule you write yourself, for what no capability covers: a custom resource, or one object by name. Each rule has:

Field What goes in it
API group The group, for example argoproj.io. Empty for the core group (Pods, ConfigMaps…), which the builder offers as Core group.
Resources The resources, in plural and lowercase (applications), with a subresource after a slash where needed (deployments/scale).
Verbs Among get, list, watch, create, update, patch, delete and deletecollection.
Names (optional) Object names the rule is limited to (resourceNames in Kubernetes).

In the builder, resources and names are chips (type one and press Enter), the verbs are eight chips with Read and Write presets, and the group is typed or picked. A role holds up to 50 advanced rules, each with up to 20 names.

Suggest from cluster, in the builder, reads the groups, resources, subresources and verbs of a ready cluster from its discovery API and offers them in a rule's fields. Object names are always typed by hand: listing a cluster's objects would mean giving the reconciler read access to everything.

What names can't limit

Kubernetes only restricts get, update, patch and delete by name. It can't restrict list, watch, create or deletecollection: a list with a name in the rule would still list every object, and a create can't know a name before the object exists. kubelatch refuses a rule with names and any of those four verbs.

The consequence: a rule limited to the ConfigMap app-config lets its holder read and change that ConfigMap with kubectl get configmap app-config, but not list ConfigMaps, so kubectl get configmaps fails.

What is always refused

In capabilities and in advanced rules alike, kubelatch refuses:

Refused Why
The verbs impersonate, escalate and bind They step over RBAC: acting as someone else, or granting more than you have.
Any verb outside the eight above (approve, sign, use…) Each is a special power no custom role should carry.
The wildcard * in a group, a resource or a verb It covers whatever the cluster gains later, and no one can review it.
Any write to the rbac.authorization.k8s.io and admissionregistration.k8s.io groups Writing RBAC grants anything; writing admission configuration switches off the controls, Pod Security among them.
serviceaccounts/token, nodes/proxy, certificatesigningrequests/approval, certificatesigningrequests/status and signers, with any verb Shortcuts to someone else's identity or to the kubelet.
Any write to namespaces and its subresources A RoleBinding in namespace X lets its holder change the Namespace object X itself. Whoever removed its Pod Security label could then create a privileged pod and reach the node. Kubernetes' admin role doesn't allow it either.
Control and formatting characters in the name, the description and object names They make a name read as something else. A line break counts: the description is one line.
A role with no capability and no rule, or the same capability twice A role that grants nothing means nothing.

The builder shows each problem next to its field (a capability, a rule's field, the name, the clusters), and under To fix before saving. The API refuses the role with the first one (Errors).

Roles granted to a whole cluster

Every custom role can be granted on a namespace. With Namespaces and whole cluster under Where it applies, it can also be granted with scope *, and then it must be read-only:

  • only the verbs get, list and watch;
  • never secrets;
  • no subresource but status, scale and pods/log.

The reason is that Kubernetes has no "every namespace but these": a binding with scope * also reaches kube-system and kubelatch-system, where the Secret with the reconciler's token lives. Reading Secrets there exposes that token. Writing there (EKS's aws-auth ConfigMap, CoreDNS's, a pod in kube-system) is a way to cluster-admin that skips the audited emergency access. On many subresources a get already acts: exec, attach, portforward, proxy and ephemeralcontainers, but also nodes/log or a KubeVirt virtual machine's console. They can't all be listed, so the list is closed on the allowed side. To write across a whole cluster there is cluster-admin, with its 8 h.

The cluster-wide capabilities (Read nodes, Read namespaces, Read volumes, Read CRDs) need Namespaces and whole cluster. Granted on a namespace, they grant nothing: a RoleBinding only reaches what lives in its namespace. A role that can be granted to a whole cluster is always Sensitive.

Limited to some clusters

Under Clusters, in Where it applies, a role is for Every cluster or Only some…, up to 100. A cluster that isn't registered yet can be on the list. Granting the role on any other cluster is refused, and an edit can't take a cluster off the list while the role has active permissions there.

Risk and Pod Security

Both are derived from what the role holds; nobody sets them by hand.

A role is Sensitive when:

  • it can be granted to a whole cluster;
  • it has a sensitive capability: Deploy workloads, Exec and attach, Port-forward, Ephemeral containers, Read Secrets or Write Secrets;
  • or an advanced rule touches secrets, a subresource that acts (exec, attach, portforward, proxy, ephemeralcontainers), writes serviceaccounts, needs Pod Security (below), or names an API group outside Kubernetes (any group with a dot that doesn't end in .k8s.io).

The lists show a sensitive role in semibold, with the Sensitive pill in Roles; the builder says why under Look twice.

A role requires Pod Security when it has Deploy workloads or Ephemeral containers, or an advanced rule that creates or changes (create, update, patch) Pods, ReplicationControllers, Deployments, StatefulSets, DaemonSets, ReplicaSets, Jobs or CronJobs, or that touches pods/ephemeralcontainers. Granting it on a namespace then requires the pod-security.kubernetes.io/enforce label set to baseline or restricted, as for developer (Pod Security Admission).

That label is checked once, when granting. So an edit can't make a role start requiring Pod Security while it's granted on namespaces: revoke those permissions and grant them again, and each namespace is checked.

The namespace is still the boundary: a role that deploys pods reaches the namespace's Secrets and ServiceAccounts by mounting them, even without Read Secrets (The namespace is the boundary).

What kubelatch can't know: custom resources

kubelatch doesn't know what a custom resource does. A role allowed to create an Argo Workflow runs code without touching a single pod: the controller creates them. That's why any rule on a group outside Kubernetes marks the role Sensitive, but kubelatch can't tell whether it needs Pod Security. Grant roles with such rules only on namespaces that enforce PSA, as you would developer, and read the controller's documentation for what each resource lets its author do.

Limits

Limit Value Why
Custom roles per instance 200 Every role in use is work for each reconciliation.
Advanced rules per role 50 A ClusterRole is one object in etcd, and a role nobody can read at a glance is a role nobody reviews.
Names per advanced rule 20 The same, and the summary stops being readable.
Clusters in Only some… 100 —
Name 80 characters —

They are fixed in kubelatch, not configurable.

On the cluster

For each custom role with at least one active permission on a cluster, the reconciler writes:

Object Name Binding subject
ClusterRole with the role's rules kubelatch-role-<id>
RoleBinding, for scope namespace kubelatch-role-<id> in that namespace Group kubelatch:ns:<namespace>:role-<id>
ClusterRoleBinding, for scope * kubelatch-role-<id>-cluster Group kubelatch:cluster:role-<id>

The ClusterRole carries the labels kubelatch.io/kind=role and kubelatch.io/role=<id> and the annotation kubelatch.io/revision. It isn't aggregated: unlike viewer, a custom role doesn't pick up labelled ClusterRoles, so add a CRD with an advanced rule. The YAML tab in the builder shows exactly the ClusterRole kubelatch writes.

Saving a role reconciles every cluster where it has active permissions: its holders get the new rules within seconds, with the same credential. When the reconciler stops needing a role on a cluster, it deletes its ClusterRole and bindings.

If a role stops passing these rules (a later release refuses something it holds), kubelatch stops applying it: its permissions take no effect and the cluster goes to Error with custom role <id> no longer passes validation and was not applied: edit or delete it.

The cluster needs the current bootstrap

Custom roles need the current bootstrap manifest on the cluster, which bounds the reconciler with an admission policy instead of a fixed list of names (The reconciler and its ServiceAccount), and Kubernetes 1.30 or later. A cluster registered with the previous manifest keeps working with the system roles; its card in Clusters says Bootstrap v1: custom roles cannot be granted on this cluster., and the grant form disables custom roles there. Update the bootstrap says how to move it.