Introduction
An ordinary Rancher user holding nothing more than the default user global role, with no role bindings on the management cluster, could obtain full cluster-admin on Rancher's local cluster simply by pointing a Fleet Cluster object at the wrong kubeconfig Secret. Because the management cluster holds the credentials and API tokens for every downstream cluster, the vendor advisory states that the impact of CVE-2026-94290 extends to every cluster managed by the installation.
Fleet is SUSE's Kubernetes deployment engine. Built from custom resource definitions and controllers, it manages GitOps deployments from Kubernetes YAML, Helm charts and Kustomize. It ships bundled with Rancher and is enabled by default. SUSE reports recognition as a Leader in The Forrester Wave for Multicloud Container Platforms, Q3 2025, and placement in the Leaders Quadrant of Gartner's 2025 Magic Quadrant for Container Management with SUSE Rancher Prime. These are vendor-reported analyst recognitions, not market share figures. Because Fleet's controllers run with administrative rights on the management cluster, their authorization decisions sit directly on the trust boundary between Rancher tenants and the platform itself.
Technical Information
The privileged controller
Fleet's agent management, or "import", controller runs on Rancher's management cluster, called local, with cluster administrator privileges. Fleet Cluster objects reference a kubeconfig Secret through spec.kubeConfigSecretNamespace, and the controller uses that Secret to install and manage a Fleet agent on the target cluster. Whatever credentials the Secret contains, the controller will use on behalf of whoever created the Cluster object.
That makes the question "which Secrets may this Cluster reference?" a security decision. In affected versions, the controller answered it incorrectly.
Root cause: a namespace name allowlist
The vulnerable code resolved spec.kubeConfigSecretNamespace through allowedKubeConfigSecretNamespace(). The following is reconstructed from the removed and context lines of the fix diff:
func allowedKubeConfigSecretNamespace(cluster *fleet.Cluster) (string, error) { if cluster.Spec.KubeConfigSecretNamespace == "" || cluster.Spec.KubeConfigSecretNamespace == cluster.Namespace { return cluster.Namespace, nil } ns := cluster.Spec.KubeConfigSecretNamespace if ns == "fleet-local" || ns == "fleet-default" || ns == config.DefaultNamespace || ns == config.LegacyDefaultNamespace || ns == fleetns.SystemRegistrationNamespace(config.DefaultNamespace) || ns == fleetns.SystemRegistrationNamespace(config.LegacyDefaultNamespace) || strings.HasPrefix(ns, "cluster-") { return ns, nil } return "", fmt.Errorf("kubeConfigSecretNamespace %q is not an allowed Fleet namespace", ns) }
The function checks only where the Secret lives, never who is asking for it. A Cluster created in any namespace, including a workspace owned by an ordinary user, could reference a Secret in any of the well-known namespaces and pass. The critical entry is fleet-local, which holds the kubeconfig for the management cluster itself. The PR description also notes that the cluster- prefix rule matched a namespace name that "any namespace could claim".
The result is a confused deputy. The controller supplies authority that the initiating user does not possess directly. NVD records CWE-250 (Execution with Unnecessary Privileges). The GitHub advisory also lists CWE-269 (Improper Privilege Management), CWE-441 (Unintended Proxy or Intermediary) and CWE-863 (Incorrect Authorization).
Attack flow
Based on the advisory and the patch description, the documented attack path looks like this:
- An authenticated Rancher user holds only the default
userglobal role. No role bindings onlocaland no nondefault configuration are required. Fleet is enabled by default, and ordinary users' ability to create Fleet workspaces is intentional behavior. - The user creates a Fleet workspace, which gives them a namespace they control.
- In that namespace, the user creates a Fleet
Clusterwhosespec.kubeConfigSecretNamespacenamesfleet-local, referencing the management cluster's own administrative kubeconfig (thelocal-clusterSecret used in the fix's end-to-end test). - The import controller evaluates the reference with
allowedKubeConfigSecretNamespace(). Becausefleet-localis on the allowlist, the reference is accepted. - The controller builds a client from those credentials and deploys a Fleet agent onto
local, managing the management cluster on the user's behalf. - Fleet content the user controls, such as bundles targeting their
Cluster, is then applied to the management cluster with elevated privileges, which yields fullcluster-admin.
From there the advisory documents access to all management cluster secrets (including downstream cluster credentials and API tokens), the ability to change RBAC and any other resource, and the ability to disrupt the Rancher management server.
Severity scoring
- SUSE CNA, CVSS 3.1: 9.1 Critical,
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H - GitHub advisory, CVSS 4.0: 9.4 Critical,
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H - NIST: no separate NVD assessment in the retrieved record
The two scores come from different scoring models and do not conflict. Both vectors specify PR:H, while the advisory separately documents exploitation by a default Rancher user. The advisory does not reconcile the two, so we recommend basing exposure assessment on the concrete permission scenario rather than the metric alone.
Exploitation status
As of November 3, 2026, we found no evidence of active exploitation or attribution. CISA's ADP SSVC assessment (recorded 2026-11-02T16:13:30.631420Z) lists exploitation as none, automatable as no and technical impact as total. CVE Watchtower reporting dated November 3 states exploitation is not confirmed, and TheHackerWire reports no confirmed public exploit code or proof of concept, no KEV listing as of its November 2 record, and no known ransomware linkage. Kenzie Cuttler of Tulip Interfaces, Inc. Security Research reported the issue. The GitHub advisory was published October 29, 2026, and NVD publication followed on November 2, 2026.
Patch Information
Fixed releases
- Fleet:
v0.16.3,v0.15.8,v0.14.12andv0.13.17 - Rancher:
v2.15.3,v2.14.7,v2.13.11andv2.12.15
The security fix itself lives in Fleet. Rancher needs a compatibility change for the new authorization checks, so the advisory directs operators to upgrade Rancher and its bundled Fleet together.
On the v0.16 line the fix is PR #5969, "[v0.16] Restrict cross-namespace kubeconfig references", a backport of #5968 referencing GHSA-qfmw-gw96-ffcr and merged as commit 4f301adc. It is listed as the first change in the v0.16.3 release notes.
Layer 1: the Secret owner must opt in with a label
The patch removes the allowlist and replaces it with checkKubeConfigSecretReference() together with a new API constant, KubeConfigReferenceableFromLabel = "fleet.cattle.io/kubeconfig-referenceable-from":
func checkKubeConfigSecretReference(cluster *fleet.Cluster, secret *corev1.Secret) error { ns := kubeConfigSecretNamespace(cluster) if ns == cluster.Namespace { return nil } if secret != nil && secret.Labels[fleet.KubeConfigReferenceableFromLabel] == cluster.Namespace { return nil } if config.Get().AllowUnlabeledKubeConfigSecretNamespaces && isWellKnownFleetNamespace(ns) && (secret == nil || secret.Labels[fleet.KubeConfigReferenceableFromLabel] == "") { return nil } return fmt.Errorf("%w: secret %s/%s needs label %s=%s to be used by cluster %s/%s", ...) }
(The arguments to the final fmt.Errorf are shortened in the source material.)
This works because of Kubernetes RBAC. Only someone who can write Secrets in the Secret's namespace can set the label, so the credential owner decides which single namespace may use it. A tenant who controls only their own workspace cannot label the fleet-local Secret, so their reference is rejected. References within the same namespace are unchanged.
Every code path that previously read the Secret directly (importCluster, onConfig and teardownLocalAgent) now goes through a single helper, i.kubeConfigSecret(cluster), so none of them can skip the check. The helper also closes a small information leak. A missing Secret in another namespace returns the same error as an unlabeled one, so tenants cannot use Cluster status to enumerate Secrets in namespaces they cannot see.
Layer 2: a separate management cluster check
The label alone is not treated as sufficient. After building a client from the kubeconfig, importCluster calls i.checkManagementClusterTarget(cluster, kc), which compares the UID of the kube-system namespace seen through the kubeconfig with the management cluster's own UID. If they match, an agent is deployed only when the Cluster lives in one of:
- the bootstrap namespace (
fleet-localby default) - the controller's system namespace
- the controller's registration namespace
Otherwise the import fails with an error stating that the kubeconfig "targets the management cluster". Per the PR, this applies to every kubeconfig source, including Secrets in the Cluster's own namespace. A copied or labeled admin kubeconfig therefore still cannot be used to put an agent on local from a tenant workspace. A new end-to-end test copies the local-cluster Secret into a labeled namespace and confirms the import is rejected.
Behavior for existing agents and live changes
Previously, a Cluster with an already deployed agent exited early and was never validated again. Now importCluster still checks the reference for those clusters. A rejected reference appears in the Cluster's Imported condition, but the existing agent stays in place. The Secret index is now keyed only on the spec, and the Secret watch requeues every Cluster referencing a changed Secret, so adding or removing the label or changing the config flag takes effect without other edits.
Compatibility option
A new Helm value and config field, allowUnlabeledKubeConfigSecretNamespaces, defaults to false. Setting it to true restores the old behavior for unlabeled Secrets in the well-known namespaces (fleet-local, fleet-default, and the Fleet system and registration namespaces), with three limits:
- A Secret with a nonempty label can still only be used from the namespace the label names.
- Generated
cluster-namespaces and arbitrary namespaces such askube-systemremain rejected. - The Layer 2 management cluster check still runs.
The advisory states this option weakens protection and should be used only until affected Secrets are labeled. We would treat it as a temporary compatibility exception, not a mitigation.
Rancher's part
Rancher stores the kubeconfig Secret for clusters assigned to workspaces other than fleet-default in fleet-default. Patched Rancher adds and maintains the label on those Secrets automatically. Administrators who manage cross-namespace kubeconfig Secrets themselves must add the label. Otherwise those clusters are no longer imported:
kubectl -n kubeconfigs label secret my-cluster-kubeconfig fleet.cattle.io/kubeconfig-referenceable-from=clusters
Tests
The PR adds roughly 1,100 lines across 13 files, mostly tests:
- table-driven unit tests for
checkKubeConfigSecretReference,kubeConfigSecretandcheckManagementClusterTarget - envtest integration tests in
integrationtests/agentmanagement/cluster_import_test.go - an end-to-end suite that replays the original attack path, a workspace
Clusterreferencing thelocal-clusterSecret
If you cannot upgrade immediately
The advisory states that no Fleet configuration option fully prevents this issue on affected versions. It lists these exposure reduction measures:
- Remove workspace self-service: use custom or amended global roles so ordinary users do not receive
createonmanagement.cattle.iofleetworkspaces. Validate against your tenancy model, since this removes an intentional capability. - Restrict Fleet object creation: review bindings that grant write access to
fleet.cattle.ioclustersandbundlesin nonsystem namespaces, and limit them to administrators and trusted operators. - Reject unauthorized Secret references: use an admission or policy controller such as Kubewarden to deny tenant
Clustercreates or updates whenkubeConfigSecretNamespacepoints outside the object's namespace. Legitimate Rancher workspace references tofleet-defaultneed explicit exceptions. - Monitor: alert on unexpected Fleet agent namespaces and deployments on
local, and on newClusterRoleBindingobjects grantingcluster-admin.
Detection Methods
No public YARA, Sigma, Snort/Suricata or Nuclei signatures exist, and no static IoCs such as hashes or IPs have been published. That fits the bug. The attacker sends ordinary authenticated Kubernetes API requests, and the import controller does the privileged work with its own rights. Detection therefore relies on Kubernetes object state and audit telemetry on local, at three points: the bad reference, what the controller does next, and the resulting privilege.
1. Identify vulnerable deployments
Compare both the Rancher and Fleet versions against the fixed releases above. Rama Digital's write-up reads the server-version and fleet-version objects (settings.management.cattle.io) with kubectl. Without cluster access, it reads the unauthenticated /rancherversion endpoint, which returns only the Rancher version, not the Fleet version. Also check which global roles grant create on fleetworkspaces and how many users are bound to them, since the default user role allows it.
2. Hunt for cross-namespace kubeconfig references
This is the highest-signal check. List every Fleet cluster with its namespace and the namespace of its referenced Secret:
kubectl get clusters.fleet.cattle.io -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"|"}{.metadata.name}{"|"}{.spec.kubeConfigSecretNamespace}{"\n"}{end}'
Triage the output:
- Same namespace, or empty: normal.
- Points to
fleet-default: may be legitimate, because Rancher stores kubeconfig Secrets for clusters in other workspaces there. Confirm the owner. - Points to
fleet-local: treat as a strong sign of compromise, because that namespace holds the management cluster's own credentials.
The advisory's admission rule doubles as an audit log alert. A create or update of clusters.fleet.cattle.io outside Fleet's system namespaces whose kubeConfigSecretNamespace differs from its own namespace is suspect. Run it continuously, not just as a one-time sweep.
3. Watch the precursors in audit logs
TheHackerWire's AI-assisted analysis recommends flagging Fleet Cluster creates or edits by nonadministrative users. The Rama Digital script adds a check for new fleetworkspaces.management.cattle.io objects other than fleet-default and fleet-local, and the field.cattle.io/creatorId annotation shows who created them. An ordinary user creating a workspace and then a Cluster with a foreign Secret reference is the attack sequence in miniature.
4. Detect the consequences
SUSE's advisory names two alert conditions:
- New Fleet agent namespaces or deployments on
local. The legitimate local agent lives incattle-fleet-local-system. Afleet-agentdeployment in any other namespace on the management cluster indicates the controller was made to manage it on a tenant's behalf. - New
ClusterRoleBindingobjects whoseroleRefiscluster-admin. Compare creation times against change records. Rancher-generated bindings for global admins, namedglobaladmin-*, are normally expected but should still be verified.
More broadly, alert on Fleet bundle content landing on the management cluster when it traces back to a user-controlled Cluster definition.
5. Post-patch signals
Upgrading does not remove agents that were already deployed, so hunt backwards in time. After upgrading:
- Monitor
Importedconditions for rejections. A rejection tied to a tenant namespace may indicate an attempted attack or one that happened before the upgrade. - Audit which Secrets carry
fleet.cattle.io/kubeconfig-referenceable-fromand which namespaces the label values allow. - Alert on
allowUnlabeledKubeConfigSecretNamespaces: trueas a weakening of the protection.
Hunting notes
Run checks over a window that starts before the patch date. If you find even one strong sign, preserve Rancher and Kubernetes audit logs before cleanup. Because downstream credentials live on local, one hit means the whole managed fleet may be exposed. A clean result does not prove safety, since an attacker with cluster-admin can delete objects and logs, so back these checks with off-cluster audit log retention. TheHackerWire also suggests Shodan queries such as http.title:"Rancher" to find internet-exposed Rancher interfaces. This only maps attack surface and cannot show vulnerability or exploitation, because the attack requires a valid account.
Affected Systems and Versions
SUSE Rancher Fleet, as bundled with Rancher, in these ranges:
- 0.16: 0.16.0 through versions before 0.16.3 (fixed in 0.16.3)
- 0.15: 0.15.0 through versions before 0.15.8 (fixed in 0.15.8)
- 0.14: 0.14.0 through versions before 0.14.12 (fixed in 0.14.12)
- 0.13: 0.13.0 through versions before 0.13.17 (fixed in 0.13.17)
The CVE description's phrase "up to" should not be read as including the fixed versions. The CVE record also says "possible older versions", so releases outside these families should not automatically be treated as unaffected. The corresponding patched Rancher releases are v2.15.3, v2.14.7, v2.13.11 and v2.12.15.
The vulnerable configuration is the default one. Fleet is enabled by default, ordinary users with the user global role can create Fleet workspaces, and no additional feature or nondefault setting is required.
Vendor Security History
The most relevant comparison is CVE-2026-88808 (GHSA-q9v4-358v-r8q5), listed in SUSE's index on September 23, 2026. There, the Fleet agent wrote downstream resources with its own administrator credentials rather than the deployment's pinned ServiceAccount, letting a tenant able to create a Bundle, GitRepo or HelmOp write or overwrite Secrets and ConfigMaps across targeted downstream namespaces. Fleet v0.16.2, v0.15.7 and v0.14.11 fixed it by performing writes and namespace creation with the pinned ServiceAccount. Those releases do not fix CVE-2026-94290. The earlier flaw concerns which identity performs downstream writes, while this one concerns which credentials a tenant object can make the management controller use. A fix at one boundary does not establish protection at the other.
Other recent Rancher entries in SUSE's index:
- CVE-2026-88805 (September 23, 2026; Rancher v2.15.2): logout cleared browser cookies but left the public API session token valid until expiry. Only deployments using these tokens are affected.
- CVE-2026-75034 (August 27, 2026; Rancher v2.15.1, v2.14.5, v2.13.9): ineffective SAML replay protection in deployments with multiple replicas allowed a captured assertion to create additional victim sessions.
On process, Rancher commits to publishing advisories and CVEs for resolved issues, SUSE accepts vulnerability reports at [email protected], and the rancher/scans repository publishes daily Trivy-based image scan reports.
References
- NVD: CVE-2026-94290
- CVE record for CVE-2026-94290 (cveawg.mitre.org)
- NVD API record for CVE-2026-94290
- GitHub advisory GHSA-qfmw-gw96-ffcr: Privilege escalation to cluster admin on the Rancher local cluster through Fleet's agent management controller
- GHSA-qfmw-gw96-ffcr via GitHub API
- rancher/fleet PR #5969
- rancher/fleet PR #5969 changed files
- Fleet v0.16.3 release
- Fleet docs: Cluster registration
- Fleet overview
- SUSE Rancher Manager v2.15: Fleet overview
- SUSE Rancher Manager v2.15: Security Advisories and CVEs
- GHSA-q9v4-358v-r8q5 (CVE-2026-88808)
- CISA Known Exploited Vulnerabilities feed
- CVE Watchtower: CVE-2026-94290
- TheHackerWire: CVE-2026-94290 vulnerability details
- TheHackerWire: CVE-2026-94290 analysis
- Rama Digital: CVE-2026-94290 write up
- rancher/scans: SUSE Rancher's CVE Portal
- SUSE Security Contacts
- SUSE Recognized as a Leader in 2025 Gartner Magic Quadrant for Container Management
- SUSE a Leader in The 2025 Forrester Wave



