Skip to content

Issue 3138 - Conformance Tests for BackendTLSPolicy - normative #3212

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions conformance/base/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ spec:
spec:
containers:
- name: infra-backend-v1
# From https://github.com/kubernetes-sigs/ingress-controller-conformance/tree/master/images/echoserver
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd
# Originally from https://github.com/kubernetes-sigs/ingress-controller-conformance/tree/master/images/echoserver
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20241007-v1.2.0-6-g9f820af9
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: there is newer version of this image
gcr.io/k8s-staging-gateway-api/echo-basic:v20250605-v1.3.0-25-g77baa438

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. This version will need to be updated for all once the changes for echo-basic are accepted. Then, I will split this PR, merge the echo-basic changes, and it will give me a new version to use. Then I will change all the images to use the new version.

Copy link
Contributor Author

@candita candita Jun 19, 2025

Choose a reason for hiding this comment

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

I will not be updating echo-basic after all. We've not made any changes to echo-basic that we need to pick up, since March 2024.

env:
- name: POD_NAME
valueFrom:
Expand Down Expand Up @@ -185,7 +185,7 @@ spec:
spec:
containers:
- name: infra-backend-v2
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20241007-v1.2.0-6-g9f820af9
env:
- name: POD_NAME
valueFrom:
Expand Down Expand Up @@ -231,7 +231,7 @@ spec:
spec:
containers:
- name: infra-backend-v3
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20241007-v1.2.0-6-g9f820af9
env:
- name: POD_NAME
valueFrom:
Expand Down Expand Up @@ -277,7 +277,7 @@ spec:
spec:
containers:
- name: tls-backend
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20241007-v1.2.0-6-g9f820af9
volumeMounts:
- name: secret-volume
mountPath: /etc/secret-volume
Expand All @@ -300,7 +300,7 @@ spec:
volumes:
- name: secret-volume
secret:
secretName: tls-passthrough-checks-certificate
secretName: tls-checks-certificate
items:
- key: tls.crt
path: crt
Expand Down Expand Up @@ -346,7 +346,7 @@ spec:
spec:
containers:
- name: tls-backend
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20241007-v1.2.0-6-g9f820af9
volumeMounts:
- name: secret-volume
mountPath: /etc/secret-volume
Expand Down Expand Up @@ -408,7 +408,7 @@ spec:
spec:
containers:
- name: app-backend-v1
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20241007-v1.2.0-6-g9f820af9
env:
- name: POD_NAME
valueFrom:
Expand Down Expand Up @@ -454,7 +454,7 @@ spec:
spec:
containers:
- name: app-backend-v2
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20241007-v1.2.0-6-g9f820af9
env:
- name: POD_NAME
valueFrom:
Expand Down Expand Up @@ -507,7 +507,7 @@ spec:
spec:
containers:
- name: web-backend
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20241007-v1.2.0-6-g9f820af9
env:
- name: POD_NAME
valueFrom:
Expand Down Expand Up @@ -554,7 +554,7 @@ spec:
spec:
containers:
- name: grpc-infra-backend-v1
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20241007-v1.2.0-6-g9f820af9
env:
- name: POD_NAME
valueFrom:
Expand Down Expand Up @@ -603,7 +603,7 @@ spec:
spec:
containers:
- name: grpc-infra-backend-v2
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20241007-v1.2.0-6-g9f820af9
env:
- name: POD_NAME
valueFrom:
Expand Down Expand Up @@ -652,7 +652,7 @@ spec:
spec:
containers:
- name: grpc-infra-backend-v3
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20241007-v1.2.0-6-g9f820af9
env:
- name: POD_NAME
valueFrom:
Expand Down
5 changes: 0 additions & 5 deletions conformance/conformance.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,11 @@ import (
"sigs.k8s.io/gateway-api/conformance/tests"
conformanceconfig "sigs.k8s.io/gateway-api/conformance/utils/config"
"sigs.k8s.io/gateway-api/conformance/utils/flags"

"sigs.k8s.io/gateway-api/conformance/utils/suite"

"github.com/stretchr/testify/require"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"

clientset "k8s.io/client-go/kubernetes"

"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
"sigs.k8s.io/yaml"
Expand Down Expand Up @@ -66,7 +63,6 @@ func DefaultOptions(t *testing.T) suite.ConformanceOptions {

supportedFeatures := suite.ParseSupportedFeatures(*flags.SupportedFeatures)
exemptFeatures := suite.ParseSupportedFeatures(*flags.ExemptFeatures)

skipTests := suite.ParseSkipTests(*flags.SkipTests)
namespaceLabels := suite.ParseKeyValuePairs(*flags.NamespaceLabels)
namespaceAnnotations := suite.ParseKeyValuePairs(*flags.NamespaceAnnotations)
Expand Down Expand Up @@ -148,7 +144,6 @@ func logOptions(t *testing.T, opts suite.ConformanceOptions) {
t.Logf(" Enable All Features: %t", opts.EnableAllSupportedFeatures)
t.Logf(" Supported Features: %v", opts.SupportedFeatures.UnsortedList())
t.Logf(" ExemptFeatures: %v", opts.ExemptFeatures.UnsortedList())
t.Logf(" ConformanceProfiles: %v", opts.ConformanceProfiles.UnsortedList())
}

func writeReport(logf func(string, ...any), report confv1.ConformanceReport, output string) error {
Expand Down
90 changes: 90 additions & 0 deletions conformance/tests/backendtlspolicy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
Copyright 2024 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"

h "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, BackendTLSPolicy)
}

var BackendTLSPolicy = suite.ConformanceTest{
ShortName: "BackendTLSPolicy",
Description: "A single service that is targeted by a BackendTLSPolicy must successfully complete TLS termination",
Features: []features.FeatureName{
features.SupportGateway,
features.SupportHTTPRoute,
features.SupportBackendTLSPolicy,
},
Manifests: []string{"tests/backendtlspolicy.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
ns := "gateway-conformance-infra"
routeNN := types.NamespacedName{Name: "gateway-conformance-infra-test", Namespace: ns}
gwNN := types.NamespacedName{Name: "gateway-backendtlspolicy", Namespace: ns}

kubernetes.NamespacesMustBeReady(t, suite.Client, suite.TimeoutConfig, []string{ns})
gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAcceptedMultipleListeners(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

Copy link
Member

Choose a reason for hiding this comment

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

I think we’re also missing an assertion here to verify that the BackendTLSPolicy has been accepted, with the appropriate ancestor references and statusconditions set.

serverStr := "abc.example.com"

// Verify that the response to a backend-tls-only call to /backendTLS will return the matching SNI.
t.Run("Simple HTTP request targeting BackendTLSPolicy should reach infra-backend", func(t *testing.T) {
h.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr,
h.ExpectedResponse{
Namespace: ns,
Request: h.Request{
Host: serverStr,
Path: "/backendTLS",
SNI: serverStr,
},
Response: h.Response{StatusCode: 200},
})
})

// For the re-encrypt case, we need to use the cert for the frontend tls listener.
certNN := types.NamespacedName{Name: "tls-checks-certificate", Namespace: ns}
cPem, keyPem, err := GetTLSSecret(suite.Client, certNN)
if err != nil {
t.Fatalf("unexpected error finding TLS secret: %v", err)
}
// Verify that the response to a re-encrypted call to /backendTLS will return the matching SNI.
t.Run("Re-encrypt HTTPS request targeting BackendTLSPolicy should reach infra-backend", func(t *testing.T) {
tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, cPem, keyPem, serverStr,
h.ExpectedResponse{
Namespace: ns,
Request: h.Request{
Host: serverStr,
Path: "/backendTLS",
SNI: serverStr,
},
Response: h.Response{StatusCode: 200},
})
})

},
}
153 changes: 153 additions & 0 deletions conformance/tests/backendtlspolicy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway-backendtlspolicy
namespace: gateway-conformance-infra
Copy link
Member

Choose a reason for hiding this comment

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

Is there a specific reason for using a dedicated Gateway for these tests?

I’m generally in favor of reusing base resources, whenever possible, as this can help reduce the execution time of the conformance test suite and, in turn, improve the efficiency of the implementation's CICD pipelines.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a great idea but until we have the listener merging standardized, I'm not sure I want to take the risk of adding a listener to an existing gateway and hoping for the best for each implementation to pass and for old conformance tests to continue working.

Copy link
Member

@snorwin snorwin Jun 6, 2025

Choose a reason for hiding this comment

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

Why do we need a dedicated listener for the test? Would it not be sufficient e.g., to reuse the existing HTTPS listener also used in HTTPRouteHTTPSListener? As long as each test uses a unique PathPrefix, there should be no interference between tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've updated it so that the Gateway has an HTTP listener for the backend-tls-only case, and an HTTPS listener that terminates TLS for the re-encrypt use case.
I feel like it serves as a better example to future code maintainers and documenters to exercise two tests in one Gateway.

Copy link
Member

Choose a reason for hiding this comment

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

I still have a strong opinion that we should reuse the base Gateway wherever possible, as this has a significant impact on the conformance test suite runtime. Therefore, I suggest using the same-namespace-with-https-listener and same-namespace Gateways in this test.

spec:
gatewayClassName: "{GATEWAY_CLASS_NAME}"
listeners:
- name: http
port: 80
protocol: HTTP
hostname: "abc.example.com"
allowedRoutes:
namespaces:
from: Same
kinds:
- kind: HTTPRoute
- name: https
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- group: ""
kind: Secret
name: tls-checks-certificate
hostname: "abc.example.com"
allowedRoutes:
namespaces:
from: Same
kinds:
- kind: HTTPRoute
---
apiVersion: gateway.networking.k8s.io/v1alpha3
kind: BackendTLSPolicy
metadata:
name: normative-test-backendtlspolicy
namespace: gateway-conformance-infra
spec:
targetRefs:
- group: ""
kind: Service
name: "backendtlspolicy-test"
sectionName: "btls"
validation:
caCertificateRefs:
- group: ""
kind: ConfigMap
# This secret is generated dynamically by the test suite.
name: "backend-tls-checks-certificate"
hostname: "abc.example.com"
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: gateway-conformance-infra-test
namespace: gateway-conformance-infra
spec:
parentRefs:
- name: gateway-backendtlspolicy
namespace: gateway-conformance-infra
hostnames:
- abc.example.com
rules:
- backendRefs:
- group: ""
kind: Service
name: backendtlspolicy-test
port: 443
matches:
- path:
type: Exact
value: /backendTLS
---
apiVersion: v1
kind: Service
metadata:
name: backendtlspolicy-test
namespace: gateway-conformance-infra
spec:
selector:
app: backendtlspolicy-test
ports:
- name: "btls"
protocol: TCP
port: 443
targetPort: 8443
---
# Deployment must not be applied until after the secret is generated.
Copy link
Member

Choose a reason for hiding this comment

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

Same here: I think we should reuse the tls-backend. We can still create a dedicated copy of its Service for this test to avoid interference with other test cases.

apiVersion: apps/v1
kind: Deployment
metadata:
name: backendtlspolicy-test
namespace: gateway-conformance-infra
labels:
app: backendtlspolicy-test
spec:
replicas: 1
selector:
matchLabels:
app: backendtlspolicy-test
template:
metadata:
labels:
app: backendtlspolicy-test
spec:
containers:
- name: backendtlspolicy-test
image: gcr.io/k8s-staging-gateway-api/echo-basic:v20240412-v1.0.0-394-g40c666fd
volumeMounts:
- name: ca-volume
mountPath: /etc/ca-volume
- name: secret-volume
mountPath: /etc/secret-volume
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: CA_CERT
value: /etc/ca-volume/crt
- name: CA_CERT_KEY
value: /etc/ca-volume/key
- name: TLS_SERVER_CERT
value: /etc/secret-volume/crt
- name: TLS_SERVER_PRIVKEY
value: /etc/secret-volume/key
resources:
requests:
cpu: 10m
volumes:
- name: ca-volume
configMap:
# This configMap is generated dynamically by the test suite.
name: backend-tls-checks-certificate
items:
- key: ca.crt
path: crt
- key: key.crt
path: key
- name: secret-volume
secret:
# This secret is generated dynamically by the test suite.
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't really care but FWIW Istio just hardcodes one and embeds it into the repo/dockerfile. A bit simpler

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wanted to make it easier for later tests to be able to use different SNI and certs.

secretName: tls-checks-certificate
items:
- key: tls.crt
path: crt
- key: tls.key
path: key
2 changes: 1 addition & 1 deletion conformance/tests/grpcroute-exact-method-matching.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ var GRPCExactMethodMatching = suite.ConformanceTest{
ns := "gateway-conformance-infra"
routeNN := types.NamespacedName{Name: "exact-matching", Namespace: ns}
gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
gwAddr := kubernetes.GatewayAndRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), &v1.GRPCRoute{}, routeNN)
gwAddr := kubernetes.GatewayAndRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), &v1.GRPCRoute{}, true, routeNN)

testCases := []grpc.ExpectedResponse{
{
Expand Down
Loading