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,listandwatch; - never
secrets; - no subresource but
status,scaleandpods/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), writesserviceaccounts, 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.