From 5410755e9adbb7403a7f69298d7c53d5ac798c31 Mon Sep 17 00:00:00 2001 From: Rostislav Bobrovsky Date: Mon, 19 Jan 2026 13:57:55 +0000 Subject: [PATCH 1/6] conformance: add TLSRoute hostname intersection conformance tests --- .../tests/tlsroute-hostname-intersection.go | 117 ++++++++++++++++++ .../tests/tlsroute-hostname-intersection.yaml | 106 ++++++++++++++++ conformance/utils/kubernetes/helpers.go | 10 ++ 3 files changed, 233 insertions(+) create mode 100644 conformance/tests/tlsroute-hostname-intersection.go create mode 100644 conformance/tests/tlsroute-hostname-intersection.yaml diff --git a/conformance/tests/tlsroute-hostname-intersection.go b/conformance/tests/tlsroute-hostname-intersection.go new file mode 100644 index 0000000000..6a49d2ced8 --- /dev/null +++ b/conformance/tests/tlsroute-hostname-intersection.go @@ -0,0 +1,117 @@ +/* +Copyright 2026 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tests + +import ( + "testing" + + "k8s.io/apimachinery/pkg/types" + + "sigs.k8s.io/gateway-api/conformance/utils/http" + "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" + "sigs.k8s.io/gateway-api/conformance/utils/suite" + "sigs.k8s.io/gateway-api/conformance/utils/tls" + "sigs.k8s.io/gateway-api/pkg/features" +) + +func init() { + ConformanceTests = append(ConformanceTests, TLSRouteHostnameIntersection) +} + +var TLSRouteHostnameIntersection = suite.ConformanceTest{ + ShortName: "TLSRouteHostnameIntersection", + Description: "TLSRoutes should attach to listeners only if they have intersecting hostnames, and should accept requests only for the intersecting hostnames", + Features: []features.FeatureName{ + features.SupportGateway, + features.SupportTLSRoute, + }, + Manifests: []string{"tests/tlsroute-hostname-intersection.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + ns := "gateway-conformance-infra" + certNN := types.NamespacedName{Name: "tls-checks-certificate", Namespace: ns} + + // This test creates an additional Gateway in the gateway-conformance-infra + // namespace so we have to wait for it to be ready. + kubernetes.NamespacesMustBeReady(t, suite.Client, suite.TimeoutConfig, []string{ns}) + + t.Run("TLSRoutes with wildcard hostname intersects with exact listener hostname", func(t *testing.T) { + routeNN := types.NamespacedName{Namespace: ns, Name: "tlsroute-more-specific-wildcard-hostname"} + gwNN := types.NamespacedName{Name: "gateway-exact-hostname", Namespace: ns} + gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) + + serverCertPem, _, err := GetTLSSecret(suite.Client, certNN) + if err != nil { + t.Fatalf("unexpected error finding TLS secret: %v", err) + } + + t.Run("Simple TLS request matching hostnames intersection should reach backend", func(t *testing.T) { + tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "abc.example.com", + http.ExpectedResponse{ + Request: http.Request{Host: "abc.example.com", Path: "/"}, + Backend: "tls-backend", + Namespace: ns, + }) + }) + }) + + t.Run("TLSRoutes with exact hostname intersects with wildcard listener hostname", func(t *testing.T) { + routeNN := types.NamespacedName{Namespace: ns, Name: "tlsroute-exact-hostname"} + gwNN := types.NamespacedName{Name: "gateway-more-specific-wildcard-hostname", Namespace: ns} + gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) + + serverCertPem, _, err := GetTLSSecret(suite.Client, certNN) + if err != nil { + t.Fatalf("unexpected error finding TLS secret: %v", err) + } + + t.Run("Simple TLS request matching hostnames intersection should reach backend", func(t *testing.T) { + tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "abc.example.com", + http.ExpectedResponse{ + Request: http.Request{Host: "abc.example.com", Path: "/"}, + Backend: "tls-backend", + Namespace: ns, + }) + }) + }) + + t.Run("TLSRoute with wildcard hostname intersects with empty listener hostname", func(t *testing.T) { + routeNN := types.NamespacedName{Namespace: ns, Name: "tlsroute-less-specific-wildcard-hostname"} + gwNN := types.NamespacedName{Name: "gateway-empty-hostname", Namespace: ns} + gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) + + serverCertPem, _, err := GetTLSSecret(suite.Client, certNN) + if err != nil { + t.Fatalf("unexpected error finding TLS secret: %v", err) + } + + t.Run("Simple TLS request matching hostnames intersection should reach backend", func(t *testing.T) { + tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "other.example.com", + http.ExpectedResponse{ + Request: http.Request{Host: "other.example.com", Path: "/"}, + Backend: "tls-backend", + Namespace: ns, + }) + }) + }) + }, +} diff --git a/conformance/tests/tlsroute-hostname-intersection.yaml b/conformance/tests/tlsroute-hostname-intersection.yaml new file mode 100644 index 0000000000..1cfff93ce2 --- /dev/null +++ b/conformance/tests/tlsroute-hostname-intersection.yaml @@ -0,0 +1,106 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: gateway-exact-hostname + namespace: gateway-conformance-infra +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + listeners: + - name: listener-exact-hostname + protocol: TLS + port: 443 + hostname: abc.example.com + allowedRoutes: + namespaces: + from: Same + kinds: + - kind: TLSRoute + tls: + mode: Passthrough +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: gateway-more-specific-wildcard-hostname + namespace: gateway-conformance-infra +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + listeners: + - name: listener-more-specific-wildcard-hostname + protocol: TLS + port: 443 + hostname: "*.example.com" + allowedRoutes: + namespaces: + from: Same + kinds: + - kind: TLSRoute + tls: + mode: Passthrough +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: gateway-empty-hostname + namespace: gateway-conformance-infra +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + listeners: + - name: listener-empty-hostname + protocol: TLS + port: 443 + allowedRoutes: + namespaces: + from: Same + kinds: + - kind: TLSRoute + tls: + mode: Passthrough +--- +apiVersion: gateway.networking.k8s.io/v1alpha3 +kind: TLSRoute +metadata: + name: tlsroute-exact-hostname + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: gateway-more-specific-wildcard-hostname + namespace: gateway-conformance-infra + hostnames: + - abc.example.com + rules: + - backendRefs: + - name: tls-backend + port: 443 +--- +apiVersion: gateway.networking.k8s.io/v1alpha3 +kind: TLSRoute +metadata: + name: tlsroute-more-specific-wildcard-hostname + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: gateway-exact-hostname + namespace: gateway-conformance-infra + hostnames: + - "*.example.com" + rules: + - backendRefs: + - name: tls-backend + port: 443 +--- +apiVersion: gateway.networking.k8s.io/v1alpha3 +kind: TLSRoute +metadata: + name: tlsroute-less-specific-wildcard-hostname + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: gateway-empty-hostname + namespace: gateway-conformance-infra + hostnames: + - "*.com" + rules: + - backendRefs: + - name: tls-backend + port: 443 diff --git a/conformance/utils/kubernetes/helpers.go b/conformance/utils/kubernetes/helpers.go index 6bc0b179c5..ae6a40a0a5 100644 --- a/conformance/utils/kubernetes/helpers.go +++ b/conformance/utils/kubernetes/helpers.go @@ -1097,6 +1097,16 @@ func ListenerSetListenersMustHaveConditions(t *testing.T, client client.Client, require.NoErrorf(t, waitErr, "error waiting for ListenerSet status to have conditions matching expectations on the listeners") } +// TLSRouteMustHaveResolvedRefsConditionsTrue checks that the supplied TLSRoute has the resolvedRefsCondition +// set to true. +func TLSRouteMustHaveResolvedRefsConditionsTrue(t *testing.T, client client.Client, timeoutConfig config.TimeoutConfig, routeNN types.NamespacedName, gwNN types.NamespacedName) { + TLSRouteMustHaveCondition(t, client, timeoutConfig, routeNN, gwNN, metav1.Condition{ + Type: string(gatewayv1.RouteConditionResolvedRefs), + Status: metav1.ConditionTrue, + Reason: string(gatewayv1.RouteReasonResolvedRefs), + }) +} + // TODO(mikemorris): this and parentsMatch could possibly be rewritten as a generic function? func listenersMatch(t *testing.T, expected, actual []gatewayv1.ListenerStatus) bool { t.Helper() From 36ffe92590377d6042d857ee45d4c4d20ac03920 Mon Sep 17 00:00:00 2001 From: Rostislav Bobrovsky Date: Wed, 21 Jan 2026 11:35:02 +0000 Subject: [PATCH 2/6] TLSRoute: Indicate tlsroute in Gateway names --- conformance/tests/tlsroute-hostname-intersection.go | 6 +++--- .../tests/tlsroute-hostname-intersection.yaml | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/conformance/tests/tlsroute-hostname-intersection.go b/conformance/tests/tlsroute-hostname-intersection.go index 6a49d2ced8..e7e2a61b7f 100644 --- a/conformance/tests/tlsroute-hostname-intersection.go +++ b/conformance/tests/tlsroute-hostname-intersection.go @@ -50,7 +50,7 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ t.Run("TLSRoutes with wildcard hostname intersects with exact listener hostname", func(t *testing.T) { routeNN := types.NamespacedName{Namespace: ns, Name: "tlsroute-more-specific-wildcard-hostname"} - gwNN := types.NamespacedName{Name: "gateway-exact-hostname", Namespace: ns} + gwNN := types.NamespacedName{Name: "gateway-tlsroute-exact-hostname-intersection", Namespace: ns} gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) @@ -72,7 +72,7 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ t.Run("TLSRoutes with exact hostname intersects with wildcard listener hostname", func(t *testing.T) { routeNN := types.NamespacedName{Namespace: ns, Name: "tlsroute-exact-hostname"} - gwNN := types.NamespacedName{Name: "gateway-more-specific-wildcard-hostname", Namespace: ns} + gwNN := types.NamespacedName{Name: "gateway-tlsroute-more-specific-wildcard-hostname-intersection", Namespace: ns} gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) @@ -94,7 +94,7 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ t.Run("TLSRoute with wildcard hostname intersects with empty listener hostname", func(t *testing.T) { routeNN := types.NamespacedName{Namespace: ns, Name: "tlsroute-less-specific-wildcard-hostname"} - gwNN := types.NamespacedName{Name: "gateway-empty-hostname", Namespace: ns} + gwNN := types.NamespacedName{Name: "gateway-tlsroute-empty-hostname-intersection", Namespace: ns} gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) diff --git a/conformance/tests/tlsroute-hostname-intersection.yaml b/conformance/tests/tlsroute-hostname-intersection.yaml index 1cfff93ce2..9108fa6567 100644 --- a/conformance/tests/tlsroute-hostname-intersection.yaml +++ b/conformance/tests/tlsroute-hostname-intersection.yaml @@ -1,7 +1,7 @@ apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: - name: gateway-exact-hostname + name: gateway-tlsroute-exact-hostname-intersection namespace: gateway-conformance-infra spec: gatewayClassName: "{GATEWAY_CLASS_NAME}" @@ -21,7 +21,7 @@ spec: apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: - name: gateway-more-specific-wildcard-hostname + name: gateway-tlsroute-more-specific-wildcard-hostname-intersection namespace: gateway-conformance-infra spec: gatewayClassName: "{GATEWAY_CLASS_NAME}" @@ -41,7 +41,7 @@ spec: apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: - name: gateway-empty-hostname + name: gateway-tlsroute-empty-hostname-intersection namespace: gateway-conformance-infra spec: gatewayClassName: "{GATEWAY_CLASS_NAME}" @@ -64,7 +64,7 @@ metadata: namespace: gateway-conformance-infra spec: parentRefs: - - name: gateway-more-specific-wildcard-hostname + - name: gateway-tlsroute-more-specific-wildcard-hostname-intersection namespace: gateway-conformance-infra hostnames: - abc.example.com @@ -80,7 +80,7 @@ metadata: namespace: gateway-conformance-infra spec: parentRefs: - - name: gateway-exact-hostname + - name: gateway-tlsroute-exact-hostname-intersection namespace: gateway-conformance-infra hostnames: - "*.example.com" @@ -96,7 +96,7 @@ metadata: namespace: gateway-conformance-infra spec: parentRefs: - - name: gateway-empty-hostname + - name: gateway-tlsroute-empty-hostname-intersection namespace: gateway-conformance-infra hostnames: - "*.com" From d5e5d30923a2bd6542c7a5b5d718e1bf8a03cd40 Mon Sep 17 00:00:00 2001 From: Rostislav Bobrovsky Date: Fri, 23 Jan 2026 14:12:28 +0000 Subject: [PATCH 3/6] TLSRoute: Add test for wildcard hostname intersects with wildcard listener hostname --- .../tests/tlsroute-hostname-intersection.go | 22 ++++++++++++ .../tests/tlsroute-hostname-intersection.yaml | 36 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/conformance/tests/tlsroute-hostname-intersection.go b/conformance/tests/tlsroute-hostname-intersection.go index e7e2a61b7f..64fe3b59df 100644 --- a/conformance/tests/tlsroute-hostname-intersection.go +++ b/conformance/tests/tlsroute-hostname-intersection.go @@ -92,6 +92,28 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ }) }) + t.Run("TLSRoutes with wildcard hostname intersects with wildcard listener hostname", func(t *testing.T) { + routeNN := types.NamespacedName{Namespace: ns, Name: "tlsroute-more-specific-wildcard-hostname-2"} + gwNN := types.NamespacedName{Name: "gateway-tlsroute-less-specific-wildcard-hostname-intersection", Namespace: ns} + gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) + + serverCertPem, _, err := GetTLSSecret(suite.Client, certNN) + if err != nil { + t.Fatalf("unexpected error finding TLS secret: %v", err) + } + + t.Run("Simple TLS request matching hostnames intersection should reach backend", func(t *testing.T) { + tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "other.example.com", + http.ExpectedResponse{ + Request: http.Request{Host: "other.example.com", Path: "/"}, + Backend: "tls-backend", + Namespace: ns, + }) + }) + }) + t.Run("TLSRoute with wildcard hostname intersects with empty listener hostname", func(t *testing.T) { routeNN := types.NamespacedName{Namespace: ns, Name: "tlsroute-less-specific-wildcard-hostname"} gwNN := types.NamespacedName{Name: "gateway-tlsroute-empty-hostname-intersection", Namespace: ns} diff --git a/conformance/tests/tlsroute-hostname-intersection.yaml b/conformance/tests/tlsroute-hostname-intersection.yaml index 9108fa6567..7f443ccef6 100644 --- a/conformance/tests/tlsroute-hostname-intersection.yaml +++ b/conformance/tests/tlsroute-hostname-intersection.yaml @@ -40,6 +40,26 @@ spec: --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway +metadata: + name: gateway-tlsroute-less-specific-wildcard-hostname-intersection + namespace: gateway-conformance-infra +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + listeners: + - name: listener-less-specific-wildcard-hostname + protocol: TLS + port: 443 + hostname: "*.com" + allowedRoutes: + namespaces: + from: Same + kinds: + - kind: TLSRoute + tls: + mode: Passthrough +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway metadata: name: gateway-tlsroute-empty-hostname-intersection namespace: gateway-conformance-infra @@ -91,6 +111,22 @@ spec: --- apiVersion: gateway.networking.k8s.io/v1alpha3 kind: TLSRoute +metadata: + name: tlsroute-more-specific-wildcard-hostname-2 + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: gateway-tlsroute-less-specific-wildcard-hostname-intersection + namespace: gateway-conformance-infra + hostnames: + - "*.example.com" + rules: + - backendRefs: + - name: tls-backend + port: 443 +--- +apiVersion: gateway.networking.k8s.io/v1alpha3 +kind: TLSRoute metadata: name: tlsroute-less-specific-wildcard-hostname namespace: gateway-conformance-infra From 6c1aaa08435d12e6e1fb7336db8f4ac39366c8cf Mon Sep 17 00:00:00 2001 From: Rostislav Bobrovsky Date: Fri, 23 Jan 2026 14:14:11 +0000 Subject: [PATCH 4/6] TLSRoute: Fetch the Secret once and reuse it --- .../tests/tlsroute-hostname-intersection.go | 25 ++++--------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/conformance/tests/tlsroute-hostname-intersection.go b/conformance/tests/tlsroute-hostname-intersection.go index 64fe3b59df..8aff39b824 100644 --- a/conformance/tests/tlsroute-hostname-intersection.go +++ b/conformance/tests/tlsroute-hostname-intersection.go @@ -48,6 +48,11 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ // namespace so we have to wait for it to be ready. kubernetes.NamespacesMustBeReady(t, suite.Client, suite.TimeoutConfig, []string{ns}) + serverCertPem, _, err := GetTLSSecret(suite.Client, certNN) + if err != nil { + t.Fatalf("unexpected error finding TLS secret: %v", err) + } + t.Run("TLSRoutes with wildcard hostname intersects with exact listener hostname", func(t *testing.T) { routeNN := types.NamespacedName{Namespace: ns, Name: "tlsroute-more-specific-wildcard-hostname"} gwNN := types.NamespacedName{Name: "gateway-tlsroute-exact-hostname-intersection", Namespace: ns} @@ -55,11 +60,6 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) - serverCertPem, _, err := GetTLSSecret(suite.Client, certNN) - if err != nil { - t.Fatalf("unexpected error finding TLS secret: %v", err) - } - t.Run("Simple TLS request matching hostnames intersection should reach backend", func(t *testing.T) { tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "abc.example.com", http.ExpectedResponse{ @@ -77,11 +77,6 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) - serverCertPem, _, err := GetTLSSecret(suite.Client, certNN) - if err != nil { - t.Fatalf("unexpected error finding TLS secret: %v", err) - } - t.Run("Simple TLS request matching hostnames intersection should reach backend", func(t *testing.T) { tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "abc.example.com", http.ExpectedResponse{ @@ -99,11 +94,6 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) - serverCertPem, _, err := GetTLSSecret(suite.Client, certNN) - if err != nil { - t.Fatalf("unexpected error finding TLS secret: %v", err) - } - t.Run("Simple TLS request matching hostnames intersection should reach backend", func(t *testing.T) { tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "other.example.com", http.ExpectedResponse{ @@ -121,11 +111,6 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) - serverCertPem, _, err := GetTLSSecret(suite.Client, certNN) - if err != nil { - t.Fatalf("unexpected error finding TLS secret: %v", err) - } - t.Run("Simple TLS request matching hostnames intersection should reach backend", func(t *testing.T) { tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "other.example.com", http.ExpectedResponse{ From 405f2e35454268208cace5cf5dff47ac650ac21a Mon Sep 17 00:00:00 2001 From: Rostislav Bobrovsky Date: Thu, 29 Jan 2026 12:09:50 +0000 Subject: [PATCH 5/6] TLSRoute: Add more conformance tests to cover more intersection cases --- conformance/base/manifests.yaml | 62 +++++++++++++ .../tests/tlsroute-hostname-intersection.go | 90 ++++++++++++++----- .../tests/tlsroute-hostname-intersection.yaml | 80 +++++++++++++---- 3 files changed, 192 insertions(+), 40 deletions(-) diff --git a/conformance/base/manifests.yaml b/conformance/base/manifests.yaml index 63efb5375a..95fbcf4cce 100644 --- a/conformance/base/manifests.yaml +++ b/conformance/base/manifests.yaml @@ -308,6 +308,68 @@ spec: path: key --- apiVersion: v1 +kind: Service +metadata: + name: tls-backend-2 + namespace: gateway-conformance-infra +spec: + selector: + app: tls-backend-2 + ports: + - protocol: TCP + port: 443 + targetPort: 8443 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tls-backend-2 + namespace: gateway-conformance-infra + labels: + app: tls-backend-2 +spec: + replicas: 1 + selector: + matchLabels: + app: tls-backend-2 + template: + metadata: + labels: + app: tls-backend-2 + spec: + containers: + - name: tls-backend-2 + image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd + volumeMounts: + - name: secret-volume + mountPath: /etc/secret-volume + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: TLS_SERVER_CERT + value: /etc/secret-volume/crt + - name: TLS_SERVER_PRIVKEY + value: /etc/secret-volume/key + resources: + requests: + cpu: 10m + volumes: + - name: secret-volume + secret: + secretName: tls-checks-certificate + items: + - key: tls.crt + path: crt + - key: tls.key + path: key +--- +apiVersion: v1 kind: Namespace metadata: name: gateway-conformance-app-backend diff --git a/conformance/tests/tlsroute-hostname-intersection.go b/conformance/tests/tlsroute-hostname-intersection.go index 8aff39b824..6475e5f95a 100644 --- a/conformance/tests/tlsroute-hostname-intersection.go +++ b/conformance/tests/tlsroute-hostname-intersection.go @@ -53,14 +53,14 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ t.Fatalf("unexpected error finding TLS secret: %v", err) } - t.Run("TLSRoutes with wildcard hostname intersects with exact listener hostname", func(t *testing.T) { - routeNN := types.NamespacedName{Namespace: ns, Name: "tlsroute-more-specific-wildcard-hostname"} - gwNN := types.NamespacedName{Name: "gateway-tlsroute-exact-hostname-intersection", Namespace: ns} + t.Run("TLSRoutes intersect with exact listener hostname", func(t *testing.T) { + routeNN := types.NamespacedName{Namespace: ns, Name: "tlsroute-more-specific-wc-hostname-x-1"} + gwNN := types.NamespacedName{Name: "gw-tlsroute-exact-hostname-x-1", Namespace: ns} gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) - t.Run("Simple TLS request matching hostnames intersection should reach backend", func(t *testing.T) { + t.Run("TLS request matching exact hostnames intersection should reach backend", func(t *testing.T) { tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "abc.example.com", http.ExpectedResponse{ Request: http.Request{Host: "abc.example.com", Path: "/"}, @@ -70,14 +70,19 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ }) }) - t.Run("TLSRoutes with exact hostname intersects with wildcard listener hostname", func(t *testing.T) { - routeNN := types.NamespacedName{Namespace: ns, Name: "tlsroute-exact-hostname"} - gwNN := types.NamespacedName{Name: "gateway-tlsroute-more-specific-wildcard-hostname-intersection", Namespace: ns} - gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + t.Run("TLSRoutes intersect with more specific wildcard listener hostname", func(t *testing.T) { + routes := []types.NamespacedName{ + {Namespace: ns, Name: "tlsroute-exact-hostname-x-2"}, + {Namespace: ns, Name: "tlsroute-less-specific-wc-hostname-x-2"}, + } + gwNN := types.NamespacedName{Name: "gw-tlsroute-more-specific-wc-hostname-x-2", Namespace: ns} + gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routes...) - kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) + for _, routeNN := range routes { + kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) + } - t.Run("Simple TLS request matching hostnames intersection should reach backend", func(t *testing.T) { + t.Run("TLS request matching exact hostnames intersection should reach backend", func(t *testing.T) { tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "abc.example.com", http.ExpectedResponse{ Request: http.Request{Host: "abc.example.com", Path: "/"}, @@ -85,37 +90,74 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ Namespace: ns, }) }) + + t.Run("TLS request matching wildcard hostnames intersection should reach backend 2", func(t *testing.T) { + tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "other.example.com", + http.ExpectedResponse{ + Request: http.Request{Host: "other.example.com", Path: "/"}, + Backend: "tls-backend-2", + Namespace: ns, + }) + }) }) - t.Run("TLSRoutes with wildcard hostname intersects with wildcard listener hostname", func(t *testing.T) { - routeNN := types.NamespacedName{Namespace: ns, Name: "tlsroute-more-specific-wildcard-hostname-2"} - gwNN := types.NamespacedName{Name: "gateway-tlsroute-less-specific-wildcard-hostname-intersection", Namespace: ns} - gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + t.Run("TLSRoutes intersect with less specific wildcard listener hostname", func(t *testing.T) { + routes := []types.NamespacedName{ + {Namespace: ns, Name: "tlsroute-exact-hostname-x-3"}, + {Namespace: ns, Name: "tlsroute-more-specific-wc-hostname-x-3"}, + } + gwNN := types.NamespacedName{Name: "gw-tlsroute-less-specific-wc-hostname-x-3", Namespace: ns} + gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routes...) - kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) + for _, routeNN := range routes { + kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) + } - t.Run("Simple TLS request matching hostnames intersection should reach backend", func(t *testing.T) { + t.Run("TLS request matching exact hostnames intersection should reach backend", func(t *testing.T) { + tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "abc.example.com", + http.ExpectedResponse{ + Request: http.Request{Host: "abc.example.com", Path: "/"}, + Backend: "tls-backend", + Namespace: ns, + }) + }) + + t.Run("TLS request matching wildcard hostnames intersection should reach backend 2", func(t *testing.T) { tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "other.example.com", http.ExpectedResponse{ Request: http.Request{Host: "other.example.com", Path: "/"}, - Backend: "tls-backend", + Backend: "tls-backend-2", Namespace: ns, }) }) }) - t.Run("TLSRoute with wildcard hostname intersects with empty listener hostname", func(t *testing.T) { - routeNN := types.NamespacedName{Namespace: ns, Name: "tlsroute-less-specific-wildcard-hostname"} - gwNN := types.NamespacedName{Name: "gateway-tlsroute-empty-hostname-intersection", Namespace: ns} - gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + t.Run("TLSRoutes intersect with empty listener hostname", func(t *testing.T) { + routes := []types.NamespacedName{ + {Namespace: ns, Name: "tlsroute-exact-hostname-x-4"}, + {Namespace: ns, Name: "tlsroute-less-specific-wc-hostname-x-4"}, + } + gwNN := types.NamespacedName{Name: "gw-tlsroute-empty-hostname-x-4", Namespace: ns} + gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routes...) - kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) + for _, routeNN := range routes { + kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) + } - t.Run("Simple TLS request matching hostnames intersection should reach backend", func(t *testing.T) { + t.Run("TLS request matching exact hostnames intersection should reach backend", func(t *testing.T) { + tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "abc.example.com", + http.ExpectedResponse{ + Request: http.Request{Host: "abc.example.com", Path: "/"}, + Backend: "tls-backend", + Namespace: ns, + }) + }) + + t.Run("TLS request matching wildcard hostnames intersection should reach backend 2", func(t *testing.T) { tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "other.example.com", http.ExpectedResponse{ Request: http.Request{Host: "other.example.com", Path: "/"}, - Backend: "tls-backend", + Backend: "tls-backend-2", Namespace: ns, }) }) diff --git a/conformance/tests/tlsroute-hostname-intersection.yaml b/conformance/tests/tlsroute-hostname-intersection.yaml index 7f443ccef6..73f7aa8a2f 100644 --- a/conformance/tests/tlsroute-hostname-intersection.yaml +++ b/conformance/tests/tlsroute-hostname-intersection.yaml @@ -1,7 +1,7 @@ apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: - name: gateway-tlsroute-exact-hostname-intersection + name: gw-tlsroute-exact-hostname-x-1 namespace: gateway-conformance-infra spec: gatewayClassName: "{GATEWAY_CLASS_NAME}" @@ -21,12 +21,12 @@ spec: apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: - name: gateway-tlsroute-more-specific-wildcard-hostname-intersection + name: gw-tlsroute-more-specific-wc-hostname-x-2 namespace: gateway-conformance-infra spec: gatewayClassName: "{GATEWAY_CLASS_NAME}" listeners: - - name: listener-more-specific-wildcard-hostname + - name: listener-more-specific-wc-hostname protocol: TLS port: 443 hostname: "*.example.com" @@ -41,12 +41,12 @@ spec: apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: - name: gateway-tlsroute-less-specific-wildcard-hostname-intersection + name: gw-tlsroute-less-specific-wc-hostname-x-3 namespace: gateway-conformance-infra spec: gatewayClassName: "{GATEWAY_CLASS_NAME}" listeners: - - name: listener-less-specific-wildcard-hostname + - name: listener-less-specific-wc-hostname protocol: TLS port: 443 hostname: "*.com" @@ -61,7 +61,7 @@ spec: apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: - name: gateway-tlsroute-empty-hostname-intersection + name: gw-tlsroute-empty-hostname-x-4 namespace: gateway-conformance-infra spec: gatewayClassName: "{GATEWAY_CLASS_NAME}" @@ -80,11 +80,27 @@ spec: apiVersion: gateway.networking.k8s.io/v1alpha3 kind: TLSRoute metadata: - name: tlsroute-exact-hostname + name: tlsroute-more-specific-wc-hostname-x-1 namespace: gateway-conformance-infra spec: parentRefs: - - name: gateway-tlsroute-more-specific-wildcard-hostname-intersection + - name: gw-tlsroute-exact-hostname-x-1 + namespace: gateway-conformance-infra + hostnames: + - "*.example.com" + rules: + - backendRefs: + - name: tls-backend + port: 443 +--- +apiVersion: gateway.networking.k8s.io/v1alpha3 +kind: TLSRoute +metadata: + name: tlsroute-exact-hostname-x-2 + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: gw-tlsroute-more-specific-wc-hostname-x-2 namespace: gateway-conformance-infra hostnames: - abc.example.com @@ -96,14 +112,30 @@ spec: apiVersion: gateway.networking.k8s.io/v1alpha3 kind: TLSRoute metadata: - name: tlsroute-more-specific-wildcard-hostname + name: tlsroute-less-specific-wc-hostname-x-2 namespace: gateway-conformance-infra spec: parentRefs: - - name: gateway-tlsroute-exact-hostname-intersection + - name: gw-tlsroute-more-specific-wc-hostname-x-2 namespace: gateway-conformance-infra hostnames: - - "*.example.com" + - "*.com" + rules: + - backendRefs: + - name: tls-backend-2 + port: 443 +--- +apiVersion: gateway.networking.k8s.io/v1alpha3 +kind: TLSRoute +metadata: + name: tlsroute-exact-hostname-x-3 + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: gw-tlsroute-less-specific-wc-hostname-x-3 + namespace: gateway-conformance-infra + hostnames: + - abc.example.com rules: - backendRefs: - name: tls-backend @@ -112,15 +144,31 @@ spec: apiVersion: gateway.networking.k8s.io/v1alpha3 kind: TLSRoute metadata: - name: tlsroute-more-specific-wildcard-hostname-2 + name: tlsroute-more-specific-wc-hostname-x-3 namespace: gateway-conformance-infra spec: parentRefs: - - name: gateway-tlsroute-less-specific-wildcard-hostname-intersection + - name: gw-tlsroute-less-specific-wc-hostname-x-3 namespace: gateway-conformance-infra hostnames: - "*.example.com" rules: + - backendRefs: + - name: tls-backend-2 + port: 443 +--- +apiVersion: gateway.networking.k8s.io/v1alpha3 +kind: TLSRoute +metadata: + name: tlsroute-exact-hostname-x-4 + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: gw-tlsroute-empty-hostname-x-4 + namespace: gateway-conformance-infra + hostnames: + - abc.example.com + rules: - backendRefs: - name: tls-backend port: 443 @@ -128,15 +176,15 @@ spec: apiVersion: gateway.networking.k8s.io/v1alpha3 kind: TLSRoute metadata: - name: tlsroute-less-specific-wildcard-hostname + name: tlsroute-less-specific-wc-hostname-x-4 namespace: gateway-conformance-infra spec: parentRefs: - - name: gateway-tlsroute-empty-hostname-intersection + - name: gw-tlsroute-empty-hostname-x-4 namespace: gateway-conformance-infra hostnames: - "*.com" rules: - backendRefs: - - name: tls-backend + - name: tls-backend-2 port: 443 From 48da635ab69ce2ab00a5bb1cde1a4c1212e4a7f5 Mon Sep 17 00:00:00 2001 From: Rostislav Bobrovsky Date: Thu, 29 Jan 2026 12:18:15 +0000 Subject: [PATCH 6/6] TLSRoute: Make TLS request tests run in parallel --- conformance/tests/tlsroute-hostname-intersection.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/conformance/tests/tlsroute-hostname-intersection.go b/conformance/tests/tlsroute-hostname-intersection.go index 6475e5f95a..069ee4c5d6 100644 --- a/conformance/tests/tlsroute-hostname-intersection.go +++ b/conformance/tests/tlsroute-hostname-intersection.go @@ -61,6 +61,7 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ kubernetes.TLSRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) t.Run("TLS request matching exact hostnames intersection should reach backend", func(t *testing.T) { + t.Parallel() tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "abc.example.com", http.ExpectedResponse{ Request: http.Request{Host: "abc.example.com", Path: "/"}, @@ -83,6 +84,7 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ } t.Run("TLS request matching exact hostnames intersection should reach backend", func(t *testing.T) { + t.Parallel() tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "abc.example.com", http.ExpectedResponse{ Request: http.Request{Host: "abc.example.com", Path: "/"}, @@ -92,6 +94,7 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ }) t.Run("TLS request matching wildcard hostnames intersection should reach backend 2", func(t *testing.T) { + t.Parallel() tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "other.example.com", http.ExpectedResponse{ Request: http.Request{Host: "other.example.com", Path: "/"}, @@ -114,6 +117,7 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ } t.Run("TLS request matching exact hostnames intersection should reach backend", func(t *testing.T) { + t.Parallel() tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "abc.example.com", http.ExpectedResponse{ Request: http.Request{Host: "abc.example.com", Path: "/"}, @@ -123,6 +127,7 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ }) t.Run("TLS request matching wildcard hostnames intersection should reach backend 2", func(t *testing.T) { + t.Parallel() tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "other.example.com", http.ExpectedResponse{ Request: http.Request{Host: "other.example.com", Path: "/"}, @@ -145,6 +150,7 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ } t.Run("TLS request matching exact hostnames intersection should reach backend", func(t *testing.T) { + t.Parallel() tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "abc.example.com", http.ExpectedResponse{ Request: http.Request{Host: "abc.example.com", Path: "/"}, @@ -154,6 +160,7 @@ var TLSRouteHostnameIntersection = suite.ConformanceTest{ }) t.Run("TLS request matching wildcard hostnames intersection should reach backend 2", func(t *testing.T) { + t.Parallel() tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, serverCertPem, nil, nil, "other.example.com", http.ExpectedResponse{ Request: http.Request{Host: "other.example.com", Path: "/"},