DevOps

Flux 2.10 Makes Identity the New GitOps Upgrade Boundary

Flux 2.10 is easy to misread as another feature-rich GitOps release. It adds SPIFFE-based mutual TLS, makes object identity generally available, stabilizes alerting APIs, expands artifact generation, and improves image automation. But the operationally important change is narrower: identity is now part of the reconciliation path, and the upgrade deliberately removes a broad token-minting permission from Flux’s default RBAC.

That combination changes how platform teams should plan the rollout. The question is no longer simply whether the new controllers reconcile the same manifests. Teams must identify which Flux objects obtain credentials, which controllers are allowed to mint tokens, which external systems trust those tokens, and whether a failure will appear as a source, registry, secrets, or remote-cluster problem. Flux 2.10 should therefore be treated as an identity migration with a GitOps upgrade attached.

Why this release changes the upgrade boundary

Flux has supported object-level workload identity since version 2.6, behind a feature gate. Version 2.10 promotes the capability to general availability, renames it object identity, and retires the ObjectLevelWorkloadIdentity gate. The terminology now distinguishes controller identity—the identity of a Flux controller—from object identity, which is assigned to an individual Flux resource through .spec.serviceAccountName.

This is more than a naming cleanup. A source, image repository, notification, Kustomization, or Helm release can use the service account associated with that object to obtain a short-lived credential. The credential can come from the Kubernetes TokenRequest API or, with the new SPIFFE provider, from the SPIFFE Broker API as a JWT-SVID. That credential can then be presented to a registry, secret store, remote Kubernetes cluster, or another supported external service.

Flux 2.10 also supports SPIFFE X509-SVIDs for mutual TLS between controllers. Artifact fetching and internal event delivery can be protected so that reaching a controller Service is no longer sufficient: the caller must present an authorized SPIFFE identity. Inter-controller mTLS and object identity solve different problems, and each traffic type is enabled separately. Platform teams should avoid treating “we installed SPIRE” as proof that every Flux path is protected.

The RBAC change is the upgrade’s sharp edge

The default Flux RBAC no longer grants create on serviceaccounts/token. That permission allows a controller to request a bound service-account token. Removing it is a sensible least-privilege default, but installations already using the Kubernetes credential provider may depend on it. The affected paths can include source-controller, notification-controller, image-reflector-controller, and image-automation-controller.

The consequence is counterintuitive: object identity is always enabled, yet the default installation no longer automatically supplies one permission needed by a common provider. Existing users must explicitly bind the documented ClusterRole to only the controller service accounts that require token creation. This forces the platform owner to make the trust decision instead of inheriting it from the installation bundle.

Do not respond by restoring token creation to every controller. That recreates the broad privilege the release is trying to remove. Inventory actual object-identity use, map each resource type to its controller, and grant the permission only where the chosen provider requires it. A cluster that uses SPIFFE for one path and Kubernetes tokens for another may legitimately need different bindings across controllers.

Build an identity map before changing controllers

A useful pre-upgrade artifact is a small identity matrix owned alongside the Flux platform configuration. For every identity-enabled object, record the namespace, service account, responsible controller, credential provider, intended audience, and external relying party. Add the authorization mechanism at the destination—for example, a registry trust policy, Vault JWT role, or remote-cluster authorization rule.

The inventory should answer five questions:

  • Who requests the credential? Identify the Flux controller and its service account.
  • Who is represented? Identify the object’s service account and namespace.
  • Who issues it? Distinguish Kubernetes TokenRequest from the SPIFFE Broker API.
  • Who accepts it? Record the registry, secrets system, or remote cluster and the audience it expects.
  • How does failure surface? Name the Flux status condition, controller log, and external audit trail that operators will inspect.

This map exposes accidental privilege sharing. If several tenants use one service account because it was convenient during a pilot, general availability is a good point to split those identities. Conversely, creating one service account per object without a policy reason can produce operational noise. The useful boundary is normally a workload or tenant trust boundary, not an arbitrary resource count.

Choose Kubernetes tokens and SPIFFE deliberately

The Kubernetes provider uses the TokenRequest API and fits environments where external systems already validate Kubernetes-issued JWTs. It has fewer moving parts, but it couples trust to the cluster issuer and requires the relevant Flux controller to create service-account tokens. Teams must configure audiences carefully; a token intended for one registry or secrets service should not be reusable at another.

The SPIFFE provider retrieves a JWT-SVID through the Broker API. It is attractive when the organization already operates a SPIFFE trust domain across clusters or wants identities independent of a particular Kubernetes issuer. It also supports a broader identity architecture in which controllers obtain X509-SVIDs for mTLS while individual objects obtain JWT-SVIDs for external systems.

SPIFFE is not a zero-cost security switch. The Workload API must be available to participating controllers, selectors and registration entries must be correct, trust bundles must be distributed, and the Broker API must enforce the expected relationship between the controller and requested object identity. An outage or policy error in that layer can stop reconciliation across multiple destinations. Adopt it where a shared identity plane is worth that dependency, not merely because the release supports it.

A safer rollout sequence

1. Remove obsolete configuration, but preserve behavior

Delete the retired ObjectLevelWorkloadIdentity feature-gate setting from the desired installation configuration. Leaving it in place only produces an informational startup log, but removing it prevents operators from assuming the gate still controls behavior. Do not change credential providers in the same commit unless that migration has been tested independently.

2. Add only the RBAC that current workloads require

Before upgrading production, use the identity map to determine whether any controller relies on Kubernetes TokenRequest. Apply the project’s documented ClusterRole and bindings only to those controller service accounts. Validate the binding with impersonation or authorization checks in a staging cluster, then confirm that an unrelated controller remains unable to create tokens.

3. Test each identity path, not just reconciliation health

A green top-level Kustomization does not exercise every credential flow. Force a safe fetch from each representative Git or OCI source, query an image repository, send a test notification, perform a controlled SOPS decryption, and validate remote-cluster access where used. Rotate or invalidate a test credential and verify that short-lived replacement works without editing a Kubernetes Secret.

4. Introduce inter-controller mTLS one traffic type at a time

Flux enables artifact fetching, event delivery, and webhook protection independently. Start with a non-production cluster, verify X509-SVID issuance and authorization, then enable one path. Capture both a successful request and a deliberately unauthorized request. This proves that traffic is not merely encrypted but authenticated against the expected SPIFFE IDs.

5. Canary the controllers and watch external audit logs

Canarying by cluster is safer than updating an entire fleet at once because identity failures can look like ordinary network or authorization problems. Monitor reconciliation latency, source fetch errors, registry authentication failures, token request denials, and SPIFFE issuance errors. External systems should show the expected subject and audience, not only a successful login.

Do not bundle unrelated migrations into the same window

Flux 2.10 contains other changes that deserve their own checks. Alert and Provider resources are promoted from v1beta3 to v1, with deprecated fields removed during conversion. The flux migrate command can update both stored cluster resources and manifests in a Git checkout. The ImageRepository tag database also defaults to Flux artifact storage; the first reconciliation after switching rescans repositories.

Those changes are relevant to upgrade planning, but they should not obscure the identity thesis. Migrate alerting manifests in a separate reviewed commit, and account for the image rescan as a capacity event. Keeping these changes separate makes rollback and diagnosis much clearer if a registry fetch fails after the controller update.

Also check the support floor. Flux 2.10 supports Kubernetes 1.35 through 1.37, and Flux 2.7 has reached end of life. A team on an older Kubernetes minor cannot treat this as an in-place controller-only upgrade; the cluster lifecycle must be resolved first.

Define rollback before production

A reliable rollback plan distinguishes controller rollback from identity rollback. If the new controller version fails, pinning the prior manifests may restore reconciliation, but external trust-policy changes or new SPIFFE registrations could remain. Likewise, removing an RBAC binding too early can break the old deployment if it already used object-level workload identity.

Record the previous controller versions, installation manifests, feature-gate configuration, RBAC bindings, and destination trust policies. Make changes additive first: establish the new trust path, verify it, switch representative objects, and only then remove the old path. Set an explicit observation period based on credential lifetime so that at least one renewal cycle completes before declaring success.

The platform contract is now explicit

Flux 2.10 gives platform teams stronger primitives: short-lived object credentials, optional SPIFFE-based controller authentication, and a default RBAC posture that refuses to mint tokens without an explicit grant. The value is not that Flux can eliminate every static secret immediately. The value is that identity decisions can be encoded per trust boundary and reviewed like the rest of the platform.

The upgrade succeeds when teams can explain every credential exchange in the reconciliation path. If the answer is only “Flux has access,” the model is still too broad. If the answer names the object, service account, controller, issuer, audience, destination, and observable failure mode, Flux 2.10 becomes a meaningful security improvement rather than a risky version bump.

Sources