Multi-Tenancy
This page describes the trust model Fleet uses to support multiple tenants — teams or projects that share a Fleet manager and, often, downstream clusters — without letting them act on each other’s resources or escalate privileges.
For the operator setup recipe, see Tenant Setup. For the field-by-field schema of the resource enforcing the model, see Policy Resource.
The Trust Model
Fleet runs upstream on a manager cluster and deploys workloads downstream by impersonating a ServiceAccount on each target cluster. Tenant isolation is the question of who decides which ServiceAccount a tenant’s workloads run as, and which permissions that account holds on the downstream cluster.
Fleet answers this by drawing a clear line:
-
Downstream clusters are the authority on what tenants may do there. The operator encodes a tenant’s authority once, as RBAC on a
ServiceAccountthat exists on each downstream cluster the tenant deploys to. The `ServiceAccount’s permissions — which namespaces it can write to, which resource kinds and verbs it can use — are the answer to "what may this tenant do downstream?" -
Upstream, Fleet enforces a single rule: a tenant’s
GitRepo,HelmOp, orBundlemust reference aServiceAccountfrom an operator-defined allow-list, and thatServiceAccountmust already exist on the downstream cluster. Anything else — target namespaces, allowed resource kinds, allowed verbs — falls under the downstream `ServiceAccount’s RBAC, where it belongs.
The upstream enforcement mechanism is the Policy custom resource.
The Policy Resource
A Policy lives in the same namespace as the tenant’s GitRepo, HelmOp, and Bundle resources. It carries the operator’s restrictions:
-
Top-level fields (
requireServiceAccount,allowedServiceAccounts) are checked by the GitRepo, HelmOp, and Bundle reconcilers. -
Sub-object fields (
gitRepo,helmOp) are read only by their respective reconciler. They carry per-resource-kind default values and source/credential restrictions.
If a tenant submits a resource that violates the active Policy, the resource’s status reports the violation and no deployment proceeds. If the resource is missing a value that the Policy provides as a default, the default is applied before validation runs.
For the full field reference, see Policy Resource.
Aggregation
A namespace may contain multiple Policy objects. The reconcilers aggregate them with OR/union semantics, sorted by name:
-
Allow-lists are unioned.
-
Default values are taken from the first non-empty entry after the sort.
-
requireServiceAccountis true if anyPolicyin the namespace sets it.
This makes Policy composable across multiple administrators, but it implies that tenants must not have write access to Policy objects in their own namespace. A tenant who can create their own Policy can extend the allow-list and reference service accounts the operator did not intend to grant. The Tenant Setup guide spells out the required RBAC.
The Pre-Existing ServiceAccount Requirement
When a GitRepo, HelmOp, or Bundle deploys downstream, the Fleet agent impersonates the named ServiceAccount on the downstream cluster. The Fleet manager does not create that ServiceAccount for you — it must already exist before the deployment runs.
This is by design. The whole point of pinning a tenant to a ServiceAccount is that the downstream cluster’s RBAC on that account is the source of truth for what the tenant may do. If Fleet created the account itself, the downstream cluster would no longer be the authority — Fleet would be.
In practice, the operator is responsible for creating the ServiceAccount (plus its Role / RoleBinding) on every downstream cluster a tenant will deploy to. The Tenant Setup guide describes how to bootstrap and maintain these resources using Fleet itself — typically via a Fleet-manager-owned GitRepo from a privileged upstream namespace that targets all relevant downstream clusters.
If a tenant’s resource names a ServiceAccount that does not exist on the downstream cluster, the deployment fails with a clear error.
The Default fleet-default ServiceAccount
A tenant resource does not have to name a ServiceAccount explicitly. When none is configured, the Fleet agent looks for a ServiceAccount named fleet-default in the cattle-fleet-system namespace on the downstream cluster and impersonates it if it exists.
If fleet-default does not exist either, the agent falls back to its own identity, which is effectively cluster-admin. This fallback is precisely the behaviour that pinning a tenant to a ServiceAccount exists to prevent: a deployment that resolves to no ServiceAccount runs with full privileges on the downstream cluster, and the downstream RBAC is no longer the source of truth for what it may do.
For this reason, do not treat the absence of a ServiceAccount as a security boundary. Set requireServiceAccount: true on the Policy so that a resource which does not name a ServiceAccount is rejected rather than silently deployed as the agent. See Tenant Setup for the recommended configuration.
The resolved identity — a pinned ServiceAccount, the fleet-default fallback, or the agent itself — is also the identity used to apply namespaceLabels and namespaceAnnotations to the target namespace. Those namespace mutations are therefore gated by the same downstream RBAC as the deployment itself, rather than by the agent’s cluster-admin credentials whenever a ServiceAccount is resolved.
Namespace Creation
By default, Fleet lets Helm create the target namespace if it does not already exist. Creating a namespace requires the create verb on the cluster-scoped namespaces resource. That verb cannot be confined to a single namespace or tenant: granting it to a tenant’s ServiceAccount lets that tenant create any namespace on the downstream cluster, which breaks the isolation that pinning to a ServiceAccount is meant to provide.
To avoid this, when a Policy in the tenant’s namespace sets requireServiceAccount: true, Fleet disables Helm namespace creation for deployments in that namespace. The tenant’s ServiceAccount therefore does not need namespaces.create, and the target namespace must already exist on the downstream cluster. Creating it is the operator’s responsibility — typically through the same Fleet-manager-owned GitRepo used to bootstrap the tenant’s ServiceAccount and RBAC. See Tenant Setup.
An operator who has secured namespace creation through other means — a ValidatingAdmissionPolicy, Kyverno, or similar — can re-enable it by setting allowNamespaceCreation: true on the Policy.
Two properties of this field are worth noting:
-
It only takes effect together with
requireServiceAccount. Without aServiceAccountrequirement, namespace creation is left at its default (enabled), because there is no tenant boundary to protect. -
Like the rest of
Policy, it aggregates across allPolicyobjects in the namespace with least-restrictive (OR) semantics. If anyPolicyin the namespace setsallowNamespaceCreation: true, namespace creation is enabled for every deployment in that namespace. An operator relying on creation staying disabled must ensure that noPolicyin the namespace enables it.
Disabling namespace creation does not disable namespace mutation: namespaceLabels and namespaceAnnotations are still applied to the existing namespace, as the resolved ServiceAccount and gated by its downstream RBAC (see above).
What Policy Deliberately Does Not Cover
Policy has no field that restricts which downstream namespaces a tenant may deploy to. This is intentional: namespace restrictions are the responsibility of the downstream cluster’s RBAC on the tenant’s ServiceAccount, not of Fleet.
If a tenant should only be able to write to namespaces app-1 and app-2 on a downstream cluster, the operator expresses this as a Role granting the relevant verbs on those namespaces, bound to the tenant’s ServiceAccount. There is nothing for Policy to add — the deployment will succeed or fail based on whether the ServiceAccount has the required RBAC, which is the same outcome any other actor would observe.
Splitting the responsibility this way means downstream RBAC remains the single source of truth for downstream authority. Fleet’s job is to make sure the right ServiceAccount is used; the permissions of that ServiceAccount are the operator’s job to set.
Why requireServiceAccount Is Its Own Field
The Policy resource declares requireServiceAccount as a distinct field rather than inferring the requirement from the presence of a default and an allow-list. The declarative form is preferred for two reasons:
-
It expresses operator intent explicitly. Auditing a
Policyanswers the question "must this tenant set aServiceAccount?" by looking at one field. -
It makes the rule uniform across
GitRepo,HelmOp, andBundle. A separate default per resource kind would not extend to the Bundle reconciler, which validates without applying defaults.
Where GitRepoRestriction Fits
The GitRepoRestriction custom resource predates Policy. It will be deprecated in v0.17 and may be removed as soon as v0.18; it remains supported until removed. Do not use it for security-sensitive setups: its repo patterns are matched unanchored, meaning a pattern like github.com/myorg also matches a URL such as https://evil.com/?ref=github.com/myorg. It also has no coverage of HelmOp or Bundle resources, so a tenant who can create those resources bypasses GitRepoRestriction entirely. Policy covers what GitRepoRestriction did for GitRepo and extends the same model to HelmOp and Bundle, with anchored regex patterns and a clearer separation between upstream enforcement and downstream authority.
Operators migrating an existing setup should follow Migration from GitRepoRestriction in the Tenant Setup guide.
Glossary
-
Tenant: a team or project that shares a Fleet manager (and often downstream clusters) with other teams. Tenants are isolated at the upstream namespace level and at the downstream
ServiceAccountRBAC level. -
FleetWorkspace: the upstream Kubernetes object used to materialise a tenant in a Rancher-integrated Fleet setup. In a stand-alone Fleet setup, the upstream namespace is the tenant’s unit of isolation.
-
Pre-existing ServiceAccount: a
ServiceAccountthat already exists on a downstream cluster before Fleet attempts to use it. The operator is responsible for creating and maintaining these accounts and their RBAC.