Istio 1.32.0: Route-Scoped Authorization, ORCA Load Balancing, and the Security Debt a Quiet Minor Paid Down
Sources
- Announcing Istio 1.32.0 (official announcement)
- Istio 1.32.0 change notes (full list)
- Istio 1.32.0 upgrade notes (breaking changes)
- GitHub release: istio/istio 1.32.0 (published 2026-11-09)
- Issue #60964 — AuthorizationPolicy targeting HTTPRoute
- Issue #60814 — backendUtilization ORCA load balancing
- Issue #60823 — JWT rule presence: REQUIRED
- Issue #60887 — AUTO_PASSTHROUGH on ambient east-west gateways
- Issue #60235 — GOMAXPROCS removed from Istio charts
- DestinationRule proto at 1.32.0 (BackendUtilizationLB, OutlierDetection fields)
- Announcing Istio 1.31.0 (support-matrix baseline)
- GHSA-w44g-8m76-8gc5 — AuthorizationPolicy method matching bypass
Istio 1.32.0 went stable on November 9, 2026 — ten weeks to the day after 1.31.0 landed on August 31, with 414 commits between the two tags and release managers Jackson Greer (Microsoft), Jianpeng He (Tetrate), and Jack Kawell (Solo.io) signing the build. On the surface it reads like a consolidation release: utilization-based load balancing, richer outlier detection, one experimental Gateway API feature. Read the change notes end to end, though, and the real story is different — this is the release where Istio paid down a genuinely alarming pile of security debt: an ambient-mode plaintext bypass, an AuthorizationPolicy method-matching bypass over HTTP/2, a fail-open in BackendTLSPolicy, a JWKS cache-poisoning path found by Anthropic's security team, and an istioctl that was quietly not verifying istiod's certificate on xDS connections. If you run Istio in production, the security ledger alone forces an upgrade decision — the features are almost secondary.
There are also three behavior changes that will bite teams who upgrade carelessly: a Kubernetes API client that now times out at 90 seconds, unhealthy endpoints that now show up in discovery responses, and — for anyone who flips on the new route-scoped authorization — a renamed Envoy filter that silently disconnects every EnvoyFilter patch matching the old name. This analysis walks the release the way a platform team has to: support matrix first, breaking changes second, features third, and the upgrade runbook last.
| Release | Announced | Supported Kubernetes | Commits since previous minor |
|---|---|---|---|
| Istio 1.31.0 | 2026-08-31 | 1.32 – 1.36 | — |
| Istio 1.32.0 | 2026-11-09 | 1.33 – 1.37 | 414 |
The upgrade math before anything else
Istio 1.32.0 is officially supported on Kubernetes 1.33 through 1.37; 1.31.x supported 1.32 through 1.36. The floor moved by one minor in both directions. That has two consequences, and the first is the one that stalls upgrades: if you are still on Kubernetes 1.32, you cannot move to Istio 1.32 at all — you ride 1.31 until the cluster itself is upgraded. The second consequence is the usual skew rule: control planes are expected to run ahead of data planes, so a 1.32 istiod serving 1.31 sidecars is supported during the rollout window — but check your own deployment's revision mixing rather than assuming the grace period is long enough for a multi-week rollout.
The istioctl binary itself moved its minimum supported Kubernetes version to 1.33.x, so CI jobs that run istioctl analyze or istioctl proxy-status against older API servers are outside the supported envelope from day one.
What did not change: the ambient-mode story remains flag-gated and mostly opt-in. The multi-network ambient work described below ships dark behind AMBIENT_ENABLE_MULTI_NETWORK, which is registered false by default in pilot/pkg/features/ambient.go. Nothing in 1.32.0 silently re-architects an existing sidecar mesh — the dangerous bits all require an operator action first.
Route-scoped AuthorizationPolicy — and the filter rename it drags in
The marquee feature is experimental support for targeting Gateway API HTTPRoute resources from an AuthorizationPolicy via targetRefs, so ALLOW and DENY rules scope to a single route instead of the entire gateway. This closes a real gap: on a shared gateway fronting dozens of teams, per-route authorization previously meant cramming everything into one gateway-wide policy with brittle when conditions, or spawning gateways per team.
The constraints are strict, and every one of them is load-bearing:
- It is disabled by default: set
ENABLE_GATEWAY_API_HTTPROUTE_AUTH=trueon istiod (registeredfalseinpilot/pkg/features/experimental.go). - The policy must live in the same namespace as the
HTTPRouteit targets. - Only ingress gateways apply these policies — waypoints and sidecars ignore them.
CUSTOMandAUDITactions cannot target anHTTPRoute.- An
HTTPRoutetarget reference cannot be combined with target references of other kinds in the same policy.
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: payments-route-allow
namespace: team-a # MUST match the HTTPRoute's namespace
spec:
action: ALLOW
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: payments-v1
rules:
- from:
- source:
principals: ["cluster.local/ns/istio-ingress/sa/gateway"]
The semantics surprise people, so spell them out now. A route-scoped ALLOW policy is evaluated together with the gateway's own ALLOW policies — a request passes if it matches either. Route-scoped ALLOW widens access on that route; it never narrows it. Route-scoped DENY policies are additive and cannot override a DENY defined for the gateway or in the root namespace. If you expected route-scoped rules to behave like a deny-by-default firewall slice, that is not the model — it is the same OR-composition ALLOW uses across the root and workload namespaces, just scoped finer.
The breaking change hiding behind the flag
Enable this feature and a filter name changes underneath you. With ENABLE_GATEWAY_API_HTTPROUTE_AUTH=true, the ALLOW action's HTTP filter on ingress gateways is emitted as istio.authorization.allow instead of envoy.filters.http.rbac. The rename exists so route-scoped configuration can address the ALLOW filter without touching the DENY and AUDIT filters, which keep the old name. Every EnvoyFilter that matches the RBAC filter by name is affected:
MERGE,REPLACE, andREMOVEoperations silently stop applying to the ALLOW filter.INSERT_BEFOREandINSERT_AFTERdrop the supplied filter from the chain entirely rather than falling back to another position.- Gateways whose only authorization policies use the ALLOW action have no filter named
envoy.filters.http.rbacat all.
And the failure is silent by design: no warning is emitted unless PILOT_ENVOY_FILTER_STATS=true is set on istiod, which exposes the pilot_envoy_filter_status metric. The upgrade notes prescribe the remediation — duplicate the patch with a second EnvoyFilter matching istio.authorization.allow, or leave the feature flag off:
apiVersion: networking.istio.io/v1
kind: EnvoyFilter
metadata:
name: rbac-filter-audit
namespace: istio-system
spec:
workloadSelector:
labels:
istio: ingressgateway
configPatches:
# pre-1.32 shape: still matches the DENY and AUDIT filters
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: envoy.filters.http.http_connection_manager
subFilter:
name: envoy.filters.http.rbac
patch:
operation: REMOVE
# required addition when ENABLE_GATEWAY_API_HTTPROUTE_AUTH=true:
# the same patch, re-anchored to the renamed ALLOW filter
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: envoy.filters.http.http_connection_manager
subFilter:
name: istio.authorization.allow
patch:
operation: REMOVE
Envoy statistics survive the rename untouched — RBAC filter metrics are derived from the connection manager's stat prefix, not the filter name — so dashboards will not warn you either. If you carry RBAC-patching EnvoyFilters, inventory them before the upgrade, not after a security fix silently stops applying:
kubectl get envoyfilter -A -o yaml | grep -n "envoy.filters.http.rbac"
The release also adds analyzer message IST0178: it warns when an EnvoyFilter inserts an HTTP filter while an AuthorizationPolicy targeting an HTTPRoute is present, because a filter that clears the routing cache after a route-dependent filter has run can route a request differently than it was authorized. Run istioctl analyze as part of the upgrade, not as a post-mortem.
ingress gateway, ENABLE_GATEWAY_API_HTTPROUTE_AUTH=true
client request
|
[listener / TLS termination]
|
envoy.filters.http.http_connection_manager
|
router: HTTPRoute match -- no match --> default route handling
|
| matched route
v
+------------------------------------+ +------------------------------------+
| route-scoped ALLOW | | gateway-wide DENY / AUDIT |
| istio.authorization.allow | | envoy.filters.http.rbac |
| evaluated WITH the gateway's own | | these filters KEEP the old name; |
| ALLOWs: request passes if it | | route-scoped DENY is additive and |
| matches EITHER | | cannot override a gateway DENY |
+------------------------------------+ +------------------------------------+
|
upstream (the route's backendRef)
Utilization-based load balancing: ORCA lands in DestinationRule
The new backendUtilization policy on DestinationRule is the feature most likely to change real request distribution: it consumes backend-reported ORCA (Open Request Cost Aggregation) load reports and dynamically weights endpoints, so endpoints reporting higher utilization receive proportionally fewer requests. Under the hood it maps to Envoy's client_side_weighted_round_robin policy, and the proto comment links the ORCA schema at xds/data/orca/v3/orca_load_report.proto.
The full field set, verified from the 1.32.0 DestinationRule proto:
| Field | Default | Meaning |
|---|---|---|
weightStabilizationPeriod | 10s | How long a fresh endpoint's reported weight is distrusted; during stabilization it gets equal weight |
weightExpirationPeriod | 3m | Silence window before a stale endpoint's weight is discarded and reverts to equal |
weightUpdatePeriod | 1s (min 100ms) | Weight recomputation cadence; faster reacts quicker, costs CPU |
errorUtilizationPenaltyPercent | 0 (disabled) | Penalty on erroring endpoints as a percentage of utilization (150 = 1.5x) |
metricNamesForComputingUtilization | application_utilization | Custom ORCA metric names; utilization is the max of listed values when greater than zero |
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: recommendations-orca
spec:
host: recommendations.prod.svc.cluster.local
trafficPolicy:
loadBalancer:
backendUtilization:
weightStabilizationPeriod: 10s
weightExpirationPeriod: 3m
weightUpdatePeriod: 1s
errorUtilizationPenaltyPercent: 150
metricNamesForComputingUtilization:
- named_metrics.queue_depth
Now the unvarnished part: the backends have to speak ORCA. Utilization reports arrive through the xDS load-reporting path — in practice that means gRPC servers embedding the ORCA load reporter, or Envoy-based intermediaries that already emit load reports. A plain HTTP deployment with no ORCA instrumentation gets nothing from this policy; every endpoint stays at the default equal weight and you have added configuration for no effect. If your platform runs gRPC-heavy internal traffic (the same traffic that benefits from the inbound retry controls and per-route authorization above), this is a meaningful lever. If your mesh is mostly JSON-over-HTTP with no ORCA instrumentation, skip it and stop reading release notes marketing.
backend pods (gRPC, ORCA load reporter embedded)
| ORCA load reports (utilization, application_utilization,
| named metrics, error rates) via xDS load reporting
v
calling proxy (sidecar or gateway)
client_side_weighted_round_robin
- fresh endpoint: equal weight for weightStabilizationPeriod
- reporting endpoint: weight inversely proportional to utilization
- silent > weightExpirationPeriod: reverts to equal weight
- errorUtilizationPenaltyPercent > 0: erroring endpoints penalized
|
v
requests skew away from busy endpoints, no app changes required
Outlier detection finally grows the two knobs everyone asked for
Two new fields on DestinationRule.OutlierDetection close long-standing gaps, both verified in the 1.32.0 proto:
outlierDetectionHttpErrorCodes(values in [100, 599]) — choose exactly which HTTP status codes count as errors toward theconsecutive5xxErrorsandconsecutiveGatewayErrorsejection thresholds. Unset, only 5xx responses count. Issue #60828.failurePercentageThreshold(values in [0, 100], default 85) — eject hosts on failure rate over the analysis interval instead of consecutive failures. A host whose errors are interspersed with successes still gets ejected.
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: recommendations-outlier
spec:
host: recommendations.prod.svc.cluster.local
trafficPolicy:
outlierDetection:
interval: 10s
baseEjectionTime: 30s
consecutive5xxErrors: 5
outlierDetectionHttpErrorCodes: [429, 503]
failurePercentageThreshold: 50
Read that example carefully — the interplay is a footgun. Once outlierDetectionHttpErrorCodes is set, only the listed codes count toward the consecutive-error thresholds. A 500 is no longer an error for ejection purposes in the example above, even though most SREs would call it the canonical one. Treat the field as "replace the error definition," not "extend it."
Rate-based ejection is the more honest signal in production anyway: a pod serving 8% errors among healthy responses was invisible to consecutive-count ejection, and becomes ejectable the moment its failure rate crosses whatever failurePercentageThreshold you configure — the default 85 tolerates a lot, so set it per-service rather than trusting the fleet default. Pair it with the existing interval sweep to control detection latency.
The inbound retry you never configured is now yours to configure
Every sidecar shipped with a hardcoded inbound retry: 2 attempts on Envoy's reset-before-request condition — requests the proxy never delivered to the application. The mesh config field defaultInboundHttpRetryPolicy finally exposes it, verified as field 74 in the 1.32.0 MeshConfig proto:
- Only
attempts,retryOn, andbackoffapply —perTryTimeout,retryRemoteLocalities, andretryIgnorePreviousHostsare ignored (an inbound route always targets the single local application cluster). - Setting
defaultInboundHttpRetryPolicy: {}(attempts 0) disables inbound retries mesh-wide. - The policy is never applied to ports declared as gRPC, where
reset-before-requestbehaves badly for streaming services. - It is distinct from
defaultHttpRetryPolicy, which governs outbound traffic — configuring one does not affect the other.
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: control-plane
spec:
meshConfig:
defaultInboundHttpRetryPolicy:
attempts: 4
retryOn: reset-before-request
backoff:
baseInterval: 50ms
Why care about a retry default? The hardcoded 2 attempts exist to shield clients from the classic race where an application closes an idle keep-alive connection mid-request. Retrying a request that never reached the app is safe even for non-idempotent methods. If you disable it mesh-wide to chase p99 tail latency, you are opting every workload into that race — do it for the workloads that need determinism, not as a fleet-wide default.
Ambient: one east-west gateway for mixed meshes
Mixed deployments — ambient and sidecar workloads in the same mesh — have paid a topology tax: sidecar multi-network traffic needed its own east-west gateway in every cluster, separate from the ambient one. Issue #60887 fixes that: an istio-east-west gateway (GatewayClass: istio-east-west) can now run an AUTO_PASSTHROUGH TLS listener on port 15443 alongside its HBONE listener, serving sidecar multi-network traffic from the same gateway. A custom port needs the listener option the release notes name: gateway.istio.io/listener-protocol: auto-passthrough. The whole capability is gated on AMBIENT_ENABLE_MULTI_NETWORK, which registers false in pilot/pkg/features/ambient.go — an istiod environment variable:
# containers[0].env additions on the istiod Deployment
env:
- name: AMBIENT_ENABLE_MULTI_NETWORK
value: "true"
# optional, only matters once the parent flag is on
# (registered default: true, inert while MULTI_NETWORK is false)
- name: AMBIENT_ENABLE_MULTI_NETWORK_WAYPOINT
value: "true"
BEFORE 1.32.0: mixed mesh = TWO east-west gateways per cluster
sidecar pods --> east-west gateway #2 (AUTO_PASSTHROUGH :15443)
ambient pods --> east-west gateway #1 (HBONE)
one gateway per plane, per cluster, per network
AFTER 1.32.0: one istio-east-west gateway carries both planes
sidecar pods --+
+--> istio-east-west
ambient pods --+ | HBONE listener
+ AUTO_PASSTHROUGH listener on :15443
requires AMBIENT_ENABLE_MULTI_NETWORK=true on istiod
Treat the feature as new rather than mature. The change notes also carry issue #60609 — enabling ambient multi-network for ingress gateways previously broke ingress routing to sidecar pods entirely, which is precisely the kind of bug that kept the flag dark. If you run the mixed topology, this release is the first where it plausibly works; budget for canary testing, not flip-and-forget.
Two more ambient fixes deserve space in your upgrade notes: the node-untaint controller now waits for ztunnel readiness before removing cni.istio.io/not-ready (disable with PILOT_NODE_UNTAINT_CHECK_ZTUNNEL=false), so pods stop landing on nodes with a wedged ztunnel; and an HBONE stream leak where connect_originate streams could stay open forever after a peer closed — fixed through the Envoy update bundled in this release.
The security ledger: why this release is not optional
This is the section to read twice. The 1.32 change notes carry a density of security fixes — several with GitHub Security Advisories — that turns the release from "nice to have" into "scheduled within two weeks":
| Fix | Class | Reported by | What you must do |
|---|---|---|---|
Ambient mTLS bypass: a port-level STRICT PeerAuthentication was not enforced when the workload-level mode was PERMISSIVE or DISABLE and the mesh-wide mode was STRICT — plaintext could reach that port | AuthZ/mTLS bypass | Istio team | Upgrade ambient meshes; audit port-level PeerAuthentications that assumed enforcement |
AuthorizationPolicy method matching bypassed over HTTP/2 by changing case (post for POST); request.headers[:authority]/[host] conditions bypassed the same way | Policy bypass (GHSA-w44g-8m76-8gc5) | @izzysec | Upgrade — method and host matching is now case-insensitive, matching Envoy semantics |
| istiod crash via IP-address collision between a waypoint and another service (e.g. a ServiceEntry in another namespace) — a low-privileged tenant could crash the control plane | DoS / privilege boundary (GHSA-7p2q-4xvg-vxc6) | @yshch | Upgrade; colliding addresses are skipped with a warning instead of panicking |
BackendTLSPolicy fail-open: sidecar proxies sent upstream traffic in plaintext when caCertificateRefs could not be resolved | Fail-open defect | Istio team | Upgrade; verify all caCertificateRefs resolve after upgrade (it now fails closed) |
JWKS background refresh cached a fetched response without validating it is a JWKS — a jwksUri pointed at an unintended internal endpoint could be served back as keys | Cache poisoning | Anthropic and Ada Logics | Upgrade; audit every RequestAuthentication.jwksUri in the fleet |
jwksUri SSRF: istiod now blocks link-local and cloud metadata addresses (169.254.169.254) at dial level and rejects non-JWKS responses; BLOCKED_CIDRS_IN_JWKS_URIS blocks private/loopback ranges | SSRF hardening | Istio team | Set BLOCKED_CIDRS_IN_JWKS_URIS if istiod runs multi-tenant |
| Wasm fetch chain: the HTTP fetcher had no destination-IP restriction at all; the OCI fetcher's SSRF guard missed 3xx redirects; a malicious registry could force a plaintext HTTP retry on demand | SSRF / transport downgrade | Aser Ahmed; 박현범; Svpernova; Adam Korczynski | Upgrade; move registries behind TLS; allowlist insecure ones explicitly via WASM_INSECURE_REGISTRIES; consider BLOCKED_CIDRS_IN_WASM_FETCH |
sidecar.istio.io/* annotations (proxyImage, bootstrapOverride, logLevel, componentLogLevel, agentLogLevel) were interpolated into injection templates without escaping — crafted values could inject fields into pod specs | Template injection | localhost-detect | Upgrade; audit who can label pods in injection-enabled namespaces |
Sidecar bypass via conntrack: a new connection reusing the 4-tuple of a torn-down connection could match a stale ESTABLISHED entry and skip the proxy; a SYN-only rule now runs ahead of the divert rule in iptables and nftables modes | Proxy bypass | Istio team | Upgrade — no config; long-lived client pools benefit most |
istiod debug endpoints (/debug/config_dump, /debug/edsz) matched proxyID as a substring — a caller could read other proxies' config by partial ID; exact <pod>.<namespace> now required | Info disclosure | 박현범 | Upgrade; restrict istiod debug port access regardless (NetworkPolicy) |
istioctl proxy-status, istioctl x internal-debug, and --xds-address did not verify istiod's certificate when authenticating with a service account token — now verified against the istio-ca-root-cert ConfigMap | Missing cert verification (#41937) | Leon Zlobecki | Upgrade istioctl everywhere, including CI images; --insecure/ISTIOCTL_INSECURE=true preserves old behavior explicitly |
istioctl analyze built K8s clients from istio-system multicluster secrets without sanitizing the kubeconfig — a crafted secret could run an exec credential plugin on the operator's machine; manifest translate wrote unquoted values into generated shell scripts | Client-side RCE vectors | Adam Korczynski; OpenSec Intelligence | Upgrade istioctl; treat multicluster Secrets as untrusted input in your threat model |
Experimental K8s CSR RA path (EXTERNAL_CA=ISTIOD_RA_KUBERNETES_API): CreateCertificate leaked raw API errors; a new MAX_RA_WORKLOAD_CERT_TTL (default 48h) caps client-requested certificate TTL to bound stolen istio-token blast radius | Info leak + credential hardening | Juno Im | Upgrade; if you legitimately request >48h certs on this path, raise MAX_RA_WORKLOAD_CERT_TTL during the change window |
Two more hardening switches ship alongside the fixes. PILOT_DISABLE_SELF_SIGNED_CA (default false) makes istiod refuse to start its CA instead of generating or reusing a self-signed root when no plugged-in cacerts bundle exists — deployments that must only use an external CA can now fail closed. And the distroless base images moved to cgr.dev/chainguard/static with a refreshed CA bundle that drops emSign ECC Root CA - G3, following the compromise of eMudhra intermediate CA keys — relevant if you terminate TLS to endpoints that still chain to eMudhra.
The honest read: none of these are remote-code-execution-in-the-data-plane catastrophes, and the ambient mTLS bypass requires a specific three-layer policy combination. But the pattern — a missing verification here, a fail-open there, a template sink that trusted user input — is exactly what a hostile tenant in a multi-tenant mesh looks for, and 1.32.0 closes all of it at once. The upgrade is not about features.
Telemetry: protobuf passthrough and a label fix your dashboards will feel
Issue #49950 lands native Prometheus protobuf passthrough in pilot-agent's metrics merger. When a scraper sends the protobuf accept header, Envoy's native (sparse) histograms now reach the scraper unchanged instead of being silently degraded by text-format re-encoding; the text and OpenMetrics paths are byte-for-byte unchanged. A new counter reports the negotiated format:
# which formats your scrapers actually negotiate
istio_agent_scrape_format_total{format="protobuf"}
istio_agent_scrape_format_total{format="openmetrics"}
istio_agent_scrape_format_total{format="text"}
# downgrades happen when upstreams disagree on format — track them
sum by (downgrade) (istio_agent_scrape_format_total)
Accept: application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited
The label fix that changes dashboards: source_app and destination_app now use the app pod label, correcting a regression introduced in 1.29.1 where they had used app.kubernetes.io/name. If your panels were rebuilt during the 1.29–1.31 window to match the wrong label, expect series to flip back on upgrade — annotate the change or your on-call will hunt a phantom metric migration. Relatedly, a Telemetry resource in a waypoint's own namespace, selected by targetRefs or selector, was silently dropped for every cross-namespace Service the waypoint handled (#61538) — access logs and tracing that "mysteriously" vanished for cross-namespace traffic are fixed by this release, not by your logging stack.
For mesh-config observability, a new istiod_mesh_config_load_errors_total counter surfaces config load failures that previously only showed in logs.
Installation and charts: GOMAXPROCS is gone, and that is a good thing
The chart change with real operational weight: the GOMAXPROCS environment variable was removed from all Istio charts (#60235). Istio 1.32 is built with Go 1.25, which derives GOMAXPROCS from cgroup CPU limits automatically. If you were using chart values to pin GOMAXPROCS below the CPU limit to control noisy-neighbor scheduler pressure, that override is gone — set honest CPU limits instead. Every component now matches its own throttling envelope, which is the correct default; the migration work is reviewing any CPU limits that were set artificially high because you compensated with GOMAXPROCS.
The rest of the installation ledger:
- Kiali addon updated to v2.34.1; agentgateway pinned at v1.6.0 and now honors Gateway API frontend client certificate validation (
spec.tls.frontend, includingAllowInsecureFallback) and backend client certificates, withResolvedRefs/InsecureFrontendValidationModeconditions. terminationGracePeriodSecondsis now a Helm value for istiod (default 30s — matches the old hardcoded behavior; raise it if you rely on graceful xDS drain windows).values.sidecarInjectorWebhook.revisionLabelPrecedence(defaultnamespace) — set topodand a pod-levelistio.io/revlabel overrides the namespace's. A quiet but genuinely useful knob for canarying revisions at the pod level.- The
istioctl installschema holes are closed:cni.ambient.enablementSelectors,cni.ambient.shareHostNetworkNamespace,cni.ambient.enableAmbientDetectionRetry,values.base.validationFailurePolicy, and a list ofpilot.*/cni.*values (pilot.initContainers,pilot.pdb,pilot.trustedZtunnelName,cni.updateStrategy,cni.seLinuxOptions,global.waypoint.tolerations…) were supported by the charts but rejected by the IstioOperator schema. - The default chart's
ValidatingWebhookConfigurationno longer hardcodesfailurePolicy: Ignore, fixing the server-side-apply field-manager conflict on Helm upgrades. - istiod now tolerates a custom
taint.nameinstead of only the defaultcni.istio.io/not-ready; the gateway chart's NetworkPolicy no longer gets deleted and recreated on revision changes; and the istio-cni chart fixes a stack of upgrade/uninstall hygiene issues (owned config cleanup, restart on config change). - The OpenShift profile now defaults
global.nativeNftablesto true for sidecar and ambient installs. - istiod CA-selection fix: the always-present
istio-ca-root-certConfigMap no longer beats the CA cert co-located with the DNS serving cert — relevant with cert-manager istio-csr, where the webhookcaBundlecould stop matching after external cert provisioning.
Control-plane behavior changes to rehearse before the upgrade
Kubernetes API requests now carry client-side timeouts. Previously an Istio component could hang forever on an API server request whose return path was silently dropped — stalling the issuing controller until process restart. The defaults suit most clusters, and both can be zeroed to restore the old unbounded behavior:
| Variable | Default | Meaning |
|---|---|---|
ISTIO_KUBE_HEADER_TIMEOUT | 90s | Max wait for response headers; must stay above the API server's --request-timeout (default 60s) |
ISTIO_KUBE_REQUEST_TIMEOUT | 15m | Total duration of a request that does not set its own deadline, including reading the response |
Exec, attach, and port-forward sessions are unaffected. Every timeout increments kube_client_deadline_fired_total and logs a warning naming the method and path — put the metric on your upgrade dashboard before you start:
# client-side K8s API timeouts firing (should be zero on a healthy API server)
increase(kube_client_deadline_fired_total[5m])
# mesh config load errors (new counter in 1.32)
istiod_mesh_config_load_errors_total
# EnvoyFilter match failures — only emitted once PILOT_ENVOY_FILTER_STATS=true
pilot_envoy_filter_status
# scrape format negotiation after the protobuf passthrough change
increase(istio_agent_scrape_format_total[5m])
Unhealthy endpoints become visible. With PILOT_AUTO_SEND_UNHEALTHY_ENDPOINTS enabled (it defaults on), unready pods selected by ServiceEntry resources and unhealthy health-checked WorkloadEntry resources now appear in endpoint discovery responses with unhealthy status instead of being omitted. Proxies that assumed "present means healthy" need re-checking; Envoy handles the status correctly, but custom EDS consumers might not.
Push-frequency changes, both directions. A 1.31 regression is fixed: a change to a CA-certificate ConfigMap — including the kube-root-ca.crt ConfigMap Kubernetes maintains in every namespace — used to trigger CDS, LDS, RDS and NDS pushes to every proxy; these now trigger only SDS pushes. On large meshes with GitOps-managed CA bundles this alone is a real CPU win. Working the other way, the RDS cache key is now computed from a conservative superset of relevant DestinationRules, so some unrelated DestinationRule changes (particularly wildcard hosts) may cause an extra cache invalidation and push that 1.31 would have skipped — correctness unchanged, push frequency slightly up. Also fixed: istiod CPU growth as AuthorizationPolicy count rises (#61254), and a multi-cluster memory leak where rotating a remote cluster's credentials leaked its entire cached state (#60033).
Worth knowing about if you operate big StatefulSets: an opt-in optimization generates a single wildcard 0.0.0.0:Port listener with per-pod /32 CIDR filter chains instead of one listener per pod IP — off by default, enabled with PILOT_ENABLE_HEADLESS_FILTER_CHAIN_LISTENER=true on istiod (#60552).
The upgrade runbook
# 0. Confirm the support envelope BEFORE touching anything.
# Istio 1.32 needs Kubernetes 1.33-1.37.
kubectl version --output=yaml
istioctl version
# 1. Static analysis first — IST0178 (EnvoyFilter x route-scoped auth)
# ships in 1.32's analyzer and will flag the dangerous combos.
istioctl analyze -A
# 2. Inventory EnvoyFilters that match the RBAC filter by name.
# If you enable ENABLE_GATEWAY_API_HTTPROUTE_AUTH=true, these need
# a second entry matching istio.authorization.allow.
kubectl get envoyfilter -A -o yaml | grep -n "envoy.filters.http.rbac"
# 3. Canary: install the new revision without touching workloads.
istioctl install --revision 1-32-0 \
--set profile=production \
-f control-plane-1.32.yaml
# 4. Flip one namespace at a time; watch the new signals as you go.
kubectl label namespace app-team-a istio.io/rev=1-32-0 --overwrite
istioctl proxy-status
# 5. When the fleet is on 1.32, remove the old revision.
istioctl uninstall --revision 1-31-4 # your previous revision tag
Sequencing matters: run the EnvoyFilter inventory before the install, because the filter rename only bites when you later set ENABLE_GATEWAY_API_HTTPROUTE_AUTH=true — teams that enable the flag in the same change window as the upgrade are the ones who lose a security patch silently. And if you run the experimental Kubernetes CSR RA path with certificates legitimately longer than 48 hours, raise MAX_RA_WORKLOAD_CERT_TTL in the same change window or your workloads start failing to mint certs after the control plane upgrades.
Who should skip
Clusters on Kubernetes 1.32 — you have no choice; the support floor moved. Stay on 1.31.x until the cluster upgrades.
Teams using the experimental K8s CSR RA path with long-lived certs — upgrading without raising MAX_RA_WORKLOAD_CERT_TTL above 48h breaks your certificate issuance. Fix the env var first, or stay put.
Ships with a deep library of RBAC-patching EnvoyFilters who want route-scoped auth day one — the feature is behind a flag, so you can upgrade and defer. Just do not enable the flag before the EnvoyFilter audit; silent is the operative word for how the old patches stop applying.
HTTP-only services eyeing backendUtilization — without ORCA-reporting backends the policy is dead weight. gRPC-heavy platforms: proceed; JSON-over-HTTP fleets: come back when your services report utilization.
Ambient multi-network true-believers — ironically you should be the first to upgrade (the ingress-to-sidecar routing fix in #60609 unblocks your topology), but treat the mixed-mesh east-west story as freshly-landed code, not a battle-tested one. Canary the flag per-cluster.
What is not a reason to skip: waiting for 1.32.1. The security ledger above is the argument; a patch release will polish, but the exposure surface — method-case bypasses, fail-open TLS, JWKS poisoning, an istioctl that skipped server verification — is already live in whatever you are running today.
Verdict
Istio 1.32.0 is a defensive release wearing a feature-release costume. The ORCA load balancer and rate-based outlier detection are genuine upgrades for gRPC-heavy platforms; route-scoped AuthorizationPolicy is the right primitive with honest experimental boundaries and one landmine (the filter rename) clearly documented; the mixed-mesh east-west gateway removes a real per-cluster cost. But the part that should drive your scheduling is the security ledger — several of those fixes (ambient port-level mTLS bypass, BackendTLSPolicy fail-open, the istioctl verification gap) are the kind that get written up after someone else finds them in production. The support floor moving to Kubernetes 1.33 is the only hard blocker, the client-side API timeouts are the only behavior change likely to page anyone on a stressed API server, and both are knowable before the first istioctl install command runs. Rehearse the three behavior changes, inventory the EnvoyFilters, canary the revision — and get the control plane patched this sprint, not next quarter.