Skip to content
Stackship documentation Svenska

Access controlAdministrators

Kubernetes RBAC

How roles and assignments are written into each cluster's Kubernetes RBAC, and why a release can need a cluster administrator once.

Access control has two enforcement paths. Calls to the platform API are decided by the RBAC service. Calls straight to a cluster's Kubernetes API server are decided by Kubernetes' own RBAC, which the platform writes from the same roles and assignments — so that a person or service principal signing in to the API server with their platform identity gets rights that match their roles.

What a role becomes

Each action in the platform's catalogue can carry Kubernetes rules; kernel/k8sChannel/read, for example, carries get, list and watch on the platform's own resources. A role's Kubernetes rights are the rules of the control-plane actions it grants, minus those of its NotActions; data actions are not written into Kubernetes. Built-in roles can add rules of their own on top: Owner, Contributor, Reader and the three platform roles carry rules on pods, deployments, services, jobs and similar core resources.

  • Built-in roles become ClusterRoles, written by the platform.
  • Custom roles become namespaced Roles, only in the namespaces where they are assigned.

Caution

Some of these rules give more in Kubernetes than the role gives in the portal:

  • A shell in every pod the binding covers (pods/exec): Owner, Contributor, Platform Owner, Platform Contributor, Apps Operator and Blueprints Operator. Apps Operator and Blueprints Operator carry it because their file browsing and terminals reach the pod through it.
  • Secret values (secrets), which Kubernetes returns to anyone who may get a Secret. Platform Owner and Platform Contributor may read and change every Secret in every namespace, the platform's own and kube-system included. Any role whose actions include kernel/k8sChannel/readSecretMetadata — directly or through a wildcard such as */* or kernel/* — may get, list and watch Secrets in the namespaces it is bound in; Owner and Contributor are such roles.

Treat an assignment of these roles at the root, a boundary or a resource group as including those rights.

What an assignment becomes

Scope of the assignment Kubernetes binding
/ A ClusterRoleBinding — the role applies in every namespace of every cluster
A boundary A RoleBinding in each of the boundary's namespaces
A resource group A RoleBinding in the resource group's namespace
A resource None — resource scope exists only in the platform API

An active just-in-time grant is written like an assignment and removed when it expires or is revoked. Custom roles are never bound at /: a root binding applies in every namespace, including the platform's own and kube-system, so it is limited to the built-in roles.

Users and service principals appear to Kubernetes as the user oidc: followed by their principal ID, and groups as a Kubernetes group named with the group's ID. The prefix is the module setting Rbac__Reconciler__OidcUsernamePrefix and must match the API server's username prefix. The installer writes what an API server needs to accept platform sign-ins into the ConfigMap stackship-kubeapi-oidc; putting it on the control-plane nodes is up to you:

bash
kubectl -n stackship-system get configmap stackship-kubeapi-oidc -o yaml

Deny assignments

Kubernetes RBAC cannot deny. A deny assignment can therefore only leave a role's binding out of a cluster, and it does so only when all of these hold:

  • the deny names the principal the binding is for. Groups are not expanded: a deny on a user leaves the bindings the user has through a group in place;
  • the deny applies at the binding's scope;
  • the deny covers every action and every data action the role grants. A role's wildcard is covered only by the same or a wider one, so Owner's */* needs */* in both the deny's actions and its data actions.

Anything less is enforced by the platform API but not by Kubernetes: the principal keeps the Kubernetes rights of the role. Active just-in-time grants are written to Kubernetes whatever denies apply to them.

Caution

A deny created in the portal has control-plane actions only, so it never removes the binding of a role that has data actions — Owner, Contributor, Reader, Platform Owner, Platform Contributor, Apps Operator and Blueprints Operator among them. Someone denied everything in the portal still has these roles' full Kubernetes rights, including the shells and Secret reads above. To take them away in Kubernetes, remove the role assignment — or create the deny through the API or the CLI with data actions that cover the role's as well, naming the principal each binding is for.

The permission ceiling

The platform writes these objects as a dedicated identity, the ServiceAccount stackship-iam-projector in stackship-system, bound to a ClusterRole of the same name — its ceiling: every rule the catalogue can express. Kubernetes only lets an identity write a role containing rules it holds itself, so the ceiling is what the platform can ever grant, and the platform cannot widen its own ceiling.

When a release adds rules on a resource the ceiling does not cover yet, a cluster administrator has to widen it once:

  1. Open Lifecycle Manager and its After upgrading tab. A cluster in the state Needs a cluster administrator shows the rules it is missing and the commands to run.
  2. Run those commands once with a kubeconfig that holds cluster-admin.
  3. Choose Apply to all clusters again. It runs with your own credentials and is recorded against your account; applying again is safe.

View the manifest downloads the full set of objects a cluster needs, for review or to apply to a new cluster by hand. Reading it needs rbac/projector/read and applying it rbac/projector/apply, both at the root.