Skip to main content
logoTetrate Service BridgeVersion: 1.14.x

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 INSTALLED after 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 INSTALLING phase 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 set INSTALLED. The operator now requeues these still-converging conditions on a short, bounded interval and caps its worst-case requeue backoff, so the install reaches INSTALLED shortly after the last component becomes ready.

  • The management plane OTel collector now forwards the control plane's xcp_edge_gateway_exposed_hosts metric 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-openldap image 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 List with 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.AddServiceAccountKey API registers a public key, in JWK format, against an existing service account, and tctl service-account add-key <name> --key-path <public key file> does the same from the command line.

    Teams.CreateServiceAccount also takes an optional publicKey, as does tctl 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.CreateCluster takes the same optional publicKey for 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 without secrets.clusterServiceAccount.JWK and 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 keyManagement field of a service account, TSB or CLIENT. 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 ServiceRoute with a portLevelSettings entry using trafficType: 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 with Required value. The configuration still reached TSB and propagated to the clusters, but the object never received the gitops.tetrate.io/finalizer and its status was never updated. The operator now writes finalizers with a patch scoped to metadata, leaving the stored spec untouched.

  • Added authenticationSettings.minProtocolVersion to SecuritySetting, 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 minProtocolVersion of its own, but a weaker one is raised to the mandated version and reported as a TLS001 advisory on the gateway. A server that sets nothing inherits the mandate. Sidecars in the same namespaces are configured through a generated EnvoyFilter on their inbound TLS-terminating filter chains, since Istio has no per-namespace API for this — meshConfig.meshMTLS.minProtocolVersion is 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_MUTUAL servers are managed by Istio's own mTLS stack and left alone, and a DIRECT mode server in a TLS mode Istio cannot honor is reported as a TLS002 advisory rather than silently ignored. Client-side TLS originated towards an upstream is out of scope, as Istio exposes no minimum version for it.

    TLSV1_0 and TLSV1_1 are 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_VERSION feature flag on XCP Edge, which is disabled by default and can be set through ControlPlane.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.serviceAccountKeySettings field on the Organization resource:

    spec:
    serviceAccountKeySettings:
    algorithm: RS256 # RS256 (default), ES256, ES384 or ES512
    rsaKeySize: 3072 # 2048 (default), 3072 or 4096; RSA algorithms only

    The 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, x and y parameters 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[].rsaKeySize field on the ManagementPlane resource, which accepts the same values and likewise defaults to 2048. It applies to the RSA based algorithms (RS256, RS384, RS512, PS256, PS384 and PS512) 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 ControlPlane custom resource left its spm-agent-daemonset running. 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.adminPort Helm 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) and operator.healthPort (default: 9082) Helm values. This matters most under operator.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 PermissionDenied to 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 default 403/PermissionDenied. It now sets status_on_error to 503/ServiceUnavailable on a transport error to IAM, which surfaces to gRPC clients (such as tctl) as a retryable Unavailable instead. Requests are still denied on a genuine authorization failure (failure_mode_allow remains disabled).

  • Gateway authorization rules now declare what happens when they match, through the new action field on LocalAuthzRule. An ALLOW rule permits the request, which is what rules did before and remains the default. A DENY rule refuses it whatever the allow rules say. An AUDIT rule 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 when conditions, matching an attribute of the request such as request.auth.claims[group], and the negated operations notPaths and notMethods.

  • 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, set install.tetrate.io/tetrate-dynamic-modules-image to 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 validationKeys field to the JWT issuers configured on the ManagementPlane resource. 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 issuer signingKey or 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 ES384 and ES512 issuer algorithms. The operator generated a P-256 key for all the ECDSA algorithms, while RFC 7518 mandates P-384 for ES384 and P-521 for ES512. 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 ES384 or ES512 issuer keep their P-256 key until it is regenerated. To pick up the fix, delete the iam-signing-key secret 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 rotationPeriod field to the JWT issuers configured on the ManagementPlane resource. 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 issuer validationKeys, where it is accepted but not used to sign, and it takes over as the signing key once rotationPropagationDelay (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 once refreshExpiration, 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-agent or otel-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 at debug level.

    A new iam_authenticate_failures_total metric counts every rejected authentication request, labelled by principal and by reason, so that agents whose token has expired can be identified with the reason="expired_token" series without relying on the logs.

  • Bumped kube-rbac-proxy from v0.15.0-tetrate-v28 to v0.15.0-tetrate-v29 to fix:

    • GHSA-hrxh-6v49-42gf: bumped google.golang.org/grpc from v1.82.1 to v1.83.2
    • CVE-2026-56854: bumped golang.org/x/crypto from v0.53.0 to v0.55.0
    • CVE-2026-46600: bumped golang.org/x/net and Go toolchain from 1.26.5 to 1.26.7
  • 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_TRAFFIC to false on 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-debounce on the TSB API server, or
    • the TSB_Q_COMPILED_REBUILD_DEBOUNCE environment variable, which can be set alongside the existing container configuration without editing CLI arguments.

    Both take a Go duration such as 250ms or 2s. A value of 0 rebuilds 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_deferral counts how often the graph starts changing continuously.
    • pdp_compiled_rebuild_deferred_seconds reports 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/grpc from 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 SameSite attribute on OIDC authentication cookies via the new cookieConfig.sameSite field in OIDCConfig. 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: true via the operator.deployment.hostNetwork Helm value, instead of requiring a manual kubectl patch after 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 set operator.webhookPort and operator.adminPort (and operator.metricsPort/operator.healthPort, see below) to ports that don't conflict with other host-networked components. The deployment strategy defaults to Recreate when hostNetwork is enabled (unless operator.deployment.strategy is 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 that hostNetwork: true is rejected by Pod Security Admission at the baseline/restricted levels, so the operator's namespace must permit host namespaces when enabling this. For example:

    operator:
    deployment:
    hostNetwork: true
    webhookPort: 9444
    adminPort: 5556
  • Added priorityClassName and podDisruptionBudget support to the operator.deployment Helm values for the ControlPlane and ManagementPlane operator charts, and a new operator.hookInitFinalizer values block for the pre-delete hook-init-finalizer Job, supporting priorityClassName, podSecurityContext, containerSecurityContext, tolerations, nodeSelector, and resources. 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. The decision_source label separates decisions answered from the in-memory authorization cache (runtime_cache) from those served by the database (transactional_db for decisions made inside a transaction, and disabled_db_base/disabled_db_tx when the cache is turned off), and the method label 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 an UpstreamTrafficSettings list 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.com inherits 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 deployment to trigger the operator's finalizer cleanup now uses the existing tsboperator-jobs image with a new uninstall-trigger job.

  • 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 createdAt on each key. Keys added before this release have no creation time.
    • Added a list-keys command to show all keys in a service account, and how long since the key was added.
    • The revoke-key command now accepts --id multiple times, so you can revoke all replaced keys in a single command.
    • If a revoke-key call would revoke every key in a service account, it is refused unless --force is given.
    • Revoking a key a service account does not have is reported and skipped, so revoke-key can be repeated safely.
  • The ControlPlane now supports a per-revision strictClientMtls setting that enforces strict mTLS on the client side across clusters. When enabled, sidecars in that revision originate ISTIO_MUTUAL to all mesh-internal upstreams unless an explicit DestinationRule tls setting 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 for authentication.trafficMode: REQUIRED hosts, which significantly reduces the number of DestinationRules 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 a tls: DISABLE DestinationRule for 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.tokenLifeTime was lowered on the ManagementPlane resource. 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 tokenRefreshBefore must be shorter than tokenLifeTime and defaults to 24 hours, so lowering tokenLifeTime to 24 hours or less requires lowering tokenRefreshBefore in 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 the global-waypoint in the xcp-multicluster namespace and therefore arrives under the global waypoint's identity rather than the client's, so it is admitted by the allow-gateway-ambient authorization 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 Workspace defines a defaultTrafficSetting. XCP Edge unconditionally attached an empty (but non-nil) legacy Resilience block to every workspace-level default traffic setting, which the legacy-to-new API conversion then expanded into a phantom catch-all upstreamTrafficSettings entry (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 config ERRORED -- and therefore the aggregated Workspace status ERRORED at 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 legacy Resilience block forward only when it is actually set, so no phantom upstreamTrafficSettings entry 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=ambient label 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 /live liveness endpoint on the monitoring port, and the central Deployment is configured with a Kubernetes livenessProbe pointing 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-fips for 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 .so plugin from a URL, e.g. via oci:// or file://) is not supported in FIPS builds; only parts of composer's built-in plugins are available there like the coraza-waf plugin.

  • Added Envoy dynamic module support to gateway Extensions which could be used to load dynamic modules in the TSB. Use the install.tetrate.io/tetrate-dynamic-modules annotation to enable dynamic module support for the gateway and the composer module 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 (a TrafficSetting with authentication.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 variable ENABLE_NON_INJECTED_MTLS_EAST_WEST_ALIGNMENT to false on XCP Edge.

  • Egress gateways no longer report a spurious port-protocol conflict when 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. Set trafficMode: EGRESS on 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 action field. ALLOW is the default and is what rules did before, DENY refuses a request whatever the allow rules say, and AUDIT records a request without taking any part in the decision.

    A DENY or AUDIT rule 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 AUDIT rule 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 when conditions and negation, matching what Istio authorization already expresses. action, when and negation apply to a gateway server's meshExternalAuthz rules; 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 when key addresses any attribute Istio authorization understands, including HTTP/2 pseudo headers, so request.headers[:authority] and request.headers[:path] work, and unlike to.paths the 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 on request.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 alpnProtocols field to the server TLS settings. When set, the listed protocols replace the ALPN protocols the gateway would otherwise advertise during the TLS handshake (h2 and http/1.1 for 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.terminationGracePeriodSeconds field to the gateway install resources. It sets the pod-level termination grace period directly, removing the need for a custom Deployment overlay 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.uri without authority) and routed to a clusterDestination lost the URI rewrite in the generated VirtualService, 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_CENTRAL environment variable on XCP Edge (default true). XCP Central must be upgraded before Edges (the standard upgrade order); set the variable to false on 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 via extensions.lua.plugins[] (each with a name, optional priority, and inlineCode). They are translated into an EnvoyFilter that 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.minProtocolVersion to SecuritySetting, 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 Istio Gateway server generated for those namespaces, and on the sidecars running in them. The gateway half applies in both BRIDGED and DIRECT config 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 a TLS001 advisory on the gateway. A server that configures no minProtocolVersion at all — the usual shape for a hand-written DIRECT mode Gateway — inherits the mandated version. TLS_AUTO (the default) applies no floor. A server that pins maxProtocolVersion below the mandated minimum — for example a gateway pinned to "TLS 1.2 exactly" meeting a TLSV1_3 mandate — has its maximum raised to the mandated version as well, reported as a TLS003 advisory: raising only the minimum would leave min > 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, MUTUAL and OPTIONAL_MUTUAL servers 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_MUTUAL servers are likewise unaffected, as Istio's own mTLS stack chooses the protocol versions there.

    This is why the setting is expressed on the http and tcp server blocks of an IngressGateway, both of which terminate TLS, and not on the tls block: a TLSServer carries 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. A DIRECT mode server in a TLS mode Istio cannot honor is reported as a TLS002 advisory rather than silently left at Envoy's default, and the mesh-internal ISTIO_MUTUAL servers XCP adds to DIRECT mode 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 EnvoyFilter named min-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.minProtocolVersion is per-istiod, so it cannot express a per-workspace floor — which is why a generated EnvoyFilter is used rather than a mesh-wide setting. Sidecars accept the same set of versions as gateway servers, TLSV1_0 and TLSV1_1 included. 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 read EnvoyFilters, 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 PeerAuthentication mode. The filter matches inbound filter chains on both transportProtocol: tls and transportSocket: envoy.transport_sockets.tls. The second condition is the load-bearing one: transportProtocol alone is ambiguous, because in PERMISSIVE mode 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 under STRICT and PERMISSIVE alike and can never graft a TLS transport socket onto a chain that was passing bytes through. This requires Istio

    = 1.28.9-a734f7aef8, which adds transportSocket to the EnvoyFilter filter chain match.

    This is gated by the ENABLE_MIN_TLS_VERSION feature flag on XCP Edge, which is disabled by default.

  • Fixed an issue where the OIDC authorization-code token exchange could fail with a protocol_error against an HTTP/2-capable identity provider. The generated upstream cluster for the OIDC token/JWKS endpoints advertised h2 in TLS ALPN but only spoke HTTP/1.1; it now advertises http/1.1 only, matching the cluster's protocol.

  • XCP Edge now sets XCP_PILOT_ENABLE_PEER_EXCHANGE_ALPN_OUTBOUND on istiod for ambient-enabled revisions, which advertises the istio-peer-exchange ALPN 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 overlays list are now rejected at admission for IngressDeployment, EgressDeployment, Tier1Deployment and GatewayDeployment.

  • XCP Edge now sends its periodic config resync ping to XCP Central on every config-resync-interval tick, instead of skipping the ping whenever central had recently pushed configuration to the edge. Central records the arrival of each ping in xcp_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 timeout of a Gateway HTTP routing rule being overridden by the requestTimeout of upstreamTrafficSettings. The EnvoyFilter generated from upstreamTrafficSettings matches on the virtual host alone, so its requestTimeout reached every route of that host. XCP now generates an additional EnvoyFilter, named route-level-<gateway>, that re-applies each route's own timeout after those filters. Routes that do not configure a timeout keep inheriting the one from upstreamTrafficSettings.

  • Fixed TLS handshakes failing on a shared ingress gateway listener that an application team's Gateway configured with tls.mode: MUTUAL or tls.mode: OPTIONAL_MUTUAL and a cross-namespace tls.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 -cacert suffix. XCP Edge only generated a ReferenceGrant for the Secret named in the Gateway, so the derived <secret>-cacert resource stayed unauthorized and the listener was left with a warming, invalid certificate. XCP Edge now also generates a <secret>-cacert-sgw-secret-grant ReferenceGrant for such listeners, which removes the need to apply that grant manually.

    Also fixed the name of a generated ReferenceGrant exceeding the maximum length of a Kubernetes object name when the referenced Secret has a very long name, which made the ReferenceGrant impossible to apply. Such names are now shortened, and remain unique. Names that already fit are unchanged.

  • Added a new strictClientMtls field to each revision under EdgeXcp.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 explicit DestinationRule tls setting opts out. This is the client-side complement to the server-side PeerAuthentication STRICT that XCP already generates for authentication.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-mTLS DestinationRules it previously created for trafficMode: REQUIRED non-injected hosts (istiod now enforces client mTLS mesh-wide); it instead generates tls: DISABLE DestinationRules only for trafficMode: OPTIONAL hosts so they keep their plaintext fallback, reducing the number of DestinationRules in the cluster. A trafficMode: REQUIRED host nested inside a trafficMode: OPTIONAL wildcard keeps its force-mTLS DestinationRule, so that the broader tls: DISABLE rule does not apply to it. Warning: non-injected or plaintext-only workloads that are not marked trafficMode: OPTIONAL will fail closed. The DestinationRule reduction can be turned off with the ENABLE_STRICT_CLIENT_MTLS_DR_REDUCTION=false environment variable on XCP Edge (istiod still enforces mTLS regardless, so this only affects DestinationRule volume, not security).

  • Added support for the SameSite attribute on OIDC authentication cookies via the new cookieConfig.sameSite field in OIDCConfig. 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[].selector is 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 labelled app: frontend without enumerating them. All labels under selector.labels must match for a namespace to be selected, and an empty selector is rejected. When a namespace matches both a name entry and a selector entry, the name entry wins. Among selector entries, 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.