January 22, 2025·5 min read·engineeringistiomtlsambient

Istio 1.22.8 fixes ambient port-level mTLS evaluation

Two ambient PeerAuthentication fixes address opposite failures in policy evaluation. Multiple strict ports could become permissive, while port exceptions could remain overly strict.

A policy requiring mutual TLS on two ports needs to protect connections to either port. If the implementation combines mutually exclusive destination-port tests incorrectly, a policy can look restrictive in YAML while enforcing almost nothing. Istio 1.22.8 fixes that kind of error in ambient PeerAuthentication evaluation.

The January 22 release notes describe a bug where multiple STRICT port-level mTLS rules effectively produced a permissive ambient policy. The notes identify incorrect AND versus OR evaluation as the cause. They also include another ambient fix for policies that were overly strict, so the patch addresses failures in both directions.

I consider workload identity the strongest reason to operate Istio. It provides a better basis for access decisions than network addresses, but the benefit depends on correctly enforcing the declared policy. A certificate infrastructure and a strict-looking manifest are not sufficient evidence of that enforcement.

Port rules describe alternatives within a workload

The archived PeerAuthentication reference defines incoming-connection mTLS requirements. It distinguishes a workload-wide mode from portLevelMtls overrides. The port numbers refer to workload ports, not the externally exposed ports of a Kubernetes Service.

That distinction matters before examining the bug. A Service can present a port that maps to a different destination port in a Pod. A PeerAuthentication rule written against the Service port can therefore describe a different connection than the author intended. The release fixes evaluation of the rules; it does not change which port namespace those rules use.

A workload selector is required for port-specific settings. Without a selector, the policy's broader scope cannot simply be narrowed through an assumption about an individual application's ports. The reference also explains inheritance when a mode is unset, which is relevant when a port override depends on namespace or mesh policy.

Consider this policy shape for a selected workload. Its default permits both forms of incoming connection, while two named destination ports require mutual TLS.

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: application-ports
  namespace: applications
spec:
  selector:
    matchLabels:
      app: backend
  mtls:
    mode: PERMISSIVE
  portLevelMtls:
    8080:
      mode: STRICT
    9090:
      mode: STRICT

For an unauthenticated connection, either destination port should be enough to require rejection. A single connection does not simultaneously target 8080 and 9090. Combining those alternatives with a conjunction can make the rejection condition impossible. This illustrates the boolean distinction identified by the release notes, without claiming that the policy's YAML syntax itself changed.

The security significance follows from the mismatch between declared and effective behavior. Adding another protected port should extend the protected set. It should not remove protection from the first port or turn the resulting policy permissive. That is a property of the rule combination, beyond whether each individual rule parses correctly.

Ambient policy has a different enforcement context

In sidecar mode, PeerAuthentication governs incoming connections at the workload's Envoy sidecar. In ambient mode, the reference describes transparent security through the ztunnel node agent. Proxy-to-proxy HBONE traffic includes mutual TLS. STRICT policy is useful for excluding connections that bypass the mesh.

That explains why the January fix is explicitly ambient-only. Sharing an API kind does not imply that sidecar and ambient enforcement use identical implementation paths. The release notes do not claim that every sidecar PeerAuthentication policy has the same defect.

The reference also says DISABLE mode is unsupported for ambient. A port-level exception needs to be interpreted within that model, rather than copied indiscriminately from an example written for sidecars. Permissive acceptance and disabling ambient's tunnel security are different concepts.

Policy placement adds another source of context. The reference describes a selector as applying to matching workloads in the policy's namespace, with additional mesh-wide behavior when the policy is in the root namespace. A policy without a selector applies at the broader namespace scope, or mesh scope in the root namespace. Those scope rules determine which workloads receive the requirement before any port expression is evaluated.

UNSET also has a defined meaning. It inherits a parent mode when one exists and otherwise behaves as PERMISSIVE. An omitted workload-wide mode is therefore not proof that no policy applies. The effective decision can depend on a broader setting, which is exactly why a port exception must be evaluated against its inherited context. The patch's precision matters to both sides of that relationship.

I find this distinction essential when evaluating the operational cost of ambient migration. The desired policy can remain familiar while the component implementing it changes. Compatibility needs to include the effective connection decision, not only acceptance of the same resource by the API server.

The opposite failure could block legitimate access

The release links issue 53884 for the overly strict behavior. Its reporter describes a workload-specific permissive exception on port 9443 for an OpenTelemetry Operator webhook. The configuration worked with sidecars, but ambient rejected connections under a converted strict policy. Broadening the entire workload to permissive served as the reporter's workaround.

That is a different failure from the multiple-STRICT case. One unexpectedly rejects traffic that a port exception should admit. The other unexpectedly admits traffic where strict requirements should apply. Describing both as a generic mTLS problem would conceal whether the observable effect is availability loss or weakened enforcement.

The reporter's broader workaround also demonstrates why an availability symptom can affect security configuration. If the precise exception does not work, an operator may relax more of the policy to restore service. Correct evaluation lets the narrower intended rule remain usable.

Connection behavior is the relevant evidence

The patch changes policy evaluation without introducing a replacement PeerAuthentication API. Existing manifests still need their selector, inheritance and workload-port interpretation understood. A correctly targeted strict rule should reject plaintext access to the protected port, while an intended permissive exception should remain usable under the surrounding policy.

These cases form distinct expectations. A successful authenticated connection establishes that the allowed path works. A rejected unauthenticated connection establishes the protected boundary. An exception on another port needs its own expected outcome. The January release is useful because it identifies concrete ways those decisions could diverge from the declared resource, including a case where adding a second restriction weakened the result.