[Feature]: Scope Secret RBAC to namespace level instead of cluster-wide

Author: stefanpeknikCreated Sep 16, 2026Updated Sep 16, 2026

Is there an existing issue already for this feature request/idea?

  • I have searched for an existing issue, and could not find anything. I believe this is a new feature request to be evaluated.

What problem is this feature going to solve? Why should it be added?

The operator's ClusterRole (manager-role) grants cluster-wide create, delete, get, list, patch, update, watch on all Secrets. In a multi-tenant environment, a compromised operator pod gets read access to every secret in the cluster, including secrets from other tenants that have nothing to do with PostgreSQL.

The operator never accesses secrets outside two scopes:

  1. The namespace of the Cluster CR being reconciled (passwords, TLS certs, pooler secrets, DatabaseRole secrets)
  2. The operator's own namespace (CA/webhook PKI, monitoring query templates, pull secret cloning)

Every client.Get/client.List call for secrets uses client.InNamespace(...) scoped to one of these two namespaces. The broad ClusterRole exists because kubebuilder's +kubebuilder:rbac markers generate a single monolithic ClusterRole. It is not a functional requirement.

The same applies to plugin-barman-cloud. It only reads credential secrets in the ObjectStore CR's namespace and already creates namespace-scoped Roles with resourceNames restrictions for instance managers, yet its own service account still gets a cluster-wide ClusterRole.

Describe the solution you'd like

Remove secrets from the ClusterRole and create namespace-scoped Roles instead. A Role granting secret access gets created in each watched namespace and in the operator namespace. The ClusterRole keeps only resources that genuinely need cluster scope (nodes, webhooks, CRDs, ClusterImageCatalogs, etc.).

The code already scopes all secret access by namespace, so this aligns the RBAC with how the operator actually works. It is not a behavioral change.

Describe alternatives you've considered

We tried to work around this for the barman plugin by replacing the broad ClusterRole with a narrower one using resourceNames:

yaml
- apiGroups: [""]
  resources: [secrets]
  resourceNames: [cnpg-backup-credentials]
  verbs: [get, watch, delete]
- apiGroups: [""]
  resources: [secrets]
  verbs: [create]
# list intentionally omitted — it leaks secret content and can't be name-restricted

This deployed cleanly, but the barman plugin's reconciler creates a namespace-scoped Role (postgresql-barman-cloud) in each Cluster CR's namespace to grant the instance manager access to credential secrets. Kubernetes's RBAC escalation prevention blocked it. The plugin's service account can't grant list on secrets via a Role if it doesn't hold that verb itself:

roles.rbac.authorization.k8s.io "postgresql-barman-cloud" is forbidden:
user "system:serviceaccount:paas-cloudnative-pg-operator:plugin-barman-cloud"
is attempting to grant RBAC permissions not currently held:
{APIGroups:[""], Resources:["secrets"], ResourceNames:["cnpg-backup-credentials"], Verbs:["list"]}

Adding escalate and bind verbs on the Role resource gets past this, but granting escalation privileges to compensate for overly broad defaults defeats the purpose. We abandoned this approach.

The operator and plugin should ship with namespace-scoped RBAC for secrets by default, not force downstream users to patch around cluster-wide permissions.

Additional context

Related: #10663 (about removing unused secrets/status subresource permissions, a subset of this broader scoping issue).

Backport?

No

Are you willing to actively contribute to this feature?

No

Code of Conduct

  • I agree to follow this project's Code of Conduct

Source: cloudnative-pg/cloudnative-pg