Skip to content

rbac: dashboard resource Roles bind the tenant and every parent tenant ServiceAccount to the credentials Secret #4164

Closed
cozystack/community
#74

Description

@kvaps

Describe the bug

Every chart that renders dashboard-resourcemap.yaml grants get/list/watch on its own <release>-credentials Secret and binds that Role through cozy-lib.rbac.subjectsForTenantAndAccessLevel (list "use" .Release.Namespace).

That helper (packages/library/cozy-lib/templates/_rbac.tpl) expands a namespace such as tenant-abc-def into the chain tenant-abc, tenant-abc-def, tenant-root, and emits for each link both the groups <tenant>-use|admin|super-admin and a ServiceAccount subject named <tenant> in namespace <tenant>.

So, for every managed application:

  • the tenant's own ServiceAccount — a workload identity — can read the application's credentials Secret directly, bypassing the tenantsecrets projection that exists to mediate that access;
  • so can the ServiceAccount of every parent tenant, up to tenant-root.

Expected behaviour

A workload identity does not read managed-service credentials. The dashboard reads them as the logged-in user, so nothing in the product needs the ServiceAccount to hold that grant.

Actual behaviour

kubectl auth can-i get secret <release>-credentials --as=system:serviceaccount:tenant-root:tenant-root -n tenant-abc-def succeeds for a child tenant's application.

How it got here

The Role itself dates from 00f7c3647 (2024-08-07) but had no RoleBinding. The binding was added in 3b8a9f9d2 (2025-06-16, "Configure all apps to use new function to generate subjects"), already carrying ServiceAccount subjects.

Worth stating plainly, because the commit was not a straightforward widening: it simultaneously narrowed the tenant Roles in packages/apps/tenant/templates/tenant.yaml from apiGroups: [""], resources: ["*"] — every Secret in the namespace — to an enumerated list without Secrets. For groups that was a tightening. For ServiceAccounts, which held no access to Secrets at all, it was a widening.

The parent-tenant subject chain is itself the platform's normal idiom: RoleBinding cozy:tenant in the same file binds the same chain. The difference is that ClusterRole cozy:tenant grants no core/v1 Secrets, so until this binding existed the chain never reached a credential.

Scope

subjectsForTenantAndAccessLevel is consumed by 28 charts under packages/apps/* and packages/extra/*, plus packages/apps/tenant/templates/tenant.yaml and packages/apps/vpc/templates/vpc.yaml. Most of those bindings grant reads on Services, Ingresses and WorkloadMonitors, where a ServiceAccount subject is harmless.

Two possible fixes, and I would like direction before sending a patch

  1. Drop the ServiceAccount subjects from subjectsForTenantAndAccessLevel. One edit, but it changes the subject list of all 28 bindings, most of which are not about credentials.
  2. Split the secrets rule out into its own Role bound to groups only, and leave the rest of each resource map untouched. Precise to the defect, but it touches every chart that renders a credentials Secret.

Who depends on the ServiceAccount grant

Nothing I can find. No pod in this repository runs as the bare <tenant> ServiceAccount — the tenant cleanup Job uses its own <tenant>-cleanup. A tenant could nevertheless have issued a token for that ServiceAccount and wired it into their own automation, which is not visible from the source, so whichever fix lands should carry a release note.

Environment

  • main at 4007db7a0
  • Affects any installation; not provider-specific.

Related

Checklist

  • I have checked the documentation
  • I have searched for similar issues
  • I have included all required information
  • I have provided clear steps to reproduce
  • I have included relevant logs — not applicable, this is a manifest-level grant rather than a runtime failure

Activity

  1. lexfrei commented on Sep 22, 2026

    @lexfrei
    Contributor

    The info chart doesn't follow this pattern, and its binding for the OIDC-off case is dead. That branch in packages/extra/info/templates/dashboard-resourcemap.yaml skips the helper and hard-codes a ServiceAccount subject with the tenant- prefix twice. For the release in tenant-root it renders tenant-tenant-root in namespace tenant-tenant-root, while the tenant ServiceAccount is tenant-root in tenant-root. So the binding matches nothing, and the Role behind it (get on the tenant-root Secret) reaches no one. Whichever fix lands here, that branch needs its own call: fix the name or drop the binding.

  2. added
    area/tenantIssues or PRs related to the tenant chart and multi-tenancy
    triage/acceptedIndicates an issue is ready to be actively worked on
    priority/important-longtermImportant over the long term, but may not be staffed and/or may need multiple releases to complete
    and removed
    triage/needs-triageIndicates an issue needs triage by a maintainer
    on Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/tenantIssues or PRs related to the tenant chart and multi-tenancykind/bugCategorizes issue or PR as related to a bugpriority/important-longtermImportant over the long term, but may not be staffed and/or may need multiple releases to completesecurity/triage-neededNeeds security triagetriage/acceptedIndicates an issue is ready to be actively worked on

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions