Skip to content

Commit e3c6ccc

Browse files
committed
Add TLSRoute + ReferenceGrant check
This checks ReferenceGrant rules for TLSRoute against accepting an invalid ReferenceGrant for the backend service. Signed-off-by: Maartje Eyskens <[email protected]>
1 parent a13b497 commit e3c6ccc

File tree

8 files changed

+308
-4
lines changed

8 files changed

+308
-4
lines changed

conformance/base/manifests.yaml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,68 @@ metadata:
307307
---
308308
apiVersion: v1
309309
kind: Service
310+
metadata:
311+
name: tls-backend
312+
namespace: gateway-conformance-app-backend
313+
spec:
314+
selector:
315+
app: tls-backend
316+
ports:
317+
- protocol: TCP
318+
port: 443
319+
targetPort: 8443
320+
---
321+
apiVersion: apps/v1
322+
kind: Deployment
323+
metadata:
324+
name: tls-backend
325+
namespace: gateway-conformance-app-backend
326+
labels:
327+
app: tls-backend
328+
spec:
329+
replicas: 1
330+
selector:
331+
matchLabels:
332+
app: tls-backend
333+
template:
334+
metadata:
335+
labels:
336+
app: tls-backend
337+
spec:
338+
containers:
339+
- name: tls-backend
340+
image: gcr.io/k8s-staging-ingressconformance/echoserver:v20221109-7ee2f3e
341+
volumeMounts:
342+
- name: secret-volume
343+
mountPath: /etc/secret-volume
344+
env:
345+
- name: POD_NAME
346+
valueFrom:
347+
fieldRef:
348+
fieldPath: metadata.name
349+
- name: NAMESPACE
350+
valueFrom:
351+
fieldRef:
352+
fieldPath: metadata.namespace
353+
- name: TLS_SERVER_CERT
354+
value: /etc/secret-volume/crt
355+
- name: TLS_SERVER_PRIVKEY
356+
value: /etc/secret-volume/key
357+
resources:
358+
requests:
359+
cpu: 10m
360+
volumes:
361+
- name: secret-volume
362+
secret:
363+
secretName: tls-passthrough-checks-certificate
364+
items:
365+
- key: tls.crt
366+
path: crt
367+
- key: tls.key
368+
path: key
369+
---
370+
apiVersion: v1
371+
kind: Service
310372
metadata:
311373
name: app-backend-v1
312374
namespace: gateway-conformance-app-backend

conformance/tests/httproute-invalid-reference-grant.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2022 The Kubernetes Authors.
2+
Copyright 2023 The Kubernetes Authors.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -47,7 +47,6 @@ var HTTPRouteInvalidReferenceGrant = suite.ConformanceTest{
4747
gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
4848

4949
t.Run("HTTPRoute with BackendRef in another namespace and no ReferenceGrant covering the Service has a ResolvedRefs Condition with status False and Reason RefNotPermitted", func(t *testing.T) {
50-
5150
resolvedRefsCond := metav1.Condition{
5251
Type: string(v1beta1.RouteConditionResolvedRefs),
5352
Status: metav1.ConditionFalse,
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
Copyright 2023 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+
"sigs.k8s.io/gateway-api/apis/v1beta1"
26+
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
27+
"sigs.k8s.io/gateway-api/conformance/utils/suite"
28+
)
29+
30+
func init() {
31+
ConformanceTests = append(ConformanceTests, TLSRouteInvalidReferenceGrant)
32+
}
33+
34+
var TLSRouteInvalidReferenceGrant = suite.ConformanceTest{
35+
ShortName: "TLSRouteInvalidReferenceGrant",
36+
Description: "A single TLSRoute in the gateway-conformance-infra namespace, with a backendRef in another namespace without valid ReferenceGrant, should have the ResolvedRefs condition set to False",
37+
Features: []suite.SupportedFeature{
38+
suite.SupportGateway,
39+
suite.SupportTLSRoute,
40+
suite.SupportReferenceGrant,
41+
},
42+
Manifests: []string{"tests/tlsroute-invalid-reference-grant.yaml"},
43+
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
44+
routeNN := types.NamespacedName{Name: "gateway-conformance-infra-test", Namespace: "gateway-conformance-infra"}
45+
gwNN := types.NamespacedName{Name: "gateway-tlsroute-referencegrant", Namespace: "gateway-conformance-infra"}
46+
47+
kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
48+
49+
t.Run("TLSRoute with BackendRef in another namespace and no ReferenceGrant covering the Service has a ResolvedRefs Condition with status False and Reason RefNotPermitted", func(t *testing.T) {
50+
resolvedRefsCond := metav1.Condition{
51+
Type: string(v1beta1.RouteConditionResolvedRefs),
52+
Status: metav1.ConditionFalse,
53+
Reason: string(v1beta1.RouteReasonRefNotPermitted),
54+
}
55+
56+
kubernetes.TLSRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN, resolvedRefsCond)
57+
})
58+
},
59+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
---
2+
apiVersion: gateway.networking.k8s.io/v1beta1
3+
kind: ReferenceGrant
4+
metadata:
5+
name: reference-grant-wrong-namespace
6+
namespace: gateway-conformance-infra
7+
spec:
8+
from:
9+
- group: gateway.networking.k8s.io
10+
kind: TLSRoute
11+
namespace: gateway-conformance-infra
12+
to:
13+
- group: ""
14+
kind: Service
15+
name: tls-backend
16+
---
17+
apiVersion: gateway.networking.k8s.io/v1beta1
18+
kind: ReferenceGrant
19+
metadata:
20+
name: reference-grant-wrong-from-group
21+
namespace: gateway-conformance-app-backend
22+
spec:
23+
from:
24+
- group: not-the-group-youre-looking-for
25+
kind: TLSRoute
26+
namespace: gateway-conformance-infra
27+
to:
28+
- group: ""
29+
kind: Service
30+
name: tls-backend
31+
---
32+
apiVersion: gateway.networking.k8s.io/v1beta1
33+
kind: ReferenceGrant
34+
metadata:
35+
name: reference-grant-wrong-from-kind
36+
namespace: gateway-conformance-app-backend
37+
spec:
38+
from:
39+
- group: gateway.networking.k8s.io
40+
kind: Gateway
41+
namespace: gateway-conformance-infra
42+
to:
43+
- group: ""
44+
kind: Service
45+
name: tls-backend
46+
---
47+
apiVersion: gateway.networking.k8s.io/v1beta1
48+
kind: ReferenceGrant
49+
metadata:
50+
name: reference-grant-wrong-from-namespace
51+
namespace: gateway-conformance-app-backend
52+
spec:
53+
from:
54+
- group: gateway.networking.k8s.io
55+
kind: TLSRoute
56+
namespace: not-the-namespace-youre-looking-for
57+
to:
58+
- group: ""
59+
kind: Service
60+
name: tls-backend
61+
---
62+
apiVersion: gateway.networking.k8s.io/v1beta1
63+
kind: ReferenceGrant
64+
metadata:
65+
name: reference-grant-wrong-to-group
66+
namespace: gateway-conformance-app-backend
67+
spec:
68+
from:
69+
- group: gateway.networking.k8s.io
70+
kind: TLSRoute
71+
namespace: gateway-conformance-infra
72+
to:
73+
- group: not-the-group-youre-looking-for
74+
kind: Service
75+
name: tls-backend
76+
---
77+
apiVersion: gateway.networking.k8s.io/v1beta1
78+
kind: ReferenceGrant
79+
metadata:
80+
name: reference-grant-wrong-to-kind
81+
namespace: gateway-conformance-app-backend
82+
spec:
83+
from:
84+
- group: gateway.networking.k8s.io
85+
kind: TLSRoute
86+
namespace: gateway-conformance-infra
87+
to:
88+
- group: ""
89+
kind: Secret
90+
name: tls-backend
91+
---
92+
apiVersion: gateway.networking.k8s.io/v1beta1
93+
kind: ReferenceGrant
94+
metadata:
95+
name: reference-grant-wrong-to-name
96+
namespace: gateway-conformance-app-backend
97+
spec:
98+
from:
99+
- group: gateway.networking.k8s.io
100+
kind: TLSRoute
101+
namespace: gateway-conformance-infra
102+
to:
103+
- group: ""
104+
kind: Service
105+
name: not-the-service-youre-looking-for
106+
---
107+
apiVersion: gateway.networking.k8s.io/v1alpha2
108+
kind: TLSRoute
109+
metadata:
110+
name: gateway-conformance-infra-test
111+
namespace: gateway-conformance-infra
112+
spec:
113+
parentRefs:
114+
- name: gateway-tlsroute
115+
hostnames:
116+
- abc.example.com
117+
rules:
118+
- backendRefs:
119+
- name: tls-backend
120+
namespace: gateway-conformance-app-backend
121+
port: 443
122+
---
123+
apiVersion: gateway.networking.k8s.io/v1beta1
124+
kind: Gateway
125+
metadata:
126+
name: gateway-tlsroute
127+
namespace: gateway-conformance-infra
128+
spec:
129+
gatewayClassName: "{GATEWAY_CLASS_NAME}"
130+
listeners:
131+
- name: https
132+
port: 443
133+
protocol: TLS
134+
hostname: "*.example.com"
135+
allowedRoutes:
136+
namespaces:
137+
from: Same
138+
kinds:
139+
- kind: TLSRoute
140+
tls:
141+
mode: Passthrough

conformance/utils/config/timeout.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ type TimeoutConfig struct {
5151
// Max value for conformant implementation: None
5252
HTTPRouteMustHaveCondition time.Duration
5353

54+
// TLSRouteMustHaveCondition represents the maximum time for an TLSRoute to have the supplied Condition.
55+
// Max value for conformant implementation: None
56+
TLSRouteMustHaveCondition time.Duration
57+
5458
// RouteMustHaveParents represents the maximum time for an xRoute to have parents in status that match the expected parents.
5559
// Max value for conformant implementation: None
5660
RouteMustHaveParents time.Duration
@@ -95,6 +99,7 @@ func DefaultTimeoutConfig() TimeoutConfig {
9599
GWCMustBeAccepted: 180 * time.Second,
96100
HTTPRouteMustNotHaveParents: 60 * time.Second,
97101
HTTPRouteMustHaveCondition: 60 * time.Second,
102+
TLSRouteMustHaveCondition: 60 * time.Second,
98103
RouteMustHaveParents: 60 * time.Second,
99104
ManifestFetchTimeout: 10 * time.Second,
100105
MaxTimeToConsistency: 30 * time.Second,
@@ -149,4 +154,7 @@ func SetupTimeoutConfig(timeoutConfig *TimeoutConfig) {
149154
if timeoutConfig.LatestObservedGenerationSet == 0 {
150155
timeoutConfig.LatestObservedGenerationSet = defaultTimeoutConfig.LatestObservedGenerationSet
151156
}
157+
if timeoutConfig.TLSRouteMustHaveCondition == 0 {
158+
timeoutConfig.TLSRouteMustHaveCondition = defaultTimeoutConfig.TLSRouteMustHaveCondition
159+
}
152160
}

conformance/utils/kubernetes/helpers.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,6 @@ func HTTPRouteMustHaveNoAcceptedParents(t *testing.T, client client.Client, time
355355
var actual []v1beta1.RouteParentStatus
356356
emptyChecked := false
357357
waitErr := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, timeoutConfig.HTTPRouteMustNotHaveParents, true, func(ctx context.Context) (bool, error) {
358-
359358
route := &v1beta1.HTTPRoute{}
360359
err := client.Get(ctx, routeName, route)
361360
if err != nil {
@@ -528,7 +527,6 @@ func HTTPRouteMustHaveCondition(t *testing.T, client client.Client, timeoutConfi
528527
var conditionFound bool
529528
for _, parent := range parents {
530529
if err := ConditionsHaveLatestObservedGeneration(route, parent.Conditions); err != nil {
531-
532530
t.Logf("HTTPRoute(parentRef=%v) %v", parentRefToString(parent.ParentRef), err)
533531
return false, nil
534532
}
@@ -611,6 +609,39 @@ func GatewayAndTLSRoutesMustBeAccepted(t *testing.T, c client.Client, timeoutCon
611609
return gwAddr, hostnames
612610
}
613611

612+
// TLSRouteMustHaveCondition checks that the supplied TLSRoute has the supplied Condition,
613+
// halting after the specified timeout is exceeded.
614+
func TLSRouteMustHaveCondition(t *testing.T, client client.Client, timeoutConfig config.TimeoutConfig, routeNN types.NamespacedName, gwNN types.NamespacedName, condition metav1.Condition) {
615+
t.Helper()
616+
617+
waitErr := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, timeoutConfig.TLSRouteMustHaveCondition, true, func(ctx context.Context) (bool, error) {
618+
route := &v1alpha2.TLSRoute{}
619+
err := client.Get(ctx, routeNN, route)
620+
if err != nil {
621+
return false, fmt.Errorf("error fetching TLSRoute: %w", err)
622+
}
623+
624+
parents := route.Status.Parents
625+
var conditionFound bool
626+
for _, parent := range parents {
627+
if err := ConditionsHaveLatestObservedGeneration(route, parent.Conditions); err != nil {
628+
t.Logf("TLSRoute(parentRef=%v) %v", parentRefToString(parent.ParentRef), err)
629+
return false, nil
630+
}
631+
632+
if parent.ParentRef.Name == v1beta1.ObjectName(gwNN.Name) && (parent.ParentRef.Namespace == nil || string(*parent.ParentRef.Namespace) == gwNN.Namespace) {
633+
if findConditionInList(t, parent.Conditions, condition.Type, string(condition.Status), condition.Reason) {
634+
conditionFound = true
635+
}
636+
}
637+
}
638+
639+
return conditionFound, nil
640+
})
641+
642+
require.NoErrorf(t, waitErr, "error waiting for TLSRoute status to have a Condition matching expectations")
643+
}
644+
614645
// TODO(mikemorris): this and parentsMatch could possibly be rewritten as a generic function?
615646
func listenersMatch(t *testing.T, expected, actual []v1beta1.ListenerStatus) bool {
616647
t.Helper()

conformance/utils/suite/experimental_suite.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ func (suite *ExperimentalConformanceTestSuite) Setup(t *testing.T) {
189189
suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{secret}, suite.Cleanup)
190190
secret = kubernetes.MustCreateSelfSignedCertSecret(t, "gateway-conformance-infra", "tls-passthrough-checks-certificate", []string{"abc.example.com"})
191191
suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{secret}, suite.Cleanup)
192+
secret = kubernetes.MustCreateSelfSignedCertSecret(t, "gateway-conformance-app-backend", "tls-passthrough-checks-certificate", []string{"abc.example.com"})
193+
suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{secret}, suite.Cleanup)
192194

193195
t.Logf("Test Setup: Ensuring Gateways and Pods from base manifests are ready")
194196
namespaces := []string{

conformance/utils/suite/suite.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ func (suite *ConformanceTestSuite) Setup(t *testing.T) {
159159
suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{secret}, suite.Cleanup)
160160
secret = kubernetes.MustCreateSelfSignedCertSecret(t, "gateway-conformance-infra", "tls-passthrough-checks-certificate", []string{"abc.example.com"})
161161
suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{secret}, suite.Cleanup)
162+
secret = kubernetes.MustCreateSelfSignedCertSecret(t, "gateway-conformance-app-backend", "tls-passthrough-checks-certificate", []string{"abc.example.com"})
163+
suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{secret}, suite.Cleanup)
162164

163165
t.Logf("Test Setup: Ensuring Gateways and Pods from base manifests are ready")
164166
namespaces := []string{

0 commit comments

Comments
 (0)