Istio 1.32.0: Route-Scoped Authorization, ORCA Load Balancing, and the Security Debt a Quiet Minor Paid Down

Sources

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.

ReleaseAnnouncedSupported KubernetesCommits since previous minor
Istio 1.31.02026-08-311.32 – 1.36—
Istio 1.32.02026-11-091.33 – 1.37414

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:

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:

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:

FieldDefaultMeaning
weightStabilizationPeriod10sHow long a fresh endpoint's reported weight is distrusted; during stabilization it gets equal weight
weightExpirationPeriod3mSilence window before a stale endpoint's weight is discarded and reverts to equal
weightUpdatePeriod1s (min 100ms)Weight recomputation cadence; faster reacts quicker, costs CPU
errorUtilizationPenaltyPercent0 (disabled)Penalty on erroring endpoints as a percentage of utilization (150 = 1.5x)
metricNamesForComputingUtilizationapplication_utilizationCustom 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:

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:

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":

FixClassReported byWhat 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 portAuthZ/mTLS bypassIstio teamUpgrade 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 wayPolicy bypass (GHSA-w44g-8m76-8gc5)@izzysecUpgrade — 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 planeDoS / privilege boundary (GHSA-7p2q-4xvg-vxc6)@yshchUpgrade; 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 resolvedFail-open defectIstio teamUpgrade; 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 keysCache poisoningAnthropic and Ada LogicsUpgrade; 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 rangesSSRF hardeningIstio teamSet 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 demandSSRF / transport downgradeAser Ahmed; 박현범; Svpernova; Adam KorczynskiUpgrade; 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 specsTemplate injectionlocalhost-detectUpgrade; 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 modesProxy bypassIstio teamUpgrade — 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 requiredInfo 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 ConfigMapMissing cert verification (#41937)Leon ZlobeckiUpgrade 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 scriptsClient-side RCE vectorsAdam Korczynski; OpenSec IntelligenceUpgrade 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 radiusInfo leak + credential hardeningJuno ImUpgrade; 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:

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:

VariableDefaultMeaning
ISTIO_KUBE_HEADER_TIMEOUT90sMax wait for response headers; must stay above the API server's --request-timeout (default 60s)
ISTIO_KUBE_REQUEST_TIMEOUT15mTotal 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.