Skip to content

Commit 59a1a4e

Browse files
authored
BackendTLSPolicy conformance tests for conflict resolution (#4043)
* BackendTLSPolicy conformance tests for conflict resolution Signed-off-by: Norwin Schnyder <[email protected]> * fix linting errors Signed-off-by: Norwin Schnyder <[email protected]> * apply PR feedback Signed-off-by: Norwin Schnyder <[email protected]> --------- Signed-off-by: Norwin Schnyder <[email protected]>
1 parent 4a150bf commit 59a1a4e

File tree

3 files changed

+370
-1
lines changed

3 files changed

+370
-1
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package tests
18+
19+
import (
20+
"testing"
21+
22+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
"k8s.io/apimachinery/pkg/types"
24+
25+
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
26+
"sigs.k8s.io/gateway-api/apis/v1alpha2"
27+
h "sigs.k8s.io/gateway-api/conformance/utils/http"
28+
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
29+
"sigs.k8s.io/gateway-api/conformance/utils/suite"
30+
"sigs.k8s.io/gateway-api/pkg/features"
31+
)
32+
33+
func init() {
34+
ConformanceTests = append(ConformanceTests, BackendTLSPolicyConflictResolution)
35+
}
36+
37+
var BackendTLSPolicyConflictResolution = suite.ConformanceTest{
38+
ShortName: "BackendTLSPolicyConflictResolution",
39+
Description: "Verifies that when multiple BackendTLSPolicies target the same Service, only one policy is accepted while conflicting policies are rejected, and traffic continues to route successfully.",
40+
Features: []features.FeatureName{
41+
features.SupportGateway,
42+
features.SupportHTTPRoute,
43+
features.SupportBackendTLSPolicy,
44+
},
45+
Manifests: []string{"tests/backendtlspolicy-conflict-resolution.yaml"},
46+
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
47+
ns := "gateway-conformance-infra"
48+
routeNN := types.NamespacedName{Name: "backendtlspolicy-conflict-resolution", Namespace: ns}
49+
gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
50+
51+
kubernetes.NamespacesMustBeReady(t, suite.Client, suite.TimeoutConfig, []string{ns})
52+
gwAddr := kubernetes.GatewayAndRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), &gatewayv1.HTTPRoute{}, false, routeNN)
53+
kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)
54+
55+
acceptedCond := metav1.Condition{
56+
Type: string(v1alpha2.PolicyConditionAccepted),
57+
Status: metav1.ConditionTrue,
58+
Reason: string(v1alpha2.PolicyReasonAccepted),
59+
}
60+
conflictedCond := metav1.Condition{
61+
Type: string(v1alpha2.PolicyConditionAccepted),
62+
Status: metav1.ConditionFalse,
63+
Reason: string(v1alpha2.PolicyReasonConflicted),
64+
}
65+
66+
t.Run("Conflicting BackendTLSPolicies targeting the same Service without a section name", func(t *testing.T) {
67+
t.Run("First BackendTLSPolicy should be accepted", func(t *testing.T) {
68+
policyNN := types.NamespacedName{Name: "conflicted-without-section-name-1", Namespace: ns}
69+
kubernetes.BackendTLSPolicyMustHaveCondition(t, suite.Client, suite.TimeoutConfig, policyNN, gwNN, acceptedCond)
70+
})
71+
72+
t.Run("Second BackendTLSPolicy should have a false Accepted condition with reason Conflicted ", func(t *testing.T) {
73+
// This is not specific to BackendTLSPolicy, it follows the conflict-resolution rules, as defined in GEP-713.
74+
conflictedPolicyNN := types.NamespacedName{Name: "conflicted-without-section-name-2", Namespace: ns}
75+
kubernetes.BackendTLSPolicyMustHaveCondition(t, suite.Client, suite.TimeoutConfig, conflictedPolicyNN, gwNN, conflictedCond)
76+
})
77+
78+
t.Run("HTTP request sent to Service using the accepted BackendTLSPolicy should succeed", func(t *testing.T) {
79+
h.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr,
80+
h.ExpectedResponse{
81+
Namespace: ns,
82+
Request: h.Request{
83+
Host: "abc.example.com",
84+
Path: "/backendtlspolicy-conflicted-without-section-name",
85+
SNI: "other.example.com",
86+
},
87+
Response: h.Response{StatusCode: 200},
88+
})
89+
})
90+
})
91+
92+
t.Run("Conflicting BackendTLSPolicies targeting the same Service with the same section name", func(t *testing.T) {
93+
t.Run("First BackendTLSPolicy should be accepted", func(t *testing.T) {
94+
policyNN := types.NamespacedName{Name: "conflicted-with-section-name-1", Namespace: ns}
95+
kubernetes.BackendTLSPolicyMustHaveCondition(t, suite.Client, suite.TimeoutConfig, policyNN, gwNN, acceptedCond)
96+
})
97+
98+
t.Run("Second BackendTLSPolicy should have a false Accepted condition with reason Conflicted ", func(t *testing.T) {
99+
// This is not specific to BackendTLSPolicy, it follows the conflict-resolution rules, as defined in GEP-713.
100+
conflictedPolicyNN := types.NamespacedName{Name: "conflicted-with-section-name-2", Namespace: ns}
101+
kubernetes.BackendTLSPolicyMustHaveCondition(t, suite.Client, suite.TimeoutConfig, conflictedPolicyNN, gwNN, conflictedCond)
102+
})
103+
104+
t.Run("HTTP request sent to Service using the accepted BackendTLSPolicy should succeed", func(t *testing.T) {
105+
h.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr,
106+
h.ExpectedResponse{
107+
Namespace: ns,
108+
Request: h.Request{
109+
Host: "abc.example.com",
110+
Path: "/backendtlspolicy-conflicted-with-section-name",
111+
SNI: "other.example.com",
112+
},
113+
Response: h.Response{StatusCode: 200},
114+
})
115+
})
116+
})
117+
118+
t.Run("BackendTLSPolicies targeting the same Service with and without a section name", func(t *testing.T) {
119+
t.Run("BackendTLSPolicy with section name should be accepted", func(t *testing.T) {
120+
policyNN := types.NamespacedName{Name: "not-conflicted-with-section-name", Namespace: ns}
121+
kubernetes.BackendTLSPolicyMustHaveCondition(t, suite.Client, suite.TimeoutConfig, policyNN, gwNN, acceptedCond)
122+
})
123+
124+
t.Run("BackendTLSPolicy without section name should be accepted", func(t *testing.T) {
125+
policyNN := types.NamespacedName{Name: "not-conflicted-without-section-name", Namespace: ns}
126+
kubernetes.BackendTLSPolicyMustHaveCondition(t, suite.Client, suite.TimeoutConfig, policyNN, gwNN, acceptedCond)
127+
})
128+
129+
t.Run("HTTP request sent to Service using the BackendTLSPolicy with section name should succeed", func(t *testing.T) {
130+
h.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr,
131+
h.ExpectedResponse{
132+
Namespace: ns,
133+
Request: h.Request{
134+
Host: "abc.example.com",
135+
Path: "/backendtlspolicy-not-conflicted-with-section-name",
136+
SNI: "other.example.com",
137+
},
138+
Response: h.Response{StatusCode: 200},
139+
})
140+
})
141+
t.Run("HTTP request sent to Service using the BackendTLSPolicy without section name should succeed", func(t *testing.T) {
142+
h.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr,
143+
h.ExpectedResponse{
144+
Namespace: ns,
145+
Request: h.Request{
146+
Host: "abc.example.com",
147+
Path: "/backendtlspolicy-not-conflicted-without-section-name",
148+
SNI: "abc.example.com",
149+
},
150+
Response: h.Response{StatusCode: 200},
151+
})
152+
})
153+
})
154+
},
155+
}
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
apiVersion: gateway.networking.k8s.io/v1
2+
kind: HTTPRoute
3+
metadata:
4+
name: backendtlspolicy-conflict-resolution
5+
namespace: gateway-conformance-infra
6+
spec:
7+
parentRefs:
8+
- name: same-namespace
9+
hostnames:
10+
- abc.example.com
11+
rules:
12+
- backendRefs:
13+
- group: ""
14+
kind: Service
15+
name: backendtlspolicy-conflicted-without-section-name-test
16+
port: 443
17+
matches:
18+
- path:
19+
type: Exact
20+
value: /backendtlspolicy-conflicted-without-section-name
21+
- backendRefs:
22+
- group: ""
23+
kind: Service
24+
name: backendtlspolicy-conflicted-with-section-name-test
25+
port: 443
26+
matches:
27+
- path:
28+
type: Exact
29+
value: /backendtlspolicy-conflicted-with-section-name
30+
- backendRefs:
31+
- group: ""
32+
kind: Service
33+
name: backendtlspolicy-not-conflicted-test
34+
port: 443
35+
matches:
36+
- path:
37+
type: Exact
38+
value: /backendtlspolicy-not-conflicted-with-section-name
39+
- backendRefs:
40+
- group: ""
41+
kind: Service
42+
name: backendtlspolicy-not-conflicted-test
43+
port: 8443
44+
matches:
45+
- path:
46+
type: Exact
47+
value: /backendtlspolicy-not-conflicted-without-section-name
48+
---
49+
apiVersion: v1
50+
kind: Service
51+
metadata:
52+
name: backendtlspolicy-conflicted-without-section-name-test
53+
namespace: gateway-conformance-infra
54+
spec:
55+
selector:
56+
app: tls-backend
57+
ports:
58+
- name: "https"
59+
protocol: TCP
60+
port: 443
61+
targetPort: 8443
62+
---
63+
apiVersion: v1
64+
kind: Service
65+
metadata:
66+
name: backendtlspolicy-conflicted-with-section-name-test
67+
namespace: gateway-conformance-infra
68+
spec:
69+
selector:
70+
app: tls-backend
71+
ports:
72+
- name: "https-1"
73+
protocol: TCP
74+
port: 443
75+
targetPort: 8443
76+
- name: "https-2"
77+
protocol: TCP
78+
port: 8443
79+
targetPort: 8443
80+
---
81+
apiVersion: v1
82+
kind: Service
83+
metadata:
84+
name: backendtlspolicy-not-conflicted-test
85+
namespace: gateway-conformance-infra
86+
spec:
87+
selector:
88+
app: tls-backend
89+
ports:
90+
- name: "https-1"
91+
protocol: TCP
92+
port: 443
93+
targetPort: 8443
94+
- name: "https-2"
95+
protocol: TCP
96+
port: 8443
97+
targetPort: 8443
98+
---
99+
apiVersion: gateway.networking.k8s.io/v1alpha3
100+
kind: BackendTLSPolicy
101+
metadata:
102+
name: conflicted-without-section-name-1
103+
namespace: gateway-conformance-infra
104+
spec:
105+
targetRefs:
106+
- group: ""
107+
kind: Service
108+
name: "backendtlspolicy-conflicted-without-section-name-test"
109+
validation:
110+
caCertificateRefs:
111+
- group: ""
112+
kind: ConfigMap
113+
# This ConfigMap is generated dynamically by the test suite.
114+
# It contains the CA certificate used to sign the tls-backend serving certificate.
115+
name: "tls-checks-ca-certificate"
116+
hostname: "other.example.com"
117+
---
118+
apiVersion: gateway.networking.k8s.io/v1alpha3
119+
kind: BackendTLSPolicy
120+
metadata:
121+
name: conflicted-without-section-name-2
122+
namespace: gateway-conformance-infra
123+
spec:
124+
targetRefs:
125+
- group: ""
126+
kind: Service
127+
name: "backendtlspolicy-conflicted-without-section-name-test"
128+
validation:
129+
caCertificateRefs:
130+
- group: ""
131+
kind: ConfigMap
132+
# This ConfigMap is generated dynamically by the test suite.
133+
# It contains the CA certificate used to sign the tls-backend serving certificate.
134+
name: "tls-checks-ca-certificate"
135+
hostname: "abc.example.com"
136+
---
137+
apiVersion: gateway.networking.k8s.io/v1alpha3
138+
kind: BackendTLSPolicy
139+
metadata:
140+
name: conflicted-with-section-name-1
141+
namespace: gateway-conformance-infra
142+
spec:
143+
targetRefs:
144+
- group: ""
145+
kind: Service
146+
name: "backendtlspolicy-conflicted-with-section-name-test"
147+
sectionName: "https-1"
148+
validation:
149+
caCertificateRefs:
150+
- group: ""
151+
kind: ConfigMap
152+
# This ConfigMap is generated dynamically by the test suite.
153+
# It contains the CA certificate used to sign the tls-backend serving certificate.
154+
name: "tls-checks-ca-certificate"
155+
hostname: "other.example.com"
156+
---
157+
apiVersion: gateway.networking.k8s.io/v1alpha3
158+
kind: BackendTLSPolicy
159+
metadata:
160+
name: conflicted-with-section-name-2
161+
namespace: gateway-conformance-infra
162+
spec:
163+
targetRefs:
164+
- group: ""
165+
kind: Service
166+
name: "backendtlspolicy-conflicted-with-section-name-test"
167+
sectionName: "https-1"
168+
validation:
169+
caCertificateRefs:
170+
- group: ""
171+
kind: ConfigMap
172+
# This ConfigMap is generated dynamically by the test suite.
173+
# It contains the CA certificate used to sign the tls-backend serving certificate.
174+
name: "tls-checks-ca-certificate"
175+
hostname: "abc.example.com"
176+
---
177+
apiVersion: gateway.networking.k8s.io/v1alpha3
178+
kind: BackendTLSPolicy
179+
metadata:
180+
name: not-conflicted-with-section-name
181+
namespace: gateway-conformance-infra
182+
spec:
183+
targetRefs:
184+
- group: ""
185+
kind: Service
186+
name: "backendtlspolicy-not-conflicted-test"
187+
sectionName: "https-1"
188+
validation:
189+
caCertificateRefs:
190+
- group: ""
191+
kind: ConfigMap
192+
# This ConfigMap is generated dynamically by the test suite.
193+
# It contains the CA certificate used to sign the tls-backend serving certificate.
194+
name: "tls-checks-ca-certificate"
195+
hostname: "other.example.com"
196+
---
197+
apiVersion: gateway.networking.k8s.io/v1alpha3
198+
kind: BackendTLSPolicy
199+
metadata:
200+
name: not-conflicted-without-section-name
201+
namespace: gateway-conformance-infra
202+
spec:
203+
targetRefs:
204+
- group: ""
205+
kind: Service
206+
name: "backendtlspolicy-not-conflicted-test"
207+
validation:
208+
caCertificateRefs:
209+
- group: ""
210+
kind: ConfigMap
211+
# This ConfigMap is generated dynamically by the test suite.
212+
# It contains the CA certificate used to sign the tls-backend serving certificate.
213+
name: "tls-checks-ca-certificate"
214+
hostname: "abc.example.com"

conformance/utils/suite/suite.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ func (suite *ConformanceTestSuite) Setup(t *testing.T, tests []ConformanceTest)
390390
suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{secret}, suite.Cleanup)
391391
caConfigMap, ca, caPrivKey := kubernetes.MustCreateCACertConfigMap(t, "gateway-conformance-infra", "tls-checks-ca-certificate")
392392
suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{caConfigMap}, suite.Cleanup)
393-
secret = kubernetes.MustCreateCASignedCertSecret(t, "gateway-conformance-infra", "tls-checks-certificate", []string{"abc.example.com", "spiffe://abc.example.com/test-identity"}, ca, caPrivKey)
393+
secret = kubernetes.MustCreateCASignedCertSecret(t, "gateway-conformance-infra", "tls-checks-certificate", []string{"abc.example.com", "spiffe://abc.example.com/test-identity", "other.example.com"}, ca, caPrivKey)
394394
suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{secret}, suite.Cleanup)
395395

396396
// The following CA ceritficate is used for BackendTLSPolicy testing to intentionally force TLS validation to fail.

0 commit comments

Comments
 (0)