Skip to content

gateway-api: same-port HTTPS and TLS passthrough emit duplicate Envoy "listener-<port>", dropping the entire CEC #48269

Open
cozystack/cozystack
#3343

Description

@MasterAM

Version

v1.20.0 (verified). Code inspection says v1.20.1 is unaffected by the relevant diff. k3s v1.35.5,
Gateway API CRDs v1.6.1, Cilium as CNI + LB + Gateway API.

What happened

On a Gateway that already has two or more distinct TLS-passthrough ports, adding a
TLS/Passthrough listener on a port already used by HTTPS/Terminate listeners causes Cilium to
generate two Envoy listeners with the same name. The whole CiliumEnvoyConfig is then discarded:

level=warn msg="Skipping CiliumEnvoyConfig due to malformed xDS resources"
  module=agent.controlplane.ciliumenvoyconfig
  k8sNamespace=gateway name=cilium-gateway-onprem-gateway
  error="duplicate Listener name \"gateway/cilium-gateway-onprem-gateway/listener-443\""

Every hostname on that gateway stops serving — TLS connections are reset with no certificate offered
(errno 54, SSL handshake has read 0 bytes). This includes all the previously-working HTTPS hosts,
not just the new listener.

Throughout, Gateway status reports the new listener and all existing listeners as
Accepted=True, Programmed=True, with no Conflicted condition, and the Gateway itself as
Accepted=True, Programmed=True. There is no indication in the API that anything is wrong.

Hostnames are fully disjoint — no SNI overlap, so this is not #48092 / #47590.

How to reproduce

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
spec:
  gatewayClassName: cilium
  listeners:
    # (1) and (2): two distinct passthrough ports -> NeedsPerPortTLSPassthroughListeners() == true
    - {name: tls-a,   protocol: TLS,   port: 8443, hostname: a.example.com, tls: {mode: Passthrough}}
    - {name: tls-b,   protocol: TLS,   port: 8883, hostname: b.example.com, tls: {mode: Passthrough}}
    # (3) and (4): HTTPS and passthrough sharing :443, disjoint hostnames
    - {name: https-c, protocol: HTTPS, port: 443,  hostname: c.example.com,
       tls: {mode: Terminate, certificateRefs: [{kind: Secret, name: c-tls}]}}
    - {name: tls-d,   protocol: TLS,   port: 443,  hostname: d.example.com, tls: {mode: Passthrough}}

Attach routes to each. Listeners (1) and (2) are what arm the bug — without them the same
https-c + tls-d pair works
, because translation takes the combined-listener path.

Expected

Either all four listeners program and d.example.com passes through while c.example.com
terminates (this is what the https-tls-shared-port scenario in the compatibility matrix records as
working), or — if the combination is genuinely unsupported — the offending listener is marked
Conflicted/Accepted=False and the rest of the gateway keeps serving.

Actual

The entire CiliumEnvoyConfig is rejected, all listeners on the Gateway stop serving, and the
Gateway API status reports everything healthy.

Analysis

operator/pkg/model/model.go: NeedsPerPortListeners() is true whenever
len(TLSPassthroughPorts()) > 1, which routes translation to the per-port path where listeners are
named listener-<port>. In that path an HTTPS listener and a passthrough listener on the same port
both render listener-<port>, with nothing distinguishing them by protocol.

NeedsCrossProtocolSplit() — the predicate that reasons about HTTPS/passthrough coexistence —
explicitly continues when httpsMatch.port == tlsMatch.port, so same-port pairs are never
examined.

Two things seem worth separating: the naming collision itself, and the fact that a malformed CEC
takes down every unrelated listener on the gateway rather than failing closed on the offending one.
The second is arguably the more serious of the two for anyone running a shared gateway.

Related

Activity

  1. youngnick commented on Sep 1, 2026

    @youngnick
    Contributor

    Hi, please make sure to review Generative AI Policy, and ensure you edit AI-generated reports before submitting them as issues.

    It's actually quite hard to understand the first paragraph, does that mean that you've validated that this is an issue on v1.20.0, but couldn't validate that the problem would reoccur as of Cilium v1.20.1?

    Because there was also #47457 that prevents this issue from taking down the entire config by rejecting the conflicted listeners, and @asauber and others are already working on further fixes. #47457 was already included in v1.20.1, so I think that this issue is already fixed.

    Please confirm this sort of issue on the most recent patch if possible before opening.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions