mirror of
https://github.com/traefik/traefik.git
synced 2026-06-17 19:09:29 +03:00
Reject cross-provider references with backendRefs.namespace
This commit is contained in:
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
kind: GatewayClass
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: my-gateway-class
|
||||||
|
spec:
|
||||||
|
controllerName: traefik.io/gateway-controller
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Gateway
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: my-gateway
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
gatewayClassName: my-gateway-class
|
||||||
|
listeners: # Use GatewayClass defaults for listener definition.
|
||||||
|
- name: http
|
||||||
|
protocol: HTTP
|
||||||
|
port: 80
|
||||||
|
allowedRoutes:
|
||||||
|
namespaces:
|
||||||
|
from: Same
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: HTTPRoute
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: http-app-1
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
parentRefs:
|
||||||
|
- name: my-gateway
|
||||||
|
kind: Gateway
|
||||||
|
group: gateway.networking.k8s.io
|
||||||
|
hostnames:
|
||||||
|
- "foo.com"
|
||||||
|
rules:
|
||||||
|
- matches:
|
||||||
|
- path:
|
||||||
|
type: Exact
|
||||||
|
value: /bar
|
||||||
|
backendRefs:
|
||||||
|
- weight: 1
|
||||||
|
group: traefik.io
|
||||||
|
kind: TraefikService
|
||||||
|
name: service@file
|
||||||
|
namespace: bar
|
||||||
|
port: 80
|
||||||
|
|
||||||
|
- name: whoami
|
||||||
|
port: 80
|
||||||
|
weight: 1
|
||||||
|
group: ""
|
||||||
|
kind: Service
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
kind: GatewayClass
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: my-gateway-class
|
||||||
|
spec:
|
||||||
|
controllerName: traefik.io/gateway-controller
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Gateway
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: my-gateway
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
gatewayClassName: my-gateway-class
|
||||||
|
listeners: # Use GatewayClass defaults for listener definition.
|
||||||
|
- name: tcp
|
||||||
|
protocol: TCP
|
||||||
|
port: 9000
|
||||||
|
allowedRoutes:
|
||||||
|
kinds:
|
||||||
|
- kind: TCPRoute
|
||||||
|
group: gateway.networking.k8s.io
|
||||||
|
namespaces:
|
||||||
|
from: Same
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: TCPRoute
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1alpha2
|
||||||
|
metadata:
|
||||||
|
name: tcp-app-1
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
parentRefs:
|
||||||
|
- name: my-gateway
|
||||||
|
kind: Gateway
|
||||||
|
group: gateway.networking.k8s.io
|
||||||
|
rules:
|
||||||
|
- backendRefs:
|
||||||
|
- weight: 1
|
||||||
|
group: traefik.io
|
||||||
|
kind: TraefikService
|
||||||
|
name: service@file
|
||||||
|
namespace: bar
|
||||||
|
port: 9000
|
||||||
|
- name: whoamitcp
|
||||||
|
port: 9000
|
||||||
|
weight: 1
|
||||||
|
group: ""
|
||||||
|
kind: Service
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: supersecret
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
data:
|
||||||
|
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ==
|
||||||
|
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: GatewayClass
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: my-gateway-class
|
||||||
|
spec:
|
||||||
|
controllerName: traefik.io/gateway-controller
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Gateway
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: my-gateway
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
gatewayClassName: my-gateway-class
|
||||||
|
listeners: # Use GatewayClass defaults for listener definition.
|
||||||
|
- name: tls
|
||||||
|
protocol: TLS
|
||||||
|
port: 9000
|
||||||
|
tls:
|
||||||
|
certificateRefs:
|
||||||
|
- kind: Secret
|
||||||
|
name: supersecret
|
||||||
|
group: ""
|
||||||
|
allowedRoutes:
|
||||||
|
kinds:
|
||||||
|
- kind: TLSRoute
|
||||||
|
group: gateway.networking.k8s.io
|
||||||
|
namespaces:
|
||||||
|
from: Same
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: TLSRoute
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1alpha2
|
||||||
|
metadata:
|
||||||
|
name: tls-app-1
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
parentRefs:
|
||||||
|
- name: my-gateway
|
||||||
|
kind: Gateway
|
||||||
|
group: gateway.networking.k8s.io
|
||||||
|
rules:
|
||||||
|
- backendRefs:
|
||||||
|
- weight: 1
|
||||||
|
group: traefik.io
|
||||||
|
kind: TraefikService
|
||||||
|
name: service@file
|
||||||
|
namespace: bar
|
||||||
|
port: 9000
|
||||||
|
- name: whoamitcp
|
||||||
|
port: 9000
|
||||||
|
weight: 1
|
||||||
|
kind: Service
|
||||||
|
group: ""
|
||||||
@@ -237,6 +237,17 @@ func (p *Provider) loadService(ctx context.Context, listener gatewayListener, co
|
|||||||
namespace := route.Namespace
|
namespace := route.Namespace
|
||||||
if backendRef.Namespace != nil && *backendRef.Namespace != "" {
|
if backendRef.Namespace != nil && *backendRef.Namespace != "" {
|
||||||
namespace = string(*backendRef.Namespace)
|
namespace = string(*backendRef.Namespace)
|
||||||
|
|
||||||
|
if strings.Contains(string(backendRef.Name), "@") {
|
||||||
|
return provider.Normalize(namespace + "-" + string(backendRef.Name) + "-http"), &metav1.Condition{
|
||||||
|
Type: string(gatev1.RouteConditionResolvedRefs),
|
||||||
|
Status: metav1.ConditionFalse,
|
||||||
|
ObservedGeneration: route.Generation,
|
||||||
|
LastTransitionTime: metav1.Now(),
|
||||||
|
Reason: string(gatev1.RouteReasonRefNotPermitted),
|
||||||
|
Message: fmt.Sprintf("Cannot load HTTPBackendRef %s/%s/%s/%s: namespace is not allowed with a cross-provider reference", group, kind, namespace, backendRef.Name),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceName := provider.Normalize(namespace + "-" + string(backendRef.Name) + "-http")
|
serviceName := provider.Normalize(namespace + "-" + string(backendRef.Name) + "-http")
|
||||||
|
|||||||
@@ -8366,20 +8366,22 @@ func Test_isCrossProviderNamespaceAllowed(t *testing.T) {
|
|||||||
func TestCrossProviderNamespaces_HTTPRoute(t *testing.T) {
|
func TestCrossProviderNamespaces_HTTPRoute(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
fixture string
|
||||||
crossProviderNamespaces []string
|
crossProviderNamespaces []string
|
||||||
wantError bool
|
wantError bool
|
||||||
}{
|
}{
|
||||||
{desc: "nil: cross-provider TraefikService backendRefs accepted (backward compatible)", crossProviderNamespaces: nil, wantError: false},
|
{desc: "nil: cross-provider TraefikService backendRefs accepted (backward compatible)", fixture: "httproute/simple_cross_provider.yml", crossProviderNamespaces: nil, wantError: false},
|
||||||
{desc: "empty list: cross-provider TraefikService backendRefs are rejected, route dropped", crossProviderNamespaces: []string{}, wantError: true},
|
{desc: "empty list: cross-provider TraefikService backendRefs are rejected, route dropped", fixture: "httproute/simple_cross_provider.yml", crossProviderNamespaces: []string{}, wantError: true},
|
||||||
{desc: "namespace allowed: cross-provider TraefikService backendRefs accepted", crossProviderNamespaces: []string{"default"}, wantError: false},
|
{desc: "namespace allowed: cross-provider TraefikService backendRefs accepted", fixture: "httproute/simple_cross_provider.yml", crossProviderNamespaces: []string{"default"}, wantError: false},
|
||||||
{desc: "namespace not allowed: cross-provider TraefikService backendRefs rejected, route dropped", crossProviderNamespaces: []string{"other"}, wantError: true},
|
{desc: "namespace not allowed: cross-provider TraefikService backendRefs rejected, route dropped", fixture: "httproute/simple_cross_provider.yml", crossProviderNamespaces: []string{"other"}, wantError: true},
|
||||||
|
{desc: "namespace provided with cross-provider backendRef, route dropped", fixture: "httproute/invalid_cross_provider.yml", crossProviderNamespaces: []string{"other"}, wantError: true},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
k8sObjects, gwObjects := readResources(t, []string{"services.yml", "httproute/simple_cross_provider.yml"})
|
k8sObjects, gwObjects := readResources(t, []string{"services.yml", test.fixture})
|
||||||
|
|
||||||
kubeClient := kubefake.NewClientset(k8sObjects...)
|
kubeClient := kubefake.NewClientset(k8sObjects...)
|
||||||
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
||||||
@@ -8429,20 +8431,22 @@ func TestCrossProviderNamespaces_HTTPRoute(t *testing.T) {
|
|||||||
func TestCrossProviderNamespaces_TCPRoute(t *testing.T) {
|
func TestCrossProviderNamespaces_TCPRoute(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
fixture string
|
||||||
crossProviderNamespaces []string
|
crossProviderNamespaces []string
|
||||||
wantError bool
|
wantError bool
|
||||||
}{
|
}{
|
||||||
{desc: "nil: cross-provider TraefikService backendRefs accepted (backward compatible)", crossProviderNamespaces: nil, wantError: false},
|
{desc: "nil: cross-provider TraefikService backendRefs accepted (backward compatible)", fixture: "tcproute/simple_cross_provider.yml", crossProviderNamespaces: nil, wantError: false},
|
||||||
{desc: "empty list: cross-provider TraefikService backendRefs are rejected, route dropped", crossProviderNamespaces: []string{}, wantError: true},
|
{desc: "empty list: cross-provider TraefikService backendRefs are rejected, route dropped", fixture: "tcproute/simple_cross_provider.yml", crossProviderNamespaces: []string{}, wantError: true},
|
||||||
{desc: "namespace allowed: cross-provider TraefikService backendRefs accepted", crossProviderNamespaces: []string{"default"}, wantError: false},
|
{desc: "namespace allowed: cross-provider TraefikService backendRefs accepted", fixture: "tcproute/simple_cross_provider.yml", crossProviderNamespaces: []string{"default"}, wantError: false},
|
||||||
{desc: "namespace not allowed: cross-provider TraefikService backendRefs rejected, route dropped", crossProviderNamespaces: []string{"other"}, wantError: true},
|
{desc: "namespace not allowed: cross-provider TraefikService backendRefs rejected, route dropped", fixture: "tcproute/simple_cross_provider.yml", crossProviderNamespaces: []string{"other"}, wantError: true},
|
||||||
|
{desc: "namespace provided with cross-provider backendRef, route dropped", fixture: "tcproute/invalid_cross_provider.yml", crossProviderNamespaces: []string{"other"}, wantError: true},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
k8sObjects, gwObjects := readResources(t, []string{"services.yml", "tcproute/simple_cross_provider.yml"})
|
k8sObjects, gwObjects := readResources(t, []string{"services.yml", test.fixture})
|
||||||
|
|
||||||
kubeClient := kubefake.NewClientset(k8sObjects...)
|
kubeClient := kubefake.NewClientset(k8sObjects...)
|
||||||
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
||||||
@@ -8501,20 +8505,22 @@ func TestCrossProviderNamespaces_TCPRoute(t *testing.T) {
|
|||||||
func TestCrossProviderNamespaces_TLSRoute(t *testing.T) {
|
func TestCrossProviderNamespaces_TLSRoute(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
fixture string
|
||||||
crossProviderNamespaces []string
|
crossProviderNamespaces []string
|
||||||
wantError bool
|
wantError bool
|
||||||
}{
|
}{
|
||||||
{desc: "nil: cross-provider TraefikService backendRefs accepted (backward compatible)", crossProviderNamespaces: nil, wantError: false},
|
{desc: "nil: cross-provider TraefikService backendRefs accepted (backward compatible)", fixture: "tlsroute/simple_cross_provider.yml", crossProviderNamespaces: nil, wantError: false},
|
||||||
{desc: "empty list: cross-provider TraefikService backendRefs are rejected, route dropped", crossProviderNamespaces: []string{}, wantError: true},
|
{desc: "empty list: cross-provider TraefikService backendRefs are rejected, route dropped", fixture: "tlsroute/simple_cross_provider.yml", crossProviderNamespaces: []string{}, wantError: true},
|
||||||
{desc: "namespace allowed: cross-provider TraefikService backendRefs accepted", crossProviderNamespaces: []string{"default"}, wantError: false},
|
{desc: "namespace allowed: cross-provider TraefikService backendRefs accepted", fixture: "tlsroute/simple_cross_provider.yml", crossProviderNamespaces: []string{"default"}, wantError: false},
|
||||||
{desc: "namespace not allowed: cross-provider TraefikService backendRefs rejected, route dropped", crossProviderNamespaces: []string{"other"}, wantError: true},
|
{desc: "namespace not allowed: cross-provider TraefikService backendRefs rejected, route dropped", fixture: "tlsroute/simple_cross_provider.yml", crossProviderNamespaces: []string{"other"}, wantError: true},
|
||||||
|
{desc: "namespace provided with cross-provider backendRef, route dropped", fixture: "tlsroute/invalid_cross_provider.yml", crossProviderNamespaces: []string{"other"}, wantError: true},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
k8sObjects, gwObjects := readResources(t, []string{"services.yml", "tlsroute/simple_cross_provider.yml"})
|
k8sObjects, gwObjects := readResources(t, []string{"services.yml", test.fixture})
|
||||||
|
|
||||||
kubeClient := kubefake.NewClientset(k8sObjects...)
|
kubeClient := kubefake.NewClientset(k8sObjects...)
|
||||||
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
||||||
|
|||||||
@@ -221,6 +221,17 @@ func (p *Provider) loadTCPService(route *gatev1alpha2.TCPRoute, backendRef gatev
|
|||||||
namespace := route.Namespace
|
namespace := route.Namespace
|
||||||
if backendRef.Namespace != nil && *backendRef.Namespace != "" {
|
if backendRef.Namespace != nil && *backendRef.Namespace != "" {
|
||||||
namespace = string(*backendRef.Namespace)
|
namespace = string(*backendRef.Namespace)
|
||||||
|
|
||||||
|
if strings.Contains(string(backendRef.Name), "@") {
|
||||||
|
return provider.Normalize(namespace + "-" + string(backendRef.Name)), nil, &metav1.Condition{
|
||||||
|
Type: string(gatev1.RouteConditionResolvedRefs),
|
||||||
|
Status: metav1.ConditionFalse,
|
||||||
|
ObservedGeneration: route.Generation,
|
||||||
|
LastTransitionTime: metav1.Now(),
|
||||||
|
Reason: string(gatev1.RouteReasonRefNotPermitted),
|
||||||
|
Message: fmt.Sprintf("Cannot load TCPRoute BackendRef %s/%s/%s/%s: namespace is not allowed with a cross-provider reference", group, kind, namespace, backendRef.Name),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceName := provider.Normalize(namespace + "-" + string(backendRef.Name))
|
serviceName := provider.Normalize(namespace + "-" + string(backendRef.Name))
|
||||||
|
|||||||
@@ -224,6 +224,17 @@ func (p *Provider) loadTLSService(route *gatev1alpha2.TLSRoute, backendRef gatev
|
|||||||
namespace := route.Namespace
|
namespace := route.Namespace
|
||||||
if backendRef.Namespace != nil && *backendRef.Namespace != "" {
|
if backendRef.Namespace != nil && *backendRef.Namespace != "" {
|
||||||
namespace = string(*backendRef.Namespace)
|
namespace = string(*backendRef.Namespace)
|
||||||
|
|
||||||
|
if strings.Contains(string(backendRef.Name), "@") {
|
||||||
|
return provider.Normalize(namespace + "-" + string(backendRef.Name)), nil, &metav1.Condition{
|
||||||
|
Type: string(gatev1.RouteConditionResolvedRefs),
|
||||||
|
Status: metav1.ConditionFalse,
|
||||||
|
ObservedGeneration: route.Generation,
|
||||||
|
LastTransitionTime: metav1.Now(),
|
||||||
|
Reason: string(gatev1.RouteReasonRefNotPermitted),
|
||||||
|
Message: fmt.Sprintf("Cannot load TLSRoute BackendRef %s/%s/%s/%s: namespace is not allowed with a cross-provider reference", group, kind, namespace, backendRef.Name),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceName := provider.Normalize(namespace + "-" + string(backendRef.Name))
|
serviceName := provider.Normalize(namespace + "-" + string(backendRef.Name))
|
||||||
|
|||||||
Reference in New Issue
Block a user