Skip to main content

Tenant RBAC

Cosmonic Control is a Kubernetes-native platform, so multi-tenant access control uses standard Kubernetes Role and RoleBinding objects. This page covers how to set up namespace-scoped RBAC for tenant teams, what the Cosmonic Control operator requires cluster-wide, and how to restrict team access to their own namespaces.

How it works

Cosmonic Control installs one ClusterRole that the operator service account uses to manage CRDs and read core resources across all namespaces. Tenant teams are separate from the operator — they are given narrower, namespace-scoped Role objects that only permit them to manage their own workloads.

Cluster level:
  operator ServiceAccount  →  ClusterRole (cosmonic-control)
                               manages all CRDs, reads ConfigMaps/Secrets/Namespaces

Namespace level (per team):
  team-a ServiceAccount/User  →  Role (in namespace team-a)
                                  manage HTTPTrigger, Artifact

Operator ClusterRole

The operator's ClusterRole is installed by the Helm chart and grants the following permissions:

API GroupResourcesVerbs
control.cosmonic.iohttptriggers, projectenvironmentsfull CRUD
control.cosmonic.iohttptriggers/status, projectenvironments/statusget, update, patch
runtime.wasmcloud.devartifacts, hosts, workloads, workloadreplicasets, workloaddeploymentsfull CRUD
runtime.wasmcloud.dev*/statusget, update, patch
"" (core)configmaps, secrets, namespacesget, list, watch
"" (core)eventsget, list, watch, create, update, patch
coordination.k8s.ioleasesfull CRUD (leader election)

The operator reads configmaps and secrets cluster-wide to deliver configuration to components (see Component Configuration). This means Secrets in any namespace are readable by the operator service account. Use namespace-level RBAC to control which humans and CI systems can create or read Secrets in each namespace.

Setting up a tenant namespace

The typical pattern for onboarding a team:

  1. Create a namespace for the team
  2. Create a Role granting them access to Cosmonic Control resources in that namespace
  3. Create a RoleBinding linking the role to the team's user, group, or service account

1. Create the namespace

kubectl create namespace team-a

2. Create a Role

A standard developer role grants full access to HTTPTrigger and Artifact resources — the two resources developers interact with directly. Read access to the managed resources (WorkloadDeployment, Workload, etc.) is useful for debugging.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: cosmonic-developer
  namespace: team-a
rules:
  # Create and manage workload entrypoints
  - apiGroups: ["control.cosmonic.io"]
    resources: ["httptriggers"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  - apiGroups: ["control.cosmonic.io"]
    resources: ["httptriggers/status"]
    verbs: ["get", "list", "watch"]

  # Manage artifact pre-fetch (enterprise)
  - apiGroups: ["runtime.wasmcloud.dev"]
    resources: ["artifacts"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

  # Read-only access to managed resources for debugging
  - apiGroups: ["runtime.wasmcloud.dev"]
    resources:
      - workloaddeployments
      - workloadreplicasets
      - workloads
      - hosts
    verbs: ["get", "list", "watch"]

  # Manage own ConfigMaps and Secrets for component config
  - apiGroups: [""]
    resources: ["configmaps", "secrets"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
kubectl apply -f cosmonic-developer-role.yaml

3. Create a RoleBinding

Bind the role to a user, group, or service account:

# Bind to a specific user
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: team-a-developers
  namespace: team-a
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: cosmonic-developer
subjects:
  - kind: User
    name: alice@corp.com
    apiGroup: rbac.authorization.k8s.io
  - kind: User
    name: bob@corp.com
    apiGroup: rbac.authorization.k8s.io

For CI/CD pipelines, bind to a service account instead:

subjects:
  - kind: ServiceAccount
    name: team-a-ci
    namespace: team-a
kubectl apply -f team-a-rolebinding.yaml

Read-only role

For users who need visibility without write access (e.g., on-call engineers, security auditors):

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: cosmonic-viewer
  namespace: team-a
rules:
  - apiGroups:
      - control.cosmonic.io
      - runtime.wasmcloud.dev
    resources: ["*"]
    verbs: ["get", "list", "watch"]

Verifying access

Test that a user or service account has the expected permissions with kubectl auth can-i:

# Can alice create HTTPTriggers in team-a?
kubectl auth can-i create httptriggers.control.cosmonic.io -n team-a --as alice@corp.com

# Can alice read Secrets in team-b? (should be no)
kubectl auth can-i get secrets -n team-b --as alice@corp.com

# List all permissions a user has in a namespace
kubectl auth can-i --list -n team-a --as alice@corp.com

Isolation guarantees

Namespace-scoped RBAC controls who can manage Cosmonic Control resources. The isolation between running workloads is enforced by the WebAssembly sandbox — components running in different namespaces cannot communicate unless explicitly linked, and a component cannot access host resources (filesystem, network, environment) outside of what is declared in its localResources and allowedHosts manifest fields.

This means a compromised component cannot escalate to read another namespace's Secrets or ConfigMaps through the Wasm interface — the component model itself enforces the boundary. The RBAC layer ensures that the teams managing the manifests are also properly separated.