CVE Patch releases for 1.14.4
Release 1.14.4000
Released 9 September 2026
-
Fixed the following CVEs: CVE-2023-44487, CVE-2025-47273, CVE-2025-68384, CVE-2025-8941, CVE-2026-10722, CVE-2026-11856, CVE-2026-13346, CVE-2026-13505, CVE-2026-13595, CVE-2026-14456, CVE-2026-14457, CVE-2026-15059, CVE-2026-15060, CVE-2026-15788, CVE-2026-16742, CVE-2026-18798, CVE-2026-23949, CVE-2026-27456, CVE-2026-33818, CVE-2026-34743, CVE-2026-3731, CVE-2026-39821, CVE-2026-39822, CVE-2026-4046, CVE-2026-42505, CVE-2026-45292, CVE-2026-46600, CVE-2026-48586, CVE-2026-49844, CVE-2026-50163, CVE-2026-50813, CVE-2026-53612, CVE-2026-53613, CVE-2026-53614, CVE-2026-53615, CVE-2026-5435, CVE-2026-5450, CVE-2026-54874, CVE-2026-55969, CVE-2026-56148, CVE-2026-56149, CVE-2026-56852, CVE-2026-56853, CVE-2026-56854, CVE-2026-56858, CVE-2026-56859, CVE-2026-56860, CVE-2026-56862, CVE-2026-56864, CVE-2026-56865, CVE-2026-57585, CVE-2026-58469, CVE-2026-58470, CVE-2026-58472, CVE-2026-5928, CVE-2026-59890, CVE-2026-59903, CVE-2026-59949, CVE-2026-61801, CVE-2026-6238, CVE-2026-63072, CVE-2026-63073, CVE-2026-63074, CVE-2026-63075, CVE-2026-63076, CVE-2026-63136, CVE-2026-63140, CVE-2026-63144, CVE-2026-63263, CVE-2026-66755, CVE-2026-68497, CVE-2026-71497, CVE-2026-75803, GHSA-6v7p-g79w-8964, GHSA-gcjh-h69q-9w9g, GHSA-hrxh-6v49-42gf, GO-2026-6061, XRAY-1032218, XRAY-1032959, XRAY-1032960, XRAY-1033007, XRAY-1035464, XRAY-1046408, XRAY-1046409, XRAY-1047060, XRAY-1047069, XRAY-1047072, XRAY-1047073, XRAY-1057284, XRAY-1057286
-
Fixed the Tetrate Management Plane operator taking up to ~16 minutes to report the ManagementPlane as
INSTALLEDafter a transient error during a fresh install, even once all components had become healthy. The operator now re-reconciles on a short bounded interval until the install converges, so the status reflects readiness within seconds. -
Fixed an issue where a fresh management plane or control plane install could remain in the
INSTALLINGphase for several minutes after every component was already healthy. Transient errors during convergence (for example a dependency CRD not yet registered, or a component Deployment still cold-starting) caused the operator to requeue with exponential backoff that grew to ~16 minutes, so it stopped re-checking the now-healthy components and was late to setINSTALLED. The operator now requeues these still-converging conditions on a short, bounded interval and caps its worst-case requeue backoff, so the install reachesINSTALLEDshortly after the last component becomes ready. -
The management plane OTel collector now forwards the control plane's
xcp_edge_gateway_exposed_hostsmetric to the web backend, so the TSB UI can show the number of hostnames each gateway workload is configured to expose (per gateway kind and aggregated) on the service Configuration tab. Only allowlisted metrics are forwarded to the web backend. -
Security: The
tetrate-openldapimage used by the demo installation is now built on Alpine instead of Debian. Alpine does not ship Linux-PAM, which removes CVE-2025-8941 and the rest of the Linux-PAM vulnerability surface from the image. OpenLDAP is also updated from 2.6.8 to 2.6.13 and the image size drops from roughly 450MB to 13MB. -
The management plane policy decision point (PDP) now uses NGAC's compiled integer decision engine, lowering authorization decision latency and memory allocation on large policy graphs (roughly 3.6x faster
Listwith 2.6x fewer allocations at the 25k-node tier). The change is behavior-preserving and parity-verified against the previous decision engine. -
Service accounts can now use key pairs that you generate yourself, so the private key never reaches TSB. The new
Teams.AddServiceAccountKeyAPI registers a public key, in JWK format, against an existing service account, andtctl service-account add-key <name> --key-path <public key file>does the same from the command line.Teams.CreateServiceAccountalso takes an optionalpublicKey, as doestctl service-account create <name> --key-path <public key file>. A service account created this way never has a TSB-generated key at all, so no private key is returned and the command prints the ID of the key instead.Clusters.CreateClustertakes the same optionalpublicKeyfor the service account its Control Plane authenticates with. TSB then has no private key to put in the returned install template, so the template comes back withoutsecrets.clusterServiceAccount.JWKand with a message saying to set it to the private key you kept before installing the Control Plane. Reading the install template of the cluster again returns it the same way, rather than generating a key pair TSB would hold a Control Plane credential for.Which of the two supplied the first key is reported by the new read-only
keyManagementfield of a service account,TSBorCLIENT. It is decided when the service account is created and does not change afterwards; keys can still be added either way, whoever manages them.The key must carry a
kid. It becomes the ID of the key pair and is the key ID to use when signing tokens with it, so the client, and not TSB, decides it. As the ID addresses the key in the key management APIs, where it is a single path segment, it is limited to 128 letters, digits and the characters-,.and_.Adding a key requires the same permissions as generating one. Private keys, keys that are too weak to be trusted, keys asking for a signature algorithm other than the one TSB verifies them with, and keys whose ID is already taken are rejected.
-
Fixed GitOps-managed TSB resources getting stuck in an endless reconcile loop when the spec contained a required enum field set to its first (zero) value, such as a
ServiceRoutewith aportLevelSettingsentry usingtrafficType: HTTP. The TSB operator wrote the whole object back to Kubernetes when adding its finalizer, and the serialization dropped those fields, so the API server rejected the write withRequired value. The configuration still reached TSB and propagated to the clusters, but the object never received thegitops.tetrate.io/finalizerand itsstatuswas never updated. The operator now writes finalizers with a patch scoped tometadata, leaving the stored spec untouched. -
Added
authenticationSettings.minProtocolVersiontoSecuritySetting, so a minimum TLS protocol version can be set once on a workspace, security group or organization and enforced across the namespaces it selects. The same field is available in configuration profiles, so the floor can be handed down through the profile hierarchy rather than repeated on every workspace.The value is enforced wherever a proxy terminates TLS. On gateways it is a floor with mandate semantics: a gateway server may set a stricter
minProtocolVersionof its own, but a weaker one is raised to the mandated version and reported as aTLS001advisory on the gateway. A server that sets nothing inherits the mandate. Sidecars in the same namespaces are configured through a generatedEnvoyFilteron their inbound TLS-terminating filter chains, since Istio has no per-namespace API for this —meshConfig.meshMTLS.minProtocolVersionis per-istiod and cannot express a per-workspace floor.TLS that a proxy does not terminate is unaffected. A gateway server in passthrough mode matches on SNI and forwards the encrypted bytes, so the backend, not the gateway, negotiates the version.
ISTIO_MUTUALservers are managed by Istio's own mTLS stack and left alone, and aDIRECTmode server in a TLS mode Istio cannot honor is reported as aTLS002advisory rather than silently ignored. Client-side TLS originated towards an upstream is out of scope, as Istio exposes no minimum version for it.TLSV1_0andTLSV1_1are accepted and applied like any other version. Both are below the TLS 1.2 that Istio's mesh mutual TLS uses, so on sidecars they lower the floor rather than raise it, exactly as they do on a gateway server.TLS_AUTO, the default, applies no floor.Workloads in ambient mode are not configured: ztunnel and the waypoint HBONE path already negotiate TLS 1.3 exclusively and do not read
EnvoyFilters. Gateways in ambient namespaces still take the floor, because they terminate TLS like any other gateway.Enforcement is gated by the
ENABLE_MIN_TLS_VERSIONfeature flag on XCP Edge, which is disabled by default and can be set throughControlPlane.spec.components.xcp.kubeSpec.deployment.env. -
Fixed an issue where deleting configuration objects at the same time as related objects could intermittently fail with an internal error. Deleting a configuration object, together with the objects beneath it, is now retried automatically when it conflicts with a concurrent delete. A conflict that retrying cannot resolve is now reported as a retryable error rather than an internal one, so a client can tell it apart from a genuine server failure.
-
The algorithm and key size used to generate JWT signing keys are now configurable.
Service account key pairs are generated according to the new
spec.serviceAccountKeySettingsfield on theOrganizationresource:spec:
serviceAccountKeySettings:
algorithm: RS256 # RS256 (default), ES256, ES384 or ES512
rsaKeySize: 3072 # 2048 (default), 3072 or 4096; RSA algorithms onlyThe settings only apply to keys generated after they are changed; existing service account keys are left untouched. The published JWKS now advertises the algorithm each key was generated with, and for EC keys includes the
crv,xandyparameters instead of the RSA ones.The size of the default IAM signing key the operator generates is configurable through the new
spec.tokenIssuer.jwt.issuers[].rsaKeySizefield on theManagementPlaneresource, which accepts the same values and likewise defaults to 2048. It applies to the RSA based algorithms (RS256,RS384,RS512,PS256,PS384andPS512) and must be left unset for the others, as they either derive the curve from the algorithm or do not use a key pair at all. -
Fixed an issue where disabling the SPM agent in the
ControlPlanecustom resource left itsspm-agent-daemonsetrunning. The control plane operator now prunes the DaemonSet once the component is disabled. After upgrading, any previously orphaned spm-agent DaemonSet will be removed on the next reconciliation. -
The operator admin server port is now configurable via the
operator.adminPortHelm value (default: 5555). For example:operator:
adminPort: 5556 -
The operator's Prometheus metrics and health-check ports are now configurable via the
operator.metricsPort(default: 8383) andoperator.healthPort(default: 9082) Helm values. This matters most underoperator.deployment.hostNetwork: true, where these ports are bound on the node itself rather than in the pod's network namespace. For example:operator:
metricsPort: 8384
healthPort: 9083 -
Fixed a resource leak in the management plane's embedded PostgreSQL auto-configuration service. When a subscriber disconnected while a secret change was being delivered to it, the underlying Kubernetes watch was never released, so management plane memory use grew each time a subscriber reconnected.
-
Fixed the management plane front Envoy returning a non-retryable
PermissionDeniedto clients during a brief window after a management plane rolling restart. When the external authorization (ext_authz) call to IAM transiently failed — for example while IAM was momentarily unreachable during the restart — the filter failed closed with the default403/PermissionDenied. It now setsstatus_on_errorto503/ServiceUnavailableon a transport error to IAM, which surfaces to gRPC clients (such astctl) as a retryableUnavailableinstead. Requests are still denied on a genuine authorization failure (failure_mode_allowremains disabled). -
Gateway authorization rules now declare what happens when they match, through the new
actionfield onLocalAuthzRule. AnALLOWrule permits the request, which is what rules did before and remains the default. ADENYrule refuses it whatever the allow rules say. AnAUDITrule records it and takes no part in the decision, so naming a path in an audit rule does not make it reachable. -
Authorization rules also gained
whenconditions, matching an attribute of the request such asrequest.auth.claims[group], and the negated operationsnotPathsandnotMethods. -
Added new experimental dynamic module extension point to the gateway, which allows users to load and run Envoy dynamic modules in the gateway data path.
Enable it by annotating the gateway install with
install.tetrate.io/tetrate-dynamic-modules: "true", which mounts the Tetrate-provided modules in the gateway container and makes them available to the Envoy process. To mount a different build of those modules, setinstall.tetrate.io/tetrate-dynamic-modules-imageto the image to use; it is ignored unless the modules are mounted. -
Added new Lua extension point to the gateway, which allows users to load and run Lua scripts in the gateway data path.
-
Added an optional
validationKeysfield to the JWT issuers configured on theManagementPlaneresource. It names an entry in the signing keys secret holding a JWK Set of keys that are only ever used to validate tokens, never to sign them. Tokens are accepted when they are signed by the issuersigningKeyor by any of the validation keys, so a signing key can be replaced while the tokens it already signed keep working. Both files are watched for changes, so a pod restart is not needed if they keys change. -
Fixed the elliptic curve of the default IAM signing key generated for the
ES384andES512issuer algorithms. The operator generated a P-256 key for all the ECDSA algorithms, while RFC 7518 mandates P-384 forES384and P-521 forES512. TSB accepted the resulting tokens, but third party verifiers that enforce the curve rejected them. The operator now generates a key on the curve the configured algorithm mandates.The key is only generated when the signing key secret does not exist, so management planes that already run an
ES384orES512issuer keep their P-256 key until it is regenerated. To pick up the fix, delete theiam-signing-keysecret in the management plane namespace and let the operator create it again. Note that this invalidates the tokens signed with the previous key. -
Added an optional
rotationPeriodfield to the JWT issuers configured on theManagementPlaneresource. When set, the operator replaces the IAM signing key it generates every period, without invalidating the tokens the previous key signed. Each rotation runs in two steps: the new key is first published in the issuervalidationKeys, where it is accepted but not used to sign, and it takes over as the signing key oncerotationPropagationDelay(30m by default) has passed and everything validating these tokens has picked it up. The key it replaces is retired into the validation keys, and dropped from them by the first rotation that starts oncerefreshExpiration, plus a day of leeway, has passed and no token it signed can still be presented. -
Fixed the Management Plane IAM server flooding its logs when a control plane agent, such as
oap-agentorotel-collector, keeps retrying with an expired token. Identical authentication failures for the same principal are now logged at most once per minute, and the log line reports how many further failures were suppressed since the previous one. The suppressed failures are still logged individually atdebuglevel.A new
iam_authenticate_failures_totalmetric counts every rejected authentication request, labelled byprincipaland byreason, so that agents whose token has expired can be identified with thereason="expired_token"series without relying on the logs. -
Bumped
kube-rbac-proxyfromv0.15.0-tetrate-v28tov0.15.0-tetrate-v29to fix:- GHSA-hrxh-6v49-42gf: bumped
google.golang.org/grpcfrom v1.82.1 to v1.83.2 - CVE-2026-56854: bumped
golang.org/x/cryptofrom v0.53.0 to v0.55.0 - CVE-2026-46600: bumped
golang.org/x/netand Go toolchain from 1.26.5 to 1.26.7
- GHSA-hrxh-6v49-42gf: bumped
-
Fixed a defect that caused a configuration write and the authorization graph update belonging to it to be committed as two independent database transactions rather than one. A failure between the two could leave a resource stored without its corresponding authorization state, or the reverse, until the resource was next updated. The two now always commit or roll back together.
-
Fixed a performance regression, introduced in 1.14.1, that made creating users progressively slower as the number of users in the system grew. Each user creation checked its login name for conflicts by reading every user in every organization, so synchronizing a large team directory took time proportional to the square of the number of users and could fail to complete. Login name conflict checks now use a database index. Resolving a user by login name during authorization no longer reads the full user table either.
-
The Management Plane Controller (MPC) now connects to the TSB (bridge) gRPC service directly and verifies the peer certificate against the management plane's internal CA. Previously MPC reached TSB through front-envoy over a TLS connection whose certificate was not verified. To disable certificate verification, set
VERIFY_INTERNAL_TRAFFICtofalseon the management plane operator. -
Improved TSB API performance on large service registries, both for read operations such as listing services and for registering new services.
The compiled authorization decision engine keys its read-model by graph version and discards it whenever the graph changes, so a write could trigger a whole-graph rebuild on the next authorization decision. That rebuild is now deferred until the graph has been unchanged for a short window. Authorization decisions made while a rebuild is deferred are served by the previous decision engine, which reads the live graph, so they remain correct and observe every write that has landed.
The debounce window defaults to 1 second and can be tuned:
--q-compiled-rebuild-debounceon the TSB API server, or- the
TSB_Q_COMPILED_REBUILD_DEBOUNCEenvironment variable, which can be set alongside the existing container configuration without editing CLI arguments.
Both take a Go duration such as
250msor2s. A value of0rebuilds the read-model on the next decision after any change. An unparseable or negative environment value is ignored and logged, and the default is used.Two metrics report the tuning's health. A graph that mutates continuously faster than the debounce window never rebuilds its read-model, which forfeits the read improvement:
pdp_compiled_rebuild_deferralcounts how often the graph starts changing continuously.pdp_compiled_rebuild_deferred_secondsreports how long the current continuous episode has lasted. Sustained growth in this gauge means the debounce window should be lowered.
This release also upgrades the NGAC library to v0.1.3, which further reduces authorization traversal cost, and upgrades
google.golang.org/grpcfrom 1.81.1 to 1.82.1 as a required dependency of that upgrade. -
Improved the consistency of the management plane's in-memory authorization cache by resolving read-after-write concurrency issues, so recently committed permission and policy changes are immediately reflected in subsequent authorization decisions.
-
Added support for the
SameSiteattribute on OIDC authentication cookies via the newcookieConfig.sameSitefield inOIDCConfig. The value is applied to all cookies the proxy sets during the OIDC flow. Defaults to unset (the attribute is not added). -
The operator deployment can now run with
hostNetwork: truevia theoperator.deployment.hostNetworkHelm value, instead of requiring a manualkubectl patchafter install. This is needed on some CNIs (e.g. Cilium with kube-proxy replacement on EKS) where the API server cannot reach pod IPs directly for admission webhooks. When enabling this, also setoperator.webhookPortandoperator.adminPort(andoperator.metricsPort/operator.healthPort, see below) to ports that don't conflict with other host-networked components. The deployment strategy defaults toRecreatewhenhostNetworkis enabled (unlessoperator.deployment.strategyis set explicitly), since a surging RollingUpdate pod can otherwise land on the same node as the pod it's replacing and fail to bind the still-held host ports. Note thathostNetwork: trueis rejected by Pod Security Admission at thebaseline/restrictedlevels, so the operator's namespace must permit host namespaces when enabling this. For example:operator:
deployment:
hostNetwork: true
webhookPort: 9444
adminPort: 5556 -
Added
priorityClassNameandpodDisruptionBudgetsupport to theoperator.deploymentHelm values for the ControlPlane and ManagementPlane operator charts, and a newoperator.hookInitFinalizervalues block for the pre-deletehook-init-finalizerJob, supportingpriorityClassName,podSecurityContext,containerSecurityContext,tolerations,nodeSelector, andresources. All fields are optional and disabled by default. -
Added a new metric,
ngac_pdp_decision_source, reporting which backend served each management plane authorization decision. Thedecision_sourcelabel separates decisions answered from the in-memory authorization cache (runtime_cache) from those served by the database (transactional_dbfor decisions made inside a transaction, anddisabled_db_base/disabled_db_txwhen the cache is turned off), and themethodlabel identifies the authorization operation. Together these make it possible to confirm the cache is serving traffic and to pinpoint which operations are responsible when it is not, since database-served decisions are the main contributor to authorization latency. Decisions not served from the cache are additionally logged at debug level. -
Profile resolution now treats wildcard hosts (
*and*.example.com) in anUpstreamTrafficSettingslist as defaults that propagate across the profile hierarchy. Previously each host was treated as an independent inheritance path keyed by exact match, so a wildcard set had no effect if there was a more specific host (e.g.app1.foo.com) at the same or a lower level. Behavior is now:- A wildcard at a more-specific hierarchy level (e.g. Workspace) overrides values set on matching specific hosts at less-specific levels (e.g. Tenant or Organization), consistent with "more-specific profile wins" precedence for defaults.
- The same wildcard precedence applies to mandates with inverted direction:
a mandate
*set at a broader hierarchy level (e.g. Organization) now overrides mandated specific hosts at narrower levels (e.g. Tenant), consistent with the "broadest mandate wins" precedence logic for mandates. - When a new specific host first appears at any hierarchy level, it inherits
the settings of the most specific matching wildcard already in scope (e.g.
app1.foo.cominherits from*.foo.com, which itself inherits from*). - Within a single profile, a specific host still overrides a wildcard for the same field, so existing single-profile configurations are unaffected.
-
The kubectl image has been removed from the TSB release. The Helm pre-delete hook that previously ran
kubectl delete deploymentto trigger the operator's finalizer cleanup now uses the existingtsboperator-jobsimage with a newuninstall-triggerjob. -
Rotating service account keys is now easier with
tctl service-account. Service accounts accept tokens signed by any of their keys until those keys are revoked, allowing you to add and install a replacement key before revoking the old one:- Service account keys now record when they were added, returned as
createdAton each key. Keys added before this release have no creation time. - Added a
list-keyscommand to show all keys in a service account, and how long since the key was added. - The
revoke-keycommand now accepts--idmultiple times, so you can revoke all replaced keys in a single command. - If a
revoke-keycall would revoke every key in a service account, it is refused unless--forceis given. - Revoking a key a service account does not have is reported and skipped, so
revoke-keycan be repeated safely.
- Service account keys now record when they were added, returned as
-
The ControlPlane now supports a per-revision
strictClientMtlssetting that enforces strict mTLS on the client side across clusters. When enabled, sidecars in that revision originateISTIO_MUTUALto all mesh-internal upstreams unless an explicitDestinationRuletlssetting opts out, closing the plaintext fallback that Istio auto-mTLS leaves open for non-injected and cross-cluster destinations.Enabling it also lets XCP Edge stop generating the per-host force-mTLS
DestinationRules it used to create forauthentication.trafficMode: REQUIREDhosts, which significantly reduces the number ofDestinationRules in large meshes.The setting is per revision, so it can be rolled out one revision at a time.
Note that non-injected or plaintext-only workloads fail closed once this is enabled. Exempt them by marking them
authentication.trafficMode: OPTIONAL, which makes XCP Edge emit atls: DISABLEDestinationRulefor them.Example configuration:
spec:
components:
xcp:
isolationBoundaries:
- name: global
revisions:
- name: default
istio:
strictClientMtls: true -
Fixed an issue where TSB component tokens were not rotated promptly after
spec.components.iamServer.tokenLifeTimewas lowered on theManagementPlaneresource. Previously a token minted with a longer lifetime (for example 30 days) was not rotated until it neared its original expiration, even if the lifetime had since been reduced (for example to 1 day). The IAM server now also rotates any token that was minted with a longer lifetime than is currently configured, so a reduction takes effect when the IAM server restarts to pick up the new value.Note that
tokenRefreshBeforemust be shorter thantokenLifeTimeand defaults to 24 hours, so loweringtokenLifeTimeto 24 hours or less requires loweringtokenRefreshBeforein the same change, otherwise the IAM server fails validation on startup:spec:
components:
iamServer:
tokenLifeTime: 86400s # 1 day
tokenRefreshBefore: 3600s # 1 hour -
Fixed an issue where cross-cluster traffic originating from ambient workloads was rejected by the receiving cluster with
RBAC: access denied(HTTP 403) when the east-west gateway was deployed in a namespace that contained no ambient services of its own. Such traffic is forwarded by theglobal-waypointin thexcp-multiclusternamespace and therefore arrives under the global waypoint's identity rather than the client's, so it is admitted by theallow-gateway-ambientauthorization policy. That policy was only generated for namespaces that hosted ambient services, leaving a gateway-only namespace without it. It is now generated for those namespaces as well. Traffic from sidecar workloads was not affected, since it reaches the gateway under the application's own identity. -
Fixed a spurious translation error reported for ambient namespaces when a
Workspacedefines adefaultTrafficSetting. XCP Edge unconditionally attached an empty (but non-nil) legacyResilienceblock to every workspace-level default traffic setting, which the legacy-to-new API conversion then expanded into a phantom catch-allupstreamTrafficSettingsentry (hosts: ["*"]) carrying no user-provided content. In a namespace containing ambient services this phantom entry tripped the "upstream traffic setting is not supported in ambient mode" check, marking that namespace's configERRORED-- and therefore the aggregatedWorkspacestatusERROREDat XCP Central -- even though no upstream traffic setting was ever configured and all generated Istio config was still applied correctly on every target cluster. XCP Edge now carries the legacyResilienceblock forward only when it is actually set, so no phantomupstreamTrafficSettingsentry is produced and ambient namespaces are no longer falsely reported as errored. -
XCP Edge now removes the automatically provisioned ambient waypoint from a namespace when that namespace's
istio.io/dataplane-mode=ambientlabel is removed and no ambient-mode workloads remain. Previously the waypoint Gateway (and its backing Deployment and Service) kept running indefinitely after the label was removed, because any Service whose endpoints appeared while the namespace was ambient — including the waypoint's own Service — was incorrectly counted as an ambient workload keeping the waypoint alive. -
XCP Central now exposes a
/liveliveness endpoint on the monitoring port, and thecentralDeployment is configured with a KuberneteslivenessProbepointing at it. The endpoint dials Central's own relay gRPC port and invokes the standard gRPC health service, so a Central instance that stops accepting or answering connections from MPC/edges is automatically restarted by the kubelet. -
Upgraded the composer image to
v0.9.0-3(v0.9.0-3-fipsfor FIPS builds).Note that the FIPS composer image supports only a subset of the official composer's capabilities. In particular, the
goplugin-loader(loading a dynamic.soplugin from a URL, e.g. viaoci://orfile://) is not supported in FIPS builds; only parts of composer's built-in plugins are available there like thecoraza-wafplugin. -
Added Envoy dynamic module support to gateway
Extensionswhich could be used to load dynamic modules in the TSB. Use theinstall.tetrate.io/tetrate-dynamic-modulesannotation to enable dynamic module support for the gateway and thecomposermodule will be supported by default. -
Fixed cross-cluster failover failing with a TLS error (
SSL certificate validation failed) for a service that runs in the local cluster and is also reachable on remote clusters through an east-west gateway, when the service is configured with required mTLS (aTrafficSettingwithauthentication.trafficMode: REQUIRED). Previously, once the local endpoints were taken out of rotation (for example by outlier detection), requests that failed over to a remote cluster were rejected because the client did not accept the remote cluster's workload identity. XCP Edge now accepts the workload identities of all clusters that back the service, and applies the service's configured locality failover, so cross-cluster failover completes over mTLS as expected. The fix is enabled by default; it can be turned off per cluster by setting the environment variableENABLE_NON_INJECTED_MTLS_EAST_WEST_ALIGNMENTtofalseon XCP Edge. -
Egress gateways no longer report a spurious
port-protocol conflictwhen two TCP servers with different hostnames are exposed on the same port. Egress traffic reaches the gateway on its mTLS port and is routed by hostname, so the port is not a listener binding and the configuration is valid; previously it was rejected on apply, or accepted but reported as errored. SettrafficMode: EGRESSon the TCP server for the check to be skipped when the Gateway is validated on apply. Two egress servers that share both a hostname and a port are still rejected, as are non-egress TCP servers that share a port. -
Gateway authorization rules now declare what happens when they match, with a new
actionfield.ALLOWis the default and is what rules did before,DENYrefuses a request whatever the allow rules say, andAUDITrecords a request without taking any part in the decision.A
DENYorAUDITrule that names no operation of its own applies to the server that declares it, rather than to every host on the gateway. Allow rules are unchanged.An
AUDITrule never permits anything, so naming a path in one does not expose it. The rule name is reported by the proxy on the access log record, so a record identifies the rule that selected it.Rules also gained
whenconditions and negation, matching what Istio authorization already expresses.action,whenand negation apply to a gateway server'smeshExternalAuthzrules; the mesh-internal rule lists carry only paths and methods to the proxy, so they reject these fields rather than drop them.authorization:
meshExternalAuthz:
inline:
rules:
- name: eng-only # action defaults to ALLOW
from: [{ jwt: { iss: "…", sub: "eng" } }]
to: [{ paths: ["/api*"] }]
- name: block-legacy-clients
action: DENY
when:
- key: "request.headers[user-agent]"
values: ["legacy-sdk/*"]
- name: privileged-paths
action: AUDIT
to: [{ paths: ["/admin*", "/actuator*"] }]
- name: anonymous-writes
action: AUDIT
to: [{ methods: ["POST"], notPaths: ["/health"] }]
when:
- key: "request.headers[authorization]"
notValues: ["*"]A
whenkey addresses any attribute Istio authorization understands, including HTTP/2 pseudo headers, sorequest.headers[:authority]andrequest.headers[:path]work, and unliketo.pathsthe latter includes the query string. A rule is always scoped to the server that states it, by that server's own host and port, so a condition onrequest.headers[:authority]narrows a rule within its server rather than widening it across the gateway - on a server whose hostname is*it matches what it says, and on one with a specific hostname a different host matches nothing.Values match exactly, by prefix with a trailing
*, by suffix with a leading*, or with a single*requiring the attribute to be present. -
Added a new
alpnProtocolsfield to the server TLS settings. When set, the listed protocols replace the ALPN protocols the gateway would otherwise advertise during the TLS handshake (h2andhttp/1.1for HTTP servers), so a server can be made to advertise HTTP/1.1 only for clients that negotiate HTTP/2 but cannot use it. Any protocol identifier the gateway is able to serve may be listed. -
Added a
kubeSpec.deployment.terminationGracePeriodSecondsfield to the gateway install resources. It sets the pod-level termination grace period directly, removing the need for a customDeploymentoverlay when the proxy is configured to drain for longer than the default.When unset, the grace period continues to default to the connection drain duration plus 5 seconds (
connectionDrainDuration + 5s). When set, it should be greater than or equal to the proxy's drain duration so in-flight connections can finish draining before Kubernetes force-kills the pod. -
Fixed an issue where a gateway routing rule that rewrote only the URI (
routing.rules[].modify.rewrite.uriwithoutauthority) and routed to aclusterDestinationlost the URI rewrite in the generatedVirtualService, which ended up with the default authority rewrite only. The default authority rewrite to the server hostname now preserves the configured URI rewrite. -
XCP Edge now compresses its gRPC traffic to XCP Central with gzip (cluster state uploads, and — since the server responds with the same codec — config and cluster state pushes back to the edge). Measured on a 1,000-service cluster state, this reduces bytes on the wire by ~21x for full cluster state pushes with no measurable CPU or memory cost at Central. Controlled by the new
ENABLE_GRPC_COMPRESSION_TO_CENTRALenvironment variable on XCP Edge (defaulttrue). XCP Central must be upgraded before Edges (the standard upgrade order); set the variable tofalseon Edges that must connect to an older Central. -
Added Lua script support to gateway
Extensions, alongside the existing Composer extension. Lua plugins can now be attached at both the HTTP server and route level viaextensions.lua.plugins[](each with aname, optionalpriority, andinlineCode). They are translated into anEnvoyFilterthat wires up the native Envoy Lua HTTP filter (envoy.filters.http.lua) using the same filter-chain generation pattern as Composer: filters are inserted disabled and enabled per virtual host (server-level plugins) or per route (route-level plugins). Unlike Composer, Lua requires no additional sidecar. -
Added
authenticationSettings.minProtocolVersiontoSecuritySetting, allowing a minimum TLS protocol version to be configured once per workspace or security group and enforced across both data plane roles in the namespaces it selects: on every IstioGatewayserver generated for those namespaces, and on the sidecars running in them. The gateway half applies in bothBRIDGEDandDIRECTconfig mode, so a workspace that mixes the two gets a single, uniform TLS floor.The value acts as a floor with mandate semantics: a gateway may configure a stricter
minProtocolVersion, but a weaker one is raised to the mandated version and reported as aTLS001advisory on the gateway. A server that configures nominProtocolVersionat all — the usual shape for a hand-writtenDIRECTmodeGateway— inherits the mandated version.TLS_AUTO(the default) applies no floor. A server that pinsmaxProtocolVersionbelow the mandated minimum — for example a gateway pinned to "TLS 1.2 exactly" meeting aTLSV1_3mandate — has its maximum raised to the mandated version as well, reported as aTLS003advisory: raising only the minimum would leavemin > max, an invalid range that Envoy rejects. A server whose own range is already invalid without the mandate's involvement is left as written.The floor only applies where the gateway terminates TLS, because a minimum protocol version is a property of a TLS handshake the gateway itself performs. Concretely, only
SIMPLE,MUTUALandOPTIONAL_MUTUALservers are affected — these are the modes for which Istio builds a downstream TLS context and programs a minimum version onto it. A TLS passthrough server never completes a handshake: it matches on SNI and forwards the encrypted bytes to the backend, which is the peer that negotiates the version, so there is no version for the gateway to enforce and Istio programs no TLS context for it at all.ISTIO_MUTUALservers are likewise unaffected, as Istio's own mTLS stack chooses the protocol versions there.This is why the setting is expressed on the
httpandtcpserver blocks of anIngressGateway, both of which terminate TLS, and not on thetlsblock: aTLSServercarries no TLS settings by construction, precisely because it does not terminate TLS. For the same reason a plaintext server is never given TLS settings just to carry a floor. ADIRECTmode server in a TLS mode Istio cannot honor is reported as aTLS002advisory rather than silently left at Envoy's default, and the mesh-internalISTIO_MUTUALservers XCP adds toDIRECTmode gateways are managed by Istio and left untouched.The floor also applies only to the inbound (server) side. Istio exposes no field for a minimum TLS version on the client side of a connection, so TLS originated by a gateway towards an upstream is out of scope.
Sidecars in the selected namespaces are configured through a namespace-wide
EnvoyFilternamedmin-tls-version-sidecar, which merges the minimum version into the downstream TLS context of the sidecar's inbound filter chains. Istio has no per-namespace API for this —meshConfig.meshMTLS.minProtocolVersionis per-istiod, so it cannot express a per-workspace floor — which is why a generatedEnvoyFilteris used rather than a mesh-wide setting. Sidecars accept the same set of versions as gateway servers,TLSV1_0andTLSV1_1included. Those two are below the TLS 1.2 that Istio's mesh mutual TLS uses, so they lower the sidecar floor rather than raise it, exactly as they do on a gateway server. Namespaces running in ambient mode are skipped without an error: ztunnel and the waypoint HBONE path already negotiate TLS 1.3 exclusively and do not readEnvoyFilters, so a workspace that mixes sidecar and ambient namespaces still gets the floor everywhere it can be applied.The same termination-only rule holds on the sidecar side, and it is what makes a single filter safe under either
PeerAuthenticationmode. The filter matches inbound filter chains on bothtransportProtocol: tlsandtransportSocket: envoy.transport_sockets.tls. The second condition is the load-bearing one:transportProtocolalone is ambiguous, because inPERMISSIVEmode Istio emits two TLS-matching inbound chains — the mTLS one, and a "plain TLS" one that hands the encrypted bytes to the application and therefore has no transport socket at all. Matching on the transport socket selects the chains that actually terminate TLS, so the same filter is correct underSTRICTandPERMISSIVEalike and can never graft a TLS transport socket onto a chain that was passing bytes through. This requires Istio=
1.28.9-a734f7aef8, which addstransportSocketto theEnvoyFilterfilter chain match.This is gated by the
ENABLE_MIN_TLS_VERSIONfeature flag on XCP Edge, which is disabled by default. -
Fixed an issue where the OIDC authorization-code token exchange could fail with a
protocol_erroragainst an HTTP/2-capable identity provider. The generated upstream cluster for the OIDC token/JWKS endpoints advertisedh2in TLS ALPN but only spoke HTTP/1.1; it now advertiseshttp/1.1only, matching the cluster's protocol. -
XCP Edge now sets
XCP_PILOT_ENABLE_PEER_EXCHANGE_ALPN_OUTBOUNDon istiod for ambient-enabled revisions, which advertises theistio-peer-exchangeALPN on sidecar outbound HTTP mTLS connections. This lets a sidecar client send its identity to a destination east-west gateway running as an L4 proxy: the gateway never terminates the HTTP request, so previously the client could only learn its upstream peer when the destination echoed one back, which an ambient destination served by ztunnel cannot do. Access logs now record the correct client identity, and the Skywalking topology is correct for these cross-cluster requests. -
Install resources that configure two or more overlays targeting the same object in a single
overlayslist are now rejected at admission forIngressDeployment,EgressDeployment,Tier1DeploymentandGatewayDeployment. -
XCP Edge now sends its periodic config resync ping to XCP Central on every
config-resync-intervaltick, instead of skipping the ping whenever central had recently pushed configuration to the edge. Central records the arrival of each ping inxcp_central_last_config_propagation_event_timestamp_ms{type="config_resync_request"}, which is used to report a cluster's last sync time, so clusters receiving frequent configuration updates could report a stale last sync time and spuriously trigger the resync alert while being perfectly healthy. -
Fixed the
timeoutof a Gateway HTTP routing rule being overridden by therequestTimeoutofupstreamTrafficSettings. The EnvoyFilter generated fromupstreamTrafficSettingsmatches on the virtual host alone, so itsrequestTimeoutreached every route of that host. XCP now generates an additional EnvoyFilter, namedroute-level-<gateway>, that re-applies each route's own timeout after those filters. Routes that do not configure a timeout keep inheriting the one fromupstreamTrafficSettings. -
Fixed TLS handshakes failing on a shared ingress gateway listener that an application team's
Gatewayconfigured withtls.mode: MUTUALortls.mode: OPTIONAL_MUTUALand a cross-namespacetls.secretName. In these modes Istio validates client certificates against a CA certificate that it requests as a separate SDS resource, named after the server certificate Secret with a-cacertsuffix. XCP Edge only generated aReferenceGrantfor the Secret named in theGateway, so the derived<secret>-cacertresource stayed unauthorized and the listener was left with a warming, invalid certificate. XCP Edge now also generates a<secret>-cacert-sgw-secret-grantReferenceGrantfor such listeners, which removes the need to apply that grant manually.Also fixed the name of a generated
ReferenceGrantexceeding the maximum length of a Kubernetes object name when the referenced Secret has a very long name, which made theReferenceGrantimpossible to apply. Such names are now shortened, and remain unique. Names that already fit are unchanged. -
Added a new
strictClientMtlsfield to each revision underEdgeXcp.spec.isolationBoundaries[].revisions[].istio. When enabled, the revision's istiod is configured to make sidecars originate mTLS (ISTIO_MUTUAL, fail-closed) to all mesh-internal upstreams unless an explicitDestinationRuletlssetting opts out. This is the client-side complement to the server-sidePeerAuthenticationSTRICT that XCP already generates forauthentication.trafficMode: REQUIRED, and it closes the plaintext fallback that Istio auto-mTLS leaves open for non-injected and cross-cluster destinations, allowing strict mode to be enforced across clusters. As a result, XCP Edge no longer generates the per-host force-mTLSDestinationRules it previously created fortrafficMode: REQUIREDnon-injected hosts (istiod now enforces client mTLS mesh-wide); it instead generatestls: DISABLEDestinationRules only fortrafficMode: OPTIONALhosts so they keep their plaintext fallback, reducing the number of DestinationRules in the cluster. AtrafficMode: REQUIREDhost nested inside atrafficMode: OPTIONALwildcard keeps its force-mTLSDestinationRule, so that the broadertls: DISABLErule does not apply to it. Warning: non-injected or plaintext-only workloads that are not markedtrafficMode: OPTIONALwill fail closed. The DestinationRule reduction can be turned off with theENABLE_STRICT_CLIENT_MTLS_DR_REDUCTION=falseenvironment variable on XCP Edge (istiod still enforces mTLS regardless, so this only affects DestinationRule volume, not security). -
Added support for the
SameSiteattribute on OIDC authentication cookies via the newcookieConfig.sameSitefield inOIDCConfig. The value is applied to all cookies the proxy sets during the OIDC flow. Defaults to unset (the attribute is not added). -
Ambient:
components.istio.ambient.waypoints.namespaceLevel[].selectoris now implemented. A namespace-level waypoint configuration can target namespaces by their labels instead of by name, e.g. to enable waypoints for every namespace labelledapp: frontendwithout enumerating them. All labels underselector.labelsmust match for a namespace to be selected, and an empty selector is rejected. When a namespace matches both anameentry and aselectorentry, thenameentry wins. Amongselectorentries, the first match in list order wins.
Packages in 1.14.4000
This release is made up of the following container images:
containers.dl.tetrate.io/aws-controller:1.14.4000
containers.dl.tetrate.io/bridge-migration:1.14.4000
containers.dl.tetrate.io/bridge-server:1.14.4000
containers.dl.tetrate.io/cert-manager-cainjector:v1.16.1-tetrate-v29
containers.dl.tetrate.io/cert-manager-controller:v1.16.1-tetrate-v29
containers.dl.tetrate.io/cert-manager-webhook:v1.16.1-tetrate-v29
containers.dl.tetrate.io/coraza-proxy-wasm:0.6.0
containers.dl.tetrate.io/eck-operator:2.11.1-tetrate-v26
containers.dl.tetrate.io/elasticsearch:8.19.19-tetrate-v2
containers.dl.tetrate.io/genistio-watcher:1.14.4000
containers.dl.tetrate.io/host-management-plane-server:v0.0.0-a8651a8
containers.dl.tetrate.io/iam-jwt:1.14.4000
containers.dl.tetrate.io/iam-server:1.14.4000
containers.dl.tetrate.io/install-cni:1.28.9-8fca9cd4b6-distroless
containers.dl.tetrate.io/kube-rbac-proxy:v0.15.0-tetrate-v29
containers.dl.tetrate.io/kubegres:v1.16.0-tetrate-v42
containers.dl.tetrate.io/mpc-server:1.14.4000
containers.dl.tetrate.io/n2ac-cluster-server:0.1.31
containers.dl.tetrate.io/nc-cmd:1.14.4000
containers.dl.tetrate.io/ngac-agent:0.1.3
containers.dl.tetrate.io/ngac-server:0.1.3
containers.dl.tetrate.io/oapr3:6a81a7b
containers.dl.tetrate.io/onboarding-agent:1.14.4000
containers.dl.tetrate.io/onboarding-operator-server:1.14.4000
containers.dl.tetrate.io/onboarding-package-istio-sidecar:1.28.9-8fca9cd4b6
containers.dl.tetrate.io/onboarding-plane-server:1.14.4000
containers.dl.tetrate.io/onboarding-repository-server:1.14.4000
containers.dl.tetrate.io/otelcol:0.131.1
containers.dl.tetrate.io/otelcol:0.159.0
containers.dl.tetrate.io/package-repository-server:1.14.4000
containers.dl.tetrate.io/pilot:1.28.9-8fca9cd4b6-distroless
containers.dl.tetrate.io/pkg-host-management-agent:v0.0.0-a8651a8
containers.dl.tetrate.io/pkg-service-discovery-agent:v0.0.0-f24abe2
containers.dl.tetrate.io/pkg-spm-agent:7a22e6ff34948420493c8e5b2b4c3bf241a84964
containers.dl.tetrate.io/postgres-exporter:v0.18.1-tetrate-v12
containers.dl.tetrate.io/postgres:14.23-alpine3.22-tetrate-v6
containers.dl.tetrate.io/proxyv2:1.28.9-8fca9cd4b6-distroless
containers.dl.tetrate.io/proxyv2:1.28.9-8fca9cd4b6-pv-distroless
containers.dl.tetrate.io/ratelimit:2272ca2c
containers.dl.tetrate.io/redis:8.4.0-alpine3.22
containers.dl.tetrate.io/satellite:v833c337387bc398a62198f2ec6dcfd8623560fc3
containers.dl.tetrate.io/service-discovery-plane-server:v0.0.0-f24abe2
containers.dl.tetrate.io/spm-agent:7a22e6ff34948420493c8e5b2b4c3bf241a84964
containers.dl.tetrate.io/spm-central:e09e50e36f7051fa2102f4f3e4fcc4a942a29584
containers.dl.tetrate.io/spm-user:e09e50e36f7051fa2102f4f3e4fcc4a942a29584
containers.dl.tetrate.io/swck:5d22131-go1.26.6-v0
containers.dl.tetrate.io/tcc-web-ui:1.14.4000
containers.dl.tetrate.io/tctl:1.14.4000
containers.dl.tetrate.io/teamsync-job:1.14.4000
containers.dl.tetrate.io/tetrate-openldap:2.6.13-alpine3.23-tetrate-v2
containers.dl.tetrate.io/tsboperator-jobs:1.14.4000
containers.dl.tetrate.io/tsboperator-server:1.14.4000
containers.dl.tetrate.io/wasmfetcher-server:1.14.4000
containers.dl.tetrate.io/xcp-operator:v1.14.62000
containers.dl.tetrate.io/xcpd:v1.14.62000
containers.dl.tetrate.io/ztunnel:1.28.9-8fca9cd4b6-distroless
Outstanding CVEs
CVE-2026-5450, CVE-2026-8376, CVE-2026-39821, CVE-2026-58062, CVE-2026-59638, CVE-2026-59650, CVE-2026-8763, CVE-2026-12087, CVE-2026-42496, CVE-2025-47273, CVE-2026-14662, CVE-2026-14664, CVE-2026-14669, CVE-2026-14670, CVE-2026-14671, CVE-2026-14677, CVE-2026-14680, CVE-2026-15741, CVE-2026-15742, CVE-2026-16238, CVE-2026-16239, CVE-2026-18408, CVE-2026-19385, CVE-2026-72642, CVE-2026-72649, CVE-2026-12802, CVE-2026-12803, CVE-2026-12816, CVE-2026-12817, CVE-2026-12852, CVE-2026-12860, CVE-2026-13506, CVE-2026-14682, CVE-2026-58059, CVE-2026-58060, CVE-2026-58061, CVE-2026-59639, CVE-2026-59640, CVE-2026-59641, CVE-2026-59642, CVE-2026-59643, CVE-2026-59644, CVE-2026-59645, CVE-2026-59646, CVE-2026-59649, CVE-2026-75140, CVE-2026-84304, CVE-2026-84445, CVE-2026-23949, CVE-2026-11822, CVE-2026-11824, CVE-2026-14679, CVE-2026-86145, CVE-2026-14668, CVE-2026-6464, CVE-2026-41992, CVE-2026-42497, CVE-2026-48959, CVE-2026-56854, CVE-2026-56855, CVE-2026-57585, CVE-2026-5928, CVE-2026-78662, CVE-2026-89161, CVE-2026-48961, CVE-2026-48962, CVE-2026-6471, CVE-2026-12185, CVE-2026-59651, CVE-2026-7017.