Skip to content

Commit d84e663

Browse files
committed
fix(gateway): use TCP target group protocol for TLSRoute on passthrough NLB listeners
Motivation: Issue #4556 reported that a Gateway API NLB listener with `protocol: TLS` and `tls.mode: Passthrough` incorrectly built a TLS listener. That part was already fixed in v3.1.0 by downgrading the listener protocol to TCP for passthrough mode. However, follow-up reports on the same issue (from 2026-03-04 and 2026-03-10) show the fix was incomplete: when a `TLSRoute` is bound to such a passthrough listener, the controller still infers the *target group* protocol as TLS. This produces a real AWS API error when reconciling the load balancer: IncompatibleProtocols: The listener and the following target groups have incompatible protocols because the (now TCP) listener cannot be attached to a TLS target group. This is a functional failure, not cosmetic: the Gateway never reaches a working state and reconciliation errors repeatedly. Approach: `inferTargetGroupProtocolFromRoute` in pkg/gateway/model/model_build_target_group.go always returned `ProtocolTLS` for `TLSRouteKind` on an NLB, regardless of the actual listener protocol. It now takes the (already passthrough-downgraded) listener protocol and returns `ProtocolTCP` when the listener protocol is TCP, keeping `ProtocolTLS` for the `tls.mode: Terminate` case. Users who explicitly set `targetGroupProps.Protocol` are unaffected, since that explicit-override path is checked before the inference path. This only changes NLB behavior for TLSRoute; ALB target group protocol inference (which cannot take this NLB-only branch) is unaffected. Validation: go build ./... go test -count=1 ./pkg/gateway/model/... Both pass, including a corrected unit test case in Test_buildTargetGroupProtocol that had been asserting the buggy (mismatched) behavior. Fixes #4556 Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
1 parent 3d15af0 commit d84e663

2 files changed

Lines changed: 13 additions & 7 deletions

File tree

pkg/gateway/model/model_build_target_group.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -355,15 +355,15 @@ func (builder *targetGroupBuilderImpl) buildTargetGroupIPAddressType(backendConf
355355
func (builder *targetGroupBuilderImpl) buildTargetGroupProtocol(targetGroupProps *elbv2gw.TargetGroupProps, route routeutils.RouteDescriptor, listenerProtocol elbv2model.Protocol) (elbv2model.Protocol, error) {
356356
// TODO - Not convinced that this is good, maybe auto detect certs == HTTPS / TLS.
357357
if builder.loadBalancerType == elbv2model.LoadBalancerTypeApplication {
358-
return builder.buildL7TargetGroupProtocol(targetGroupProps, route)
358+
return builder.buildL7TargetGroupProtocol(targetGroupProps, route, listenerProtocol)
359359
}
360360

361361
return builder.buildL4TargetGroupProtocol(targetGroupProps, route, listenerProtocol)
362362
}
363363

364-
func (builder *targetGroupBuilderImpl) buildL7TargetGroupProtocol(targetGroupProps *elbv2gw.TargetGroupProps, route routeutils.RouteDescriptor) (elbv2model.Protocol, error) {
364+
func (builder *targetGroupBuilderImpl) buildL7TargetGroupProtocol(targetGroupProps *elbv2gw.TargetGroupProps, route routeutils.RouteDescriptor, listenerProtocol elbv2model.Protocol) (elbv2model.Protocol, error) {
365365
if targetGroupProps == nil || targetGroupProps.Protocol == nil {
366-
return builder.inferTargetGroupProtocolFromRoute(route), nil
366+
return builder.inferTargetGroupProtocolFromRoute(route, listenerProtocol), nil
367367
}
368368
switch string(*targetGroupProps.Protocol) {
369369
case string(elbv2model.ProtocolHTTP):
@@ -381,7 +381,7 @@ func (builder *targetGroupBuilderImpl) buildL4TargetGroupProtocol(targetGroupPro
381381
}
382382

383383
if targetGroupProps == nil || targetGroupProps.Protocol == nil {
384-
return builder.inferTargetGroupProtocolFromRoute(route), nil
384+
return builder.inferTargetGroupProtocolFromRoute(route, listenerProtocol), nil
385385
}
386386

387387
switch string(*targetGroupProps.Protocol) {
@@ -398,7 +398,7 @@ func (builder *targetGroupBuilderImpl) buildL4TargetGroupProtocol(targetGroupPro
398398
}
399399
}
400400

401-
func (builder *targetGroupBuilderImpl) inferTargetGroupProtocolFromRoute(route routeutils.RouteDescriptor) elbv2model.Protocol {
401+
func (builder *targetGroupBuilderImpl) inferTargetGroupProtocolFromRoute(route routeutils.RouteDescriptor, listenerProtocol elbv2model.Protocol) elbv2model.Protocol {
402402
switch route.GetRouteKind() {
403403
case routeutils.TCPRouteKind:
404404
return elbv2model.ProtocolTCP
@@ -410,6 +410,12 @@ func (builder *targetGroupBuilderImpl) inferTargetGroupProtocolFromRoute(route r
410410
return elbv2model.ProtocolHTTP
411411
case routeutils.TLSRouteKind:
412412
if builder.loadBalancerType == elbv2model.LoadBalancerTypeNetwork {
413+
// A TLS listener with tls.mode: Passthrough is provisioned as a TCP listener
414+
// (see mapGatewayListenerConfigsByPort), so the target group protocol must be
415+
// TCP as well, otherwise the listener and target group protocols mismatch.
416+
if listenerProtocol == elbv2model.ProtocolTCP {
417+
return elbv2model.ProtocolTCP
418+
}
413419
return elbv2model.ProtocolTLS
414420
}
415421
return elbv2model.ProtocolHTTPS

pkg/gateway/model/model_build_target_group_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -980,15 +980,15 @@ func Test_buildTargetGroupProtocol(t *testing.T) {
980980
expected: elbv2model.ProtocolUDP,
981981
},
982982
{
983-
name: "nlb - auto detect - tls",
983+
name: "nlb - auto detect - tls passthrough listener downgraded to tcp",
984984
listenerProtocol: elbv2model.ProtocolTCP,
985985
lbType: elbv2model.LoadBalancerTypeNetwork,
986986
route: &routeutils.MockRoute{
987987
Kind: routeutils.TLSRouteKind,
988988
Name: "r1",
989989
Namespace: "ns",
990990
},
991-
expected: elbv2model.ProtocolTLS,
991+
expected: elbv2model.ProtocolTCP,
992992
},
993993
{
994994
name: "alb - specified - http",

0 commit comments

Comments
 (0)