Skip to content

fix: resolve FrontendTLS CA cert refs in Gateway namespace, not ListenerSet#14232

Merged
puertomontt merged 8 commits into
kgateway-dev:mainfrom
ayushi-work:fix/frontend-tls-listenerSet-namespace
Jun 23, 2026
Merged

fix: resolve FrontendTLS CA cert refs in Gateway namespace, not ListenerSet#14232
puertomontt merged 8 commits into
kgateway-dev:mainfrom
ayushi-work:fix/frontend-tls-listenerSet-namespace

Conversation

@ayushi-work

@ayushi-work ayushi-work commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes a bug where frontend TLS client certificate validation fails for listeners contributed by ListenerSet resources, because the control plane looks for the CA certificate ConfigMap/Secret in the wrong namespace (the ListenerSet's namespace instead of the Gateway's).

Fixes #14230

Problem

When a Gateway specifies spec.tls.frontend with clientCertificateValidation pointing to a ConfigMap in the same namespace, and a ListenerSet in a different namespace binds to that Gateway, the ListenerSet's listeners never become programmed. The status reports that the referenced ConfigMap can't be found in the ListenerSet's namespace.

Root Cause

In applyClientCertificateValidation, both the parentGVK and the namespace used to resolve CA certificate references were derived from listener.Parent:

parentGVK := listener.Parent.GetObjectKind().GroupVersionKind()
...
listener.Parent.GetNamespace()

For listeners from a ListenerSet, listener.Parent is the *gwv1.ListenerSet (set at pkg/krtcollections/policy.go:567), so the namespace resolves to the ListenerSet's namespace. But the CA cert refs come from the Gateway's FrontendTLSConfig -- a Gateway-level configuration that should always be resolved relative to the Gateway's namespace.

The same function is called for two distinct cases with different ownership:

Call site (in translateTLSConfig) Config source Who owns the CA cert refs Correct namespace
Line 1007 Gateway FrontendTLSConfig (via resolveFrontendTLSConfig) Gateway Gateway's namespace (was wrong for ListenerSet listeners)
Line 1032 ListenerPolicy attachment (via getCertValidationFromAttached) The listener's parent listener.Parent.GetNamespace() (was already correct)

Fix

Make applyClientCertificateValidation accept explicit parentGVK and parentNamespace parameters instead of deriving them from listener.Parent. At each call site, the caller provides the values that match the config's actual owner:

  • Gateway FrontendTLSConfig: wellknown.GatewayGVK + gatewayNamespace (the Gateway's namespace)
  • ListenerPolicy: GVK derived from listener.Parent + listener.Parent.GetNamespace() (preserving existing behavior)

The Gateway namespace is threaded from MergedListener.TranslateListener (where ml.gateway.Namespace is available) through translateHttpsFilterChain / translateTcpFilterChain / translateTLSConfig.

Change Type

/kind fix

Changelog

fix: resolve FrontendTLS CA certificate references in the Gateway's namespace when listeners are contributed by a ListenerSet, rather than incorrectly looking in the ListenerSet's namespace.

Test Plan

  • Added yaml-based translator test listenerset-cross-ns.yaml that reproduces the exact bug scenario (Gateway in namespace-one with frontend TLS, ListenerSet in namespace-two)
  • Verified the golden output shows the ListenerSet is Programmed with CA cert validation applied from the Gateway's namespace
  • All existing FrontendTLSConfig tests continue to pass
  • All listener translator unit tests pass

Copilot AI review requested due to automatic review settings June 11, 2026 11:25
@gateway-bot gateway-bot added do-not-merge/description-invalid kind/fix Categorizes issue or PR as related to a bug. release-note labels Jun 11, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR fixes FrontendTLS client-certificate validation lookups when a ListenerSet is in a different namespace than its parent Gateway, ensuring CA certificate references are resolved using the Gateway’s namespace rather than the ListenerSet’s.

Changes:

  • Thread gatewayNamespace through listener/filter-chain translation so TLS client validation uses the Gateway namespace.
  • Refactor applyClientCertificateValidation to accept explicit parent GVK/namespace instead of deriving from listener.Parent.
  • Add an integration-style translator test fixture (input/output YAML) and a Go test covering the cross-namespace ListenerSet scenario.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
pkg/kgateway/translator/listener/gateway_listener_translator.go Passes Gateway namespace through translation and updates client-cert validation parent context.
pkg/kgateway/translator/gateway/testutils/inputs/frontendtlsconfig/listenerset-cross-ns.yaml Adds a repro input for cross-namespace ListenerSet + Gateway FrontendTLS CA ref.
pkg/kgateway/translator/gateway/testutils/outputs/frontendtlsconfig/listenerset-cross-ns.yaml Adds expected translated output verifying correct CA resolution/validation behavior.
pkg/kgateway/translator/gateway/gateway_translator_test.go Adds a test case that executes the new input/output fixture.

Comment on lines 1034 to 1047
// Check if ListenerPolicy has clientCertificateValidation override
if listenerPolCertVal := getCertValidationFromAttached(listener); listenerPolCertVal != nil {
// Apply ListenerPolicy override, which takes precedence over Gateway-level config
generated, caErr := applyClientCertificateValidation(kctx, ctx, queries, listener, listenerPolCertVal, tlsConfig)
listenerParentGVK := listener.Parent.GetObjectKind().GroupVersionKind()
if listenerParentGVK.Empty() {
switch listener.Parent.(type) {
case *gwv1.Gateway:
listenerParentGVK = wellknown.GatewayGVK
case *gwv1.ListenerSet:
listenerParentGVK = wellknown.XListenerSetGVK
}
}
generated, caErr := applyClientCertificateValidation(kctx, ctx, queries, listenerPolCertVal, tlsConfig, listenerParentGVK, listener.Parent.GetNamespace())
if !generated {
Comment on lines +1037 to +1046
listenerParentGVK := listener.Parent.GetObjectKind().GroupVersionKind()
if listenerParentGVK.Empty() {
switch listener.Parent.(type) {
case *gwv1.Gateway:
listenerParentGVK = wellknown.GatewayGVK
case *gwv1.ListenerSet:
listenerParentGVK = wellknown.XListenerSetGVK
}
}
generated, caErr := applyClientCertificateValidation(kctx, ctx, queries, listenerPolCertVal, tlsConfig, listenerParentGVK, listener.Parent.GetNamespace())
@ayushi-work
ayushi-work force-pushed the fix/frontend-tls-listenerSet-namespace branch from fedaba5 to 5908794 Compare June 11, 2026 11:30
@ayushi-work
ayushi-work force-pushed the fix/frontend-tls-listenerSet-namespace branch from 5908794 to 1810efb Compare June 11, 2026 12:15
@ayushi-work
ayushi-work requested review from Copilot June 11, 2026 12:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (2)

pkg/kgateway/translator/listener/gateway_listener_translator.go:1

  • Adding gatewayNamespace as an additional positional parameter to multiple translator functions increases signature churn and makes call sites easier to get wrong over time. Consider encapsulating these shared translation inputs (e.g., gateway identity/namespace, frontend TLS config) into a small struct/context object passed through translation, which tends to be more stable and self-documenting than repeatedly extending function signatures.
package listener

pkg/kgateway/translator/listener/gateway_listener_translator.go:1

  • Adding gatewayNamespace as an additional positional parameter to multiple translator functions increases signature churn and makes call sites easier to get wrong over time. Consider encapsulating these shared translation inputs (e.g., gateway identity/namespace, frontend TLS config) into a small struct/context object passed through translation, which tends to be more stable and self-documenting than repeatedly extending function signatures.
package listener

// For AllowInsecureFallback mode, if CA cert fetching fails, skip validation rather than failing the listener
// This allows the listener to work without client certs even if the CA cert ConfigMap is missing
generated, caErr = applyClientCertificateValidation(kctx, ctx, queries, listener, resolvedValidation, tlsConfig)
generated, caErr = applyClientCertificateValidation(kctx, ctx, queries, resolvedValidation, tlsConfig, wellknown.GatewayGVK, gatewayNamespace)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pre-existing behavior, not introduced by this PR. The original code already shared caErr between the two paths. Both results are joined into the final return at the end of translateTLSConfig via errors.Join(certErr, caErr). This PR does not change the error handling pattern.

Comment on lines +1037 to +1048
listenerParentGVK := listener.Parent.GetObjectKind().GroupVersionKind()
if listenerParentGVK.Empty() {
switch listener.Parent.(type) {
case *gwv1.Gateway:
listenerParentGVK = wellknown.GatewayGVK
case *gwv1.ListenerSet:
listenerParentGVK = wellknown.XListenerSetGVK
default:
return nil, fmt.Errorf("unsupported parent type for ListenerPolicy clientCertificateValidation: %T", listener.Parent)
}
}
generated, caErr = applyClientCertificateValidation(kctx, ctx, queries, listenerPolCertVal, tlsConfig, listenerParentGVK, listener.Parent.GetNamespace())

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pre-existing behavior, not introduced by this PR. The original code already shared caErr between the two paths. Both results are joined into the final return via errors.Join(certErr, caErr).

var matchedTcpListeners []ir.TcpIR
for _, tfc := range ml.TcpFilterChains {
if tcpListener := tfc.translateTcpFilterChain(kctx, ctx, queries, ml.name, reporter, ml.gateway.FrontendTLSConfig); tcpListener != nil {
if tcpListener := tfc.translateTcpFilterChain(kctx, ctx, queries, ml.name, reporter, ml.gateway.FrontendTLSConfig, ml.gateway.Namespace); tcpListener != nil {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change in translateTcpFilterChain is purely mechanical -- adding a gatewayNamespace parameter and threading it through to translateTLSConfig. No logic changed. The HTTPS test confirms the fix works for the reported bug.

}

tlsConfig, err := translateTLSConfig(kctx, ctx, tc.parents.listener, tc.tls, queries, resolvedValidation)
tlsConfig, err := translateTLSConfig(kctx, ctx, tc.parents.listener, tc.tls, queries, resolvedValidation, gatewayNamespace)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same mechanical change as line 383 -- just threading gatewayNamespace through.

}

tlsConfig, err := translateTLSConfig(kctx, ctx, tc.parents.listener, tc.tls, queries, resolvedValidation)
tlsConfig, err := translateTLSConfig(kctx, ctx, tc.parents.listener, tc.tls, queries, resolvedValidation, gatewayNamespace)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same mechanical change as line 383 -- just threading gatewayNamespace through.

@puertomontt
puertomontt added this pull request to the merge queue Jun 15, 2026
@puertomontt
puertomontt removed this pull request from the merge queue due to a manual request Jun 15, 2026

@puertomontt puertomontt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about reference grant

@puertomontt
puertomontt dismissed their stale review June 15, 2026 15:04

need to think about this a bit more

@ayushi-work

Copy link
Copy Markdown
Contributor Author

what about reference grant

The code before derived parentGVK and parentNamespace from listener.Parent. For ListenerSet listeners, that checked ReferenceGrants against the ListenerSet, wrong referent, wrong namespace.

After: for Gateway FrontendTLSConfig, we pass wellknown.GatewayGVK + the Gateway's namespace, so ReferenceGrants are checked against the Gateway, the actual owner of the frontend TLS config.

The existing frontendtlsconfig/basic.yaml test covers cross-namespace ConfigMap + ReferenceGrant, but only with Gateway-owned listeners. I'll add a variant of our ListenerSet test where the ConfigMap is in a separate namespace and a ReferenceGrant allows the Gateway to access it, confirming that path works correctly with a ListenerSet.

@ayushi-work
ayushi-work force-pushed the fix/frontend-tls-listenerSet-namespace branch from 1810efb to aebd1d2 Compare June 15, 2026 15:20
puertomontt
puertomontt previously approved these changes Jun 15, 2026
@puertomontt
puertomontt dismissed their stale review June 15, 2026 15:20

accident

@ayushi-work
ayushi-work requested a review from puertomontt June 15, 2026 16:12
@puertomontt
puertomontt enabled auto-merge June 16, 2026 22:26
@puertomontt
puertomontt added this pull request to the merge queue Jun 17, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 17, 2026
@ayushi-work

Copy link
Copy Markdown
Contributor Author

Hi @puertomontt The PR was removed from the merge queue due to a CI runner disk space issue
No space left on device
I verified the changes locally and all relevant tests pass. Could you please re-trigger the failed jobs?

@puertomontt
puertomontt enabled auto-merge June 18, 2026 18:47
@puertomontt
puertomontt added this pull request to the merge queue Jun 18, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 18, 2026
@ayushi-work

ayushi-work commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

@puertomontt After this branch last synced with main, PR #13978 added commonLbConfig.localityWeightedLbConfig: {} to all cluster outputs in the translator. The merge queue creates a temporary merge with current main, which now includes #13978 , so the golden file listenerset-cross-ns-refgrant.yaml that was authored on this branch after that last sync, was missing the new field and the test comparison failed.
This only surfaces on the merge queue, not the PR branch checks, because the PR branch CI runs against the main HEAD it was synced with, which did not have #13978 yet.
Fix: merged latest main and regenerated the golden file with REFRESH_GOLDEN=true.
Could you please review this again?

@ayushi-work
ayushi-work force-pushed the fix/frontend-tls-listenerSet-namespace branch from 0f46f45 to 4db14da Compare June 18, 2026 20:16
@ayushi-work
ayushi-work requested a review from puertomontt June 19, 2026 10:33
@puertomontt
puertomontt enabled auto-merge June 19, 2026 11:11
auto-merge was automatically disabled June 19, 2026 11:47

Head branch was pushed to by a user without write access

Signed-off-by: ayushi-work <[email protected]>
@ayushi-work
ayushi-work force-pushed the fix/frontend-tls-listenerSet-namespace branch from 7137b43 to 5a01362 Compare June 19, 2026 11:47
@ayushi-work
ayushi-work requested a review from puertomontt June 19, 2026 11:48
@ayushi-work

Copy link
Copy Markdown
Contributor Author

Hi! Is there something else that needs to be done?

@puertomontt
puertomontt added this pull request to the merge queue Jun 23, 2026
Merged via the queue into kgateway-dev:main with commit 809b4bf Jun 23, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/fix Categorizes issue or PR as related to a bug. release-note

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Frontend TLS reference Issue with ListenerSet resource

4 participants