Skip to content

Support runtime.wasmcloud.dev apiGroup RBAC to be namespaced vs Cluster#5208

Merged
ricochet merged 2 commits into
wasmCloud:mainfrom
jfleitz:jfleitz/finalizer-namespaced
May 27, 2026
Merged

Support runtime.wasmcloud.dev apiGroup RBAC to be namespaced vs Cluster#5208
ricochet merged 2 commits into
wasmCloud:mainfrom
jfleitz:jfleitz/finalizer-namespaced

Conversation

@jfleitz

@jfleitz jfleitz commented May 27, 2026

Copy link
Copy Markdown
Contributor

Support runtime.wasmcloud.dev RBAC as Namespaced vs Cluster

Summary

When the operator is configured to watch a fixed set of namespaces
(operator.watchNamespaces), it no longer needs — and should not be granted —
cluster-wide access to the runtime.wasmcloud.dev apiGroup resources. This PR
makes the operator's workload-side RBAC follow the watch scope: cluster-wide
when watching all namespaces, namespaced when watchNamespaces is set.

Concretely, when operator.watchNamespaces is non-empty, access to
artifacts, workloads, workloadreplicasets, and workloaddeployments
(plus their /status and /finalizers subresources) is granted via a Role +
RoleBinding in each watched namespace
instead of a cluster-wide ClusterRole.
The shared core resources (configmaps/secrets/services/events) and
endpointslices are converted to the same per-namespace pattern.

Motivation

Previously the main operator ClusterRole always granted the
runtime.wasmcloud.dev CRD verbs cluster-wide, regardless of watchNamespaces.
For a watchNamespaces install that is broader than the operator actually uses:
the workload-side informer cache is already scoped to the watched namespaces via
cacheOpts.DefaultNamespaces (runtime-operator/cmd/main.go), so the
controllers only ever read/write these resources within those namespaces.

Granting them per-namespace:

  • follows least privilege — the operator's ServiceAccount only holds permissions
    in the namespaces it manages;
  • lets a namespace-scoped install be performed with namespace-admin RBAC for the
    workload resources rather than cluster-admin.

What changed

All changes are in the runtime-operator Helm chart; no operator code or
generated manifests changed.

Template Before After (watchNamespaces set)
clusterrole.yaml always rendered; CRD rules cluster-wide gated on not .Values.operator.watchNamespaces — omitted entirely in scoped mode
clusterrolebinding.yaml always rendered gated on not .Values.operator.watchNamespaces — omitted in scoped mode
workload-crd-role.yaml (new) Role + RoleBinding per watched namespace for artifacts/workloads/workloadreplicasets/workloaddeployments + /status + /finalizers
workload-namespace-role.yaml shared ClusterRole + per-ns RoleBinding Role + RoleBinding per watched namespace (configmaps/secrets/services/events + services/finalizers)
endpointslice-role.yaml shared ClusterRole + per-ns RoleBinding Role + RoleBinding per watched namespace (endpointslices)

Resulting RBAC

watchNamespaces: [] (watch all — default, unchanged behavior):

  • one cluster-wide ClusterRole + ClusterRoleBinding carrying the
    runtime.wasmcloud.dev CRD rules and the cluster-wide core/discovery rules.

watchNamespaces: [team-a, team-b]:

  • no operator ClusterRole/ClusterRoleBinding;
  • per watched namespace: …-workload-crd, …-workload-namespace,
    …-endpointslice Role + RoleBinding;
  • …-leader-election Role remains in the operator's own namespace.

Host CRDs are unaffected — they always live in the operator's own namespace and
continue to be granted by the namespaced Role in role.yaml.

Why it's safe

The workload-side informer cache is scoped to watchNamespaces via
cacheOpts.DefaultNamespaces, so the controllers never list/watch these
resources cluster-wide. Namespaced RBAC is therefore sufficient for all
list/watch/CRUD the controllers perform, including stamping owner references with
blockOwnerDeletion=true (the GC admission plugin's update on
<owner>/finalizers check is satisfied within each namespace).

No RBAC drift

This is a chart-only change that relocates where permissions are bound; it does
not add or remove any permission from the set declared by the +kubebuilder:rbac
markers. runtime-operator/config/rbac/role.yaml (generated by controller-gen)
is unchanged, so the make manifests drift guard continues to pass. The
per-namespace scoping is a Helm-only concern that controller-gen cannot
express, so it intentionally lives only in the chart.

Testing

Verified all with a local Kind cluster install:

  • helm lint passes.
  • helm template rendered with watchNamespaces empty and with
    watchNamespaces={team-a,team-b}, verifying:
    • watch-all: operator ClusterRole/ClusterRoleBinding present with CRD rules;
    • scoped: operator ClusterRole/ClusterRoleBinding absent; Role +
      RoleBinding present in each watched namespace with the full CRD rules
      (incl. /status and /finalizers).
  • Deployed Helm chart to cluster with cluster-wide permissions
  • Deployed Helm chart with Host specific namespaces

Out of scope / follow-ups

  • Gateway (gateway.enabled, default true) (deprecated) grants cluster-wide
    hosts/workloads; could be converted to per-namespace Roles in a follow-up.
  • Metrics (metrics-auth/metrics-reader) use tokenreviews,
    subjectaccessreviews, and nonResourceURLs — inherently cluster-scoped APIs
    that cannot be namespaced; they could only be gated behind a values flag.

Signed-off-by: Jeremy Fleitz jeremy@cosmonic.com

Signed-off-by: Jeremy Fleitz <jeremy@cosmonic.com>
@jfleitz jfleitz requested review from a team as code owners May 27, 2026 18:57
@jfleitz jfleitz changed the title Support wasmCloud apiGroup RBAC to be namespaced vs Cluster Support runtime.wasmcloud.dev apiGroup RBAC to be namespaced vs Cluster May 27, 2026
@ricochet

Copy link
Copy Markdown
Contributor

We'll want to call out this change in release notes. Chart upgrade in scoped mode replaces cluster-scoped …-workload-namespace and …-endpointslice ClusterRoles with same-named namespaced Roles. Helm should just handle this but plenty of operators don't use helm for the install. Upgrade note should include that operators should verify no orphaned ClusterRoles remain post-upgrade

@ricochet ricochet merged commit a71dca9 into wasmCloud:main May 27, 2026
58 checks passed
@jfleitz jfleitz deleted the jfleitz/finalizer-namespaced branch May 27, 2026 22:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants