IngressRoute: add an option to disable cross-namespace routing
Co-authored-by: Jean-Baptiste Doumenjou <925513+jbdoumenjou@users.noreply.github.com>
This commit is contained in:
parent
c72769e2ea
commit
7ba907f261
21 changed files with 1123 additions and 36 deletions
|
@ -250,6 +250,34 @@ providers:
|
||||||
--providers.kubernetescrd.throttleDuration=10s
|
--providers.kubernetescrd.throttleDuration=10s
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `allowCrossNamespace`
|
||||||
|
|
||||||
|
_Optional, Default: true_
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[providers.kubernetesCRD]
|
||||||
|
allowCrossNamespace = false
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
providers:
|
||||||
|
kubernetesCRD:
|
||||||
|
allowCrossNamespace: false
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="CLI"
|
||||||
|
--providers.kubernetescrd.allowCrossNamespace=false
|
||||||
|
```
|
||||||
|
|
||||||
|
If the parameter is set to `false`, an IngressRoute will not be able to reference any resources
|
||||||
|
in another namespace than the IngressRoute namespace.
|
||||||
|
|
||||||
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
Please notice that the default value for this option will be set to `false` in a future version.
|
||||||
|
|
||||||
## Further
|
## Further
|
||||||
|
|
||||||
Also see the [full example](../user-guides/crd-acme/index.md) with Let's Encrypt.
|
Also see the [full example](../user-guides/crd-acme/index.md) with Let's Encrypt.
|
||||||
|
|
|
@ -540,6 +540,9 @@ TLS key
|
||||||
`--providers.kubernetescrd`:
|
`--providers.kubernetescrd`:
|
||||||
Enable Kubernetes backend with default settings. (Default: ```false```)
|
Enable Kubernetes backend with default settings. (Default: ```false```)
|
||||||
|
|
||||||
|
`--providers.kubernetescrd.allowcrossnamespace`:
|
||||||
|
Allow cross namespace resource reference. (Default: ```true```)
|
||||||
|
|
||||||
`--providers.kubernetescrd.certauthfilepath`:
|
`--providers.kubernetescrd.certauthfilepath`:
|
||||||
Kubernetes certificate authority file path (not needed for in-cluster client).
|
Kubernetes certificate authority file path (not needed for in-cluster client).
|
||||||
|
|
||||||
|
|
|
@ -540,6 +540,9 @@ TLS key
|
||||||
`TRAEFIK_PROVIDERS_KUBERNETESCRD`:
|
`TRAEFIK_PROVIDERS_KUBERNETESCRD`:
|
||||||
Enable Kubernetes backend with default settings. (Default: ```false```)
|
Enable Kubernetes backend with default settings. (Default: ```false```)
|
||||||
|
|
||||||
|
`TRAEFIK_PROVIDERS_KUBERNETESCRD_ALLOWCROSSNAMESPACE`:
|
||||||
|
Allow cross namespace resource reference. (Default: ```true```)
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_KUBERNETESCRD_CERTAUTHFILEPATH`:
|
`TRAEFIK_PROVIDERS_KUBERNETESCRD_CERTAUTHFILEPATH`:
|
||||||
Kubernetes certificate authority file path (not needed for in-cluster client).
|
Kubernetes certificate authority file path (not needed for in-cluster client).
|
||||||
|
|
||||||
|
|
|
@ -113,6 +113,7 @@
|
||||||
certAuthFilePath = "foobar"
|
certAuthFilePath = "foobar"
|
||||||
disablePassHostHeaders = true
|
disablePassHostHeaders = true
|
||||||
namespaces = ["foobar", "foobar"]
|
namespaces = ["foobar", "foobar"]
|
||||||
|
allowCrossNamespace = true
|
||||||
labelSelector = "foobar"
|
labelSelector = "foobar"
|
||||||
ingressClass = "foobar"
|
ingressClass = "foobar"
|
||||||
throttleDuration = 42
|
throttleDuration = 42
|
||||||
|
|
|
@ -123,6 +123,7 @@ providers:
|
||||||
namespaces:
|
namespaces:
|
||||||
- foobar
|
- foobar
|
||||||
- foobar
|
- foobar
|
||||||
|
allowCrossNamespace: true
|
||||||
labelSelector: foobar
|
labelSelector: foobar
|
||||||
ingressClass: foobar
|
ingressClass: foobar
|
||||||
throttleDuration: 42s
|
throttleDuration: 42s
|
||||||
|
|
120
integration/fixtures/k8s/07-ingressroute-cross-namespace.yml
Normal file
120
integration/fixtures/k8s/07-ingressroute-cross-namespace.yml
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: other-ns
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: whoami
|
||||||
|
namespace: other-ns
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
selector:
|
||||||
|
app: traefiklabs
|
||||||
|
task: whoami
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test6.route
|
||||||
|
namespace: other-ns
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
routes:
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/a`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
namespace: default
|
||||||
|
port: 80
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/b`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: wrr2
|
||||||
|
namespace: default
|
||||||
|
kind: TraefikService
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/c`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: wrr3
|
||||||
|
kind: TraefikService
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/d`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
namespace: other-ns
|
||||||
|
port: 80
|
||||||
|
middlewares:
|
||||||
|
- name: stripprefix2
|
||||||
|
namespace: default
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/e`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
port: 80
|
||||||
|
middlewares:
|
||||||
|
- name: test-errorpage
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: TraefikService
|
||||||
|
metadata:
|
||||||
|
name: wrr2
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
weighted:
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
port: 80
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: TraefikService
|
||||||
|
metadata:
|
||||||
|
name: wrr3
|
||||||
|
namespace: other-ns
|
||||||
|
|
||||||
|
spec:
|
||||||
|
weighted:
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
namespace: default
|
||||||
|
port: 80
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: stripprefix2
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
stripPrefix:
|
||||||
|
prefixes:
|
||||||
|
- /tobestripped
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: test-errorpage
|
||||||
|
|
||||||
|
spec:
|
||||||
|
errors:
|
||||||
|
status:
|
||||||
|
- 500-599
|
||||||
|
query: /{status}.html
|
||||||
|
service:
|
||||||
|
name: whoami
|
||||||
|
namespace: other-ns
|
||||||
|
port: 80
|
|
@ -16,3 +16,4 @@
|
||||||
address = ":8000"
|
address = ":8000"
|
||||||
|
|
||||||
[providers.kubernetesCRD]
|
[providers.kubernetesCRD]
|
||||||
|
allowCrossNamespace = false
|
||||||
|
|
|
@ -53,6 +53,7 @@ func (s *K8sSuite) TearDownSuite(c *check.C) {
|
||||||
"./fixtures/k8s/coredns.yaml",
|
"./fixtures/k8s/coredns.yaml",
|
||||||
"./fixtures/k8s/rolebindings.yaml",
|
"./fixtures/k8s/rolebindings.yaml",
|
||||||
"./fixtures/k8s/traefik.yaml",
|
"./fixtures/k8s/traefik.yaml",
|
||||||
|
"./fixtures/k8s/ccm.yaml",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, filename := range generatedFiles {
|
for _, filename := range generatedFiles {
|
||||||
|
|
33
integration/testdata/rawdata-crd.json
vendored
33
integration/testdata/rawdata-crd.json
vendored
|
@ -50,6 +50,20 @@
|
||||||
"using": [
|
"using": [
|
||||||
"web"
|
"web"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"other-ns-test6-route-482e4988e134701d8cc8@kubernetescrd": {
|
||||||
|
"entryPoints": [
|
||||||
|
"web"
|
||||||
|
],
|
||||||
|
"service": "other-ns-wrr3",
|
||||||
|
"rule": "Host(`foo.com`) \u0026\u0026 PathPrefix(`/c`)",
|
||||||
|
"error": [
|
||||||
|
"the service \"other-ns-wrr3@kubernetescrd\" does not exist"
|
||||||
|
],
|
||||||
|
"status": "disabled",
|
||||||
|
"using": [
|
||||||
|
"web"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"middlewares": {
|
"middlewares": {
|
||||||
|
@ -64,6 +78,14 @@
|
||||||
"default-test2-route-23c7f4c450289ee29016@kubernetescrd"
|
"default-test2-route-23c7f4c450289ee29016@kubernetescrd"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"default-stripprefix2@kubernetescrd": {
|
||||||
|
"stripPrefix": {
|
||||||
|
"prefixes": [
|
||||||
|
"/tobestripped"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": "enabled"
|
||||||
|
},
|
||||||
"default-stripprefix@kubernetescrd": {
|
"default-stripprefix@kubernetescrd": {
|
||||||
"stripPrefix": {
|
"stripPrefix": {
|
||||||
"prefixes": [
|
"prefixes": [
|
||||||
|
@ -172,6 +194,17 @@
|
||||||
"default-test3-route-7d0ac22d3d8db4b82618@kubernetescrd"
|
"default-test3-route-7d0ac22d3d8db4b82618@kubernetescrd"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"default-wrr2@kubernetescrd": {
|
||||||
|
"weighted": {
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"name": "default-whoami-80",
|
||||||
|
"weight": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": "enabled"
|
||||||
|
},
|
||||||
"noop@internal": {
|
"noop@internal": {
|
||||||
"status": "enabled"
|
"status": "enabled"
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,3 +199,33 @@ spec:
|
||||||
- name: http
|
- name: http
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
port: 8080
|
port: 8080
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: whoami-svc
|
||||||
|
namespace: cross-ns
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: web
|
||||||
|
port: 80
|
||||||
|
selector:
|
||||||
|
app: traefiklabs
|
||||||
|
task: whoami
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Endpoints
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: whoami-svc
|
||||||
|
namespace: cross-ns
|
||||||
|
|
||||||
|
subsets:
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.10.0.1
|
||||||
|
- ip: 10.10.0.2
|
||||||
|
ports:
|
||||||
|
- name: web
|
||||||
|
port: 80
|
||||||
|
|
|
@ -208,3 +208,33 @@ metadata:
|
||||||
spec:
|
spec:
|
||||||
externalName: "fe80::200:5aee:feaa:20a2"
|
externalName: "fe80::200:5aee:feaa:20a2"
|
||||||
type: ExternalName
|
type: ExternalName
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: whoamitcp-cross-ns
|
||||||
|
namespace: cross-ns
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: myapp
|
||||||
|
port: 8000
|
||||||
|
selector:
|
||||||
|
app: traefiklabs
|
||||||
|
task: whoamitcp
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Endpoints
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: whoamitcp-cross-ns
|
||||||
|
namespace: cross-ns
|
||||||
|
|
||||||
|
subsets:
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.10.0.1
|
||||||
|
- ip: 10.10.0.2
|
||||||
|
ports:
|
||||||
|
- name: myapp
|
||||||
|
port: 8000
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRouteTCP
|
||||||
|
metadata:
|
||||||
|
name: test.route
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- foo
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: HostSNI(`foo.com`)
|
||||||
|
services:
|
||||||
|
- name: whoamitcp-cross-ns
|
||||||
|
namespace: cross-ns
|
||||||
|
port: 8000
|
|
@ -130,3 +130,33 @@ subsets:
|
||||||
ports:
|
ports:
|
||||||
- name: myapp-ipv6
|
- name: myapp-ipv6
|
||||||
port: 8080
|
port: 8080
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: whoamiudp-cross-ns
|
||||||
|
namespace: cross-ns
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: myapp
|
||||||
|
port: 8000
|
||||||
|
selector:
|
||||||
|
app: traefiklabs
|
||||||
|
task: whoamiudp
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Endpoints
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: whoamiudp-cross-ns
|
||||||
|
namespace: cross-ns
|
||||||
|
|
||||||
|
subsets:
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.10.0.1
|
||||||
|
- ip: 10.10.0.2
|
||||||
|
ports:
|
||||||
|
- name: myapp
|
||||||
|
port: 8000
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRouteUDP
|
||||||
|
metadata:
|
||||||
|
name: test.route
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- foo
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- services:
|
||||||
|
- name: whoamiudp-cross-ns
|
||||||
|
namespace: cross-ns
|
||||||
|
port: 8000
|
|
@ -0,0 +1,91 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: cross-ns-route
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- foo
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/bar`)
|
||||||
|
kind: Rule
|
||||||
|
priority: 12
|
||||||
|
services:
|
||||||
|
- name: whoami-svc
|
||||||
|
namespace: cross-ns
|
||||||
|
port: 80
|
||||||
|
- name: tr-svc-wrr1
|
||||||
|
kind: TraefikService
|
||||||
|
- name: tr-svc-wrr2
|
||||||
|
namespace: cross-ns
|
||||||
|
kind: TraefikService
|
||||||
|
- name: tr-svc-mirror1
|
||||||
|
kind: TraefikService
|
||||||
|
- name: tr-svc-mirror2
|
||||||
|
namespace: cross-ns
|
||||||
|
kind: TraefikService
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: TraefikService
|
||||||
|
metadata:
|
||||||
|
name: tr-svc-wrr1
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
weighted:
|
||||||
|
services:
|
||||||
|
- name: whoami-svc
|
||||||
|
namespace: cross-ns
|
||||||
|
weight: 1
|
||||||
|
port: 80
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: TraefikService
|
||||||
|
metadata:
|
||||||
|
name: tr-svc-wrr2
|
||||||
|
namespace: cross-ns
|
||||||
|
|
||||||
|
spec:
|
||||||
|
weighted:
|
||||||
|
services:
|
||||||
|
- name: whoami-svc
|
||||||
|
weight: 1
|
||||||
|
port: 80
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: TraefikService
|
||||||
|
metadata:
|
||||||
|
name: tr-svc-mirror1
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
mirroring:
|
||||||
|
name: whoami
|
||||||
|
port: 80
|
||||||
|
mirrors:
|
||||||
|
- name: whoami-svc
|
||||||
|
namespace: cross-ns
|
||||||
|
percent: 20
|
||||||
|
port: 80
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: TraefikService
|
||||||
|
metadata:
|
||||||
|
name: tr-svc-mirror2
|
||||||
|
namespace: cross-ns
|
||||||
|
|
||||||
|
spec:
|
||||||
|
mirroring:
|
||||||
|
name: whoami-svc
|
||||||
|
port: 80
|
||||||
|
mirrors:
|
||||||
|
- name: whoami-svc
|
||||||
|
namespace: cross-ns
|
||||||
|
percent: 20
|
||||||
|
port: 80
|
|
@ -0,0 +1,58 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test-crossnamespace.route
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- foo
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/bar`)
|
||||||
|
kind: Rule
|
||||||
|
priority: 12
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
namespace: default
|
||||||
|
port: 80
|
||||||
|
middlewares:
|
||||||
|
- name: stripprefix
|
||||||
|
namespace: cross-ns
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/bir`)
|
||||||
|
kind: Rule
|
||||||
|
priority: 12
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
namespace: default
|
||||||
|
port: 80
|
||||||
|
middlewares:
|
||||||
|
- name: test-errorpage
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: stripprefix
|
||||||
|
namespace: cross-ns
|
||||||
|
|
||||||
|
spec:
|
||||||
|
stripPrefix:
|
||||||
|
prefixes:
|
||||||
|
- /stripit
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: test-errorpage
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
errors:
|
||||||
|
status:
|
||||||
|
- 500-599
|
||||||
|
query: /{status}.html
|
||||||
|
service:
|
||||||
|
name: whoami-svc
|
||||||
|
namespace: cross-ns
|
||||||
|
port: 80
|
|
@ -43,6 +43,7 @@ type Provider struct {
|
||||||
CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"`
|
CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"`
|
||||||
DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers." json:"disablePassHostHeaders,omitempty" toml:"disablePassHostHeaders,omitempty" yaml:"disablePassHostHeaders,omitempty" export:"true"`
|
DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers." json:"disablePassHostHeaders,omitempty" toml:"disablePassHostHeaders,omitempty" yaml:"disablePassHostHeaders,omitempty" export:"true"`
|
||||||
Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"`
|
Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"`
|
||||||
|
AllowCrossNamespace *bool `description:"Allow cross namespace resource reference." json:"allowCrossNamespace,omitempty" toml:"allowCrossNamespace,omitempty" yaml:"allowCrossNamespace,omitempty" export:"true"`
|
||||||
LabelSelector string `description:"Kubernetes label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"`
|
LabelSelector string `description:"Kubernetes label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"`
|
||||||
IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"`
|
IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"`
|
||||||
ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"`
|
ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"`
|
||||||
|
@ -82,6 +83,11 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) {
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDefaults sets the default values.
|
||||||
|
func (p *Provider) SetDefaults() {
|
||||||
|
p.AllowCrossNamespace = func(b bool) *bool { return &b }(true)
|
||||||
|
}
|
||||||
|
|
||||||
// Init the provider.
|
// Init the provider.
|
||||||
func (p *Provider) Init() error {
|
func (p *Provider) Init() error {
|
||||||
return nil
|
return nil
|
||||||
|
@ -98,6 +104,10 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.AllowCrossNamespace == nil || *p.AllowCrossNamespace {
|
||||||
|
logger.Warn("Cross-namespace reference between IngressRoutes and resources is enabled, please ensure that this is expected (see AllowCrossNamespace option)")
|
||||||
|
}
|
||||||
|
|
||||||
pool.GoCtx(func(ctxPool context.Context) {
|
pool.GoCtx(func(ctxPool context.Context) {
|
||||||
operation := func() error {
|
operation := func() error {
|
||||||
eventsChan, err := k8sClient.WatchAll(p.Namespaces, ctxPool.Done())
|
eventsChan, err := k8sClient.WatchAll(p.Namespaces, ctxPool.Done())
|
||||||
|
@ -197,7 +207,7 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
errorPage, errorPageService, err := createErrorPageMiddleware(client, middleware.Namespace, middleware.Spec.Errors)
|
errorPage, errorPageService, err := p.createErrorPageMiddleware(client, middleware.Namespace, middleware.Spec.Errors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.FromContext(ctxMid).Errorf("Error while reading error page middleware: %v", err)
|
log.FromContext(ctxMid).Errorf("Error while reading error page middleware: %v", err)
|
||||||
continue
|
continue
|
||||||
|
@ -236,7 +246,7 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cb := configBuilder{client}
|
cb := configBuilder{client, p.AllowCrossNamespace}
|
||||||
for _, service := range client.GetTraefikServices() {
|
for _, service := range client.GetTraefikServices() {
|
||||||
err := cb.buildTraefikService(ctx, service, conf.HTTP.Services)
|
err := cb.buildTraefikService(ctx, service, conf.HTTP.Services)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -281,7 +291,7 @@ func getServicePort(svc *corev1.Service, port int32) (*corev1.ServicePort, error
|
||||||
return &corev1.ServicePort{Port: port}, nil
|
return &corev1.ServicePort{Port: port}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createErrorPageMiddleware(client Client, namespace string, errorPage *v1alpha1.ErrorPage) (*dynamic.ErrorPage, *dynamic.Service, error) {
|
func (p *Provider) createErrorPageMiddleware(client Client, namespace string, errorPage *v1alpha1.ErrorPage) (*dynamic.ErrorPage, *dynamic.Service, error) {
|
||||||
if errorPage == nil {
|
if errorPage == nil {
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
|
@ -291,7 +301,7 @@ func createErrorPageMiddleware(client Client, namespace string, errorPage *v1alp
|
||||||
Query: errorPage.Query,
|
Query: errorPage.Query,
|
||||||
}
|
}
|
||||||
|
|
||||||
balancerServerHTTP, err := configBuilder{client}.buildServersLB(namespace, errorPage.Service.LoadBalancerSpec)
|
balancerServerHTTP, err := configBuilder{client, p.AllowCrossNamespace}.buildServersLB(namespace, errorPage.Service.LoadBalancerSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -749,3 +759,8 @@ func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *s
|
||||||
|
|
||||||
return eventsChanBuffered
|
return eventsChanBuffered
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isNamespaceAllowed(allowCrossNamespace *bool, parentNamespace, namespace string) bool {
|
||||||
|
// If allowCrossNamespace option is not defined the default behavior is to allow cross namespace references.
|
||||||
|
return allowCrossNamespace == nil || *allowCrossNamespace || parentNamespace == namespace
|
||||||
|
}
|
||||||
|
|
|
@ -48,7 +48,8 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
|
||||||
ingressName = ingressRoute.GenerateName
|
ingressName = ingressRoute.GenerateName
|
||||||
}
|
}
|
||||||
|
|
||||||
cb := configBuilder{client}
|
cb := configBuilder{client, p.AllowCrossNamespace}
|
||||||
|
|
||||||
for _, route := range ingressRoute.Spec.Routes {
|
for _, route := range ingressRoute.Spec.Routes {
|
||||||
if route.Kind != "Rule" {
|
if route.Kind != "Rule" {
|
||||||
logger.Errorf("Unsupported match kind: %s. Only \"Rule\" is supported for now.", route.Kind)
|
logger.Errorf("Unsupported match kind: %s. Only \"Rule\" is supported for now.", route.Kind)
|
||||||
|
@ -66,23 +67,10 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var mds []string
|
mds, err := p.makeMiddlewareKeys(ctx, ingressRoute.Namespace, route.Middlewares)
|
||||||
for _, mi := range route.Middlewares {
|
if err != nil {
|
||||||
if strings.Contains(mi.Name, providerNamespaceSeparator) {
|
logger.Errorf("Failed to create middleware keys: %v", err)
|
||||||
if len(mi.Namespace) > 0 {
|
continue
|
||||||
logger.
|
|
||||||
WithField(log.MiddlewareName, mi.Name).
|
|
||||||
Warnf("namespace %q is ignored in cross-provider context", mi.Namespace)
|
|
||||||
}
|
|
||||||
mds = append(mds, mi.Name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ns := mi.Namespace
|
|
||||||
if len(ns) == 0 {
|
|
||||||
ns = ingressRoute.Namespace
|
|
||||||
}
|
|
||||||
mds = append(mds, makeID(ns, mi.Name))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
normalized := provider.Normalize(makeID(ingressRoute.Namespace, serviceKey))
|
normalized := provider.Normalize(makeID(ingressRoute.Namespace, serviceKey))
|
||||||
|
@ -153,8 +141,38 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace string, middlewares []v1alpha1.MiddlewareRef) ([]string, error) {
|
||||||
|
var mds []string
|
||||||
|
|
||||||
|
for _, mi := range middlewares {
|
||||||
|
if strings.Contains(mi.Name, providerNamespaceSeparator) {
|
||||||
|
if len(mi.Namespace) > 0 {
|
||||||
|
log.FromContext(ctx).
|
||||||
|
WithField(log.MiddlewareName, mi.Name).
|
||||||
|
Warnf("namespace %q is ignored in cross-provider context", mi.Namespace)
|
||||||
|
}
|
||||||
|
mds = append(mds, mi.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ns := ingRouteNamespace
|
||||||
|
if len(mi.Namespace) > 0 {
|
||||||
|
if !isNamespaceAllowed(p.AllowCrossNamespace, ingRouteNamespace, mi.Namespace) {
|
||||||
|
return nil, fmt.Errorf("middleware %s/%s is not in the IngressRoute namespace %s", mi.Namespace, mi.Name, ingRouteNamespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
ns = mi.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
mds = append(mds, makeID(ns, mi.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
return mds, nil
|
||||||
|
}
|
||||||
|
|
||||||
type configBuilder struct {
|
type configBuilder struct {
|
||||||
client Client
|
client Client
|
||||||
|
allowCrossNamespace *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildTraefikService creates the configuration for the traefik service defined in tService,
|
// buildTraefikService creates the configuration for the traefik service defined in tService,
|
||||||
|
@ -270,7 +288,7 @@ func (c configBuilder) buildServersLB(namespace string, svc v1alpha1.LoadBalance
|
||||||
return &dynamic.Service{LoadBalancer: lb}, nil
|
return &dynamic.Service{LoadBalancer: lb}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c configBuilder) loadServers(fallbackNamespace string, svc v1alpha1.LoadBalancerSpec) ([]dynamic.Server, error) {
|
func (c configBuilder) loadServers(parentNamespace string, svc v1alpha1.LoadBalancerSpec) ([]dynamic.Server, error) {
|
||||||
strategy := svc.Strategy
|
strategy := svc.Strategy
|
||||||
if strategy == "" {
|
if strategy == "" {
|
||||||
strategy = roundRobinStrategy
|
strategy = roundRobinStrategy
|
||||||
|
@ -279,7 +297,11 @@ func (c configBuilder) loadServers(fallbackNamespace string, svc v1alpha1.LoadBa
|
||||||
return nil, fmt.Errorf("load balancing strategy %s is not supported", strategy)
|
return nil, fmt.Errorf("load balancing strategy %s is not supported", strategy)
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace := namespaceOrFallback(svc, fallbackNamespace)
|
namespace := namespaceOrFallback(svc, parentNamespace)
|
||||||
|
|
||||||
|
if !isNamespaceAllowed(c.allowCrossNamespace, parentNamespace, namespace) {
|
||||||
|
return nil, fmt.Errorf("load balancer service %s/%s is not in the parent resource namespace %s", svc.Namespace, svc.Name, parentNamespace)
|
||||||
|
}
|
||||||
|
|
||||||
// If the service uses explicitly the provider suffix
|
// If the service uses explicitly the provider suffix
|
||||||
sanitizedName := strings.TrimSuffix(svc.Name, providerNamespaceSeparator+providerName)
|
sanitizedName := strings.TrimSuffix(svc.Name, providerNamespaceSeparator+providerName)
|
||||||
|
@ -355,10 +377,14 @@ func (c configBuilder) loadServers(fallbackNamespace string, svc v1alpha1.LoadBa
|
||||||
// In addition, if the service is a Kubernetes one,
|
// In addition, if the service is a Kubernetes one,
|
||||||
// it generates and returns the configuration part for such a service,
|
// it generates and returns the configuration part for such a service,
|
||||||
// so that the caller can add it to the global config map.
|
// so that the caller can add it to the global config map.
|
||||||
func (c configBuilder) nameAndService(ctx context.Context, namespaceService string, service v1alpha1.LoadBalancerSpec) (string, *dynamic.Service, error) {
|
func (c configBuilder) nameAndService(ctx context.Context, parentNamespace string, service v1alpha1.LoadBalancerSpec) (string, *dynamic.Service, error) {
|
||||||
svcCtx := log.With(ctx, log.Str(log.ServiceName, service.Name))
|
svcCtx := log.With(ctx, log.Str(log.ServiceName, service.Name))
|
||||||
|
|
||||||
namespace := namespaceOrFallback(service, namespaceService)
|
namespace := namespaceOrFallback(service, parentNamespace)
|
||||||
|
|
||||||
|
if !isNamespaceAllowed(c.allowCrossNamespace, parentNamespace, namespace) {
|
||||||
|
return "", nil, fmt.Errorf("service %s/%s not in the parent resource namespace %s", service.Namespace, service.Name, parentNamespace)
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case service.Kind == "" || service.Kind == "Service":
|
case service.Kind == "" || service.Kind == "Service":
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
|
||||||
serviceName := makeID(ingressRouteTCP.Namespace, key)
|
serviceName := makeID(ingressRouteTCP.Namespace, key)
|
||||||
|
|
||||||
for _, service := range route.Services {
|
for _, service := range route.Services {
|
||||||
balancerServerTCP, err := createLoadBalancerServerTCP(client, ingressRouteTCP.Namespace, service)
|
balancerServerTCP, err := p.createLoadBalancerServerTCP(client, ingressRouteTCP.Namespace, service)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.
|
logger.
|
||||||
WithField("serviceName", service.Name).
|
WithField("serviceName", service.Name).
|
||||||
|
@ -125,9 +125,13 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
func createLoadBalancerServerTCP(client Client, namespace string, service v1alpha1.ServiceTCP) (*dynamic.TCPService, error) {
|
func (p *Provider) createLoadBalancerServerTCP(client Client, parentNamespace string, service v1alpha1.ServiceTCP) (*dynamic.TCPService, error) {
|
||||||
ns := namespace
|
ns := parentNamespace
|
||||||
if len(service.Namespace) > 0 {
|
if len(service.Namespace) > 0 {
|
||||||
|
if !isNamespaceAllowed(p.AllowCrossNamespace, parentNamespace, service.Namespace) {
|
||||||
|
return nil, fmt.Errorf("tcp service %s/%s is not in the parent resource namespace %s", service.Namespace, service.Name, parentNamespace)
|
||||||
|
}
|
||||||
|
|
||||||
ns = service.Namespace
|
ns = service.Namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,21 @@ package crd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||||
"github.com/traefik/traefik/v2/pkg/provider"
|
"github.com/traefik/traefik/v2/pkg/provider"
|
||||||
|
crdfake "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/provider/kubernetes/k8s"
|
||||||
"github.com/traefik/traefik/v2/pkg/tls"
|
"github.com/traefik/traefik/v2/pkg/tls"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ provider.Provider = (*Provider)(nil)
|
var _ provider.Provider = (*Provider)(nil)
|
||||||
|
@ -1122,7 +1130,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
p := Provider{IngressClass: test.ingressClass}
|
p := Provider{IngressClass: test.ingressClass}
|
||||||
conf := p.loadConfigurationFromCRD(context.Background(), newClientMock(test.paths...))
|
p.SetDefaults()
|
||||||
|
|
||||||
|
clientMock := newClientMock(test.paths...)
|
||||||
|
conf := p.loadConfigurationFromCRD(context.Background(), clientMock)
|
||||||
assert.Equal(t, test.expected, conf)
|
assert.Equal(t, test.expected, conf)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -3187,7 +3198,10 @@ func TestLoadIngressRoutes(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
p := Provider{IngressClass: test.ingressClass}
|
p := Provider{IngressClass: test.ingressClass}
|
||||||
conf := p.loadConfigurationFromCRD(context.Background(), newClientMock(test.paths...))
|
p.SetDefaults()
|
||||||
|
|
||||||
|
clientMock := newClientMock(test.paths...)
|
||||||
|
conf := p.loadConfigurationFromCRD(context.Background(), clientMock)
|
||||||
assert.Equal(t, test.expected, conf)
|
assert.Equal(t, test.expected, conf)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -3495,7 +3509,10 @@ func TestLoadIngressRouteUDPs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
p := Provider{IngressClass: test.ingressClass}
|
p := Provider{IngressClass: test.ingressClass}
|
||||||
conf := p.loadConfigurationFromCRD(context.Background(), newClientMock(test.paths...))
|
p.SetDefaults()
|
||||||
|
|
||||||
|
clientMock := newClientMock(test.paths...)
|
||||||
|
conf := p.loadConfigurationFromCRD(context.Background(), clientMock)
|
||||||
assert.Equal(t, test.expected, conf)
|
assert.Equal(t, test.expected, conf)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -3714,3 +3731,563 @@ func TestGetServicePort(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCrossNamespace(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
allowCrossNamespace bool
|
||||||
|
ingressClass string
|
||||||
|
paths []string
|
||||||
|
expected *dynamic.Configuration
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "Empty",
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.UDPRouter{},
|
||||||
|
Services: map[string]*dynamic.UDPService{},
|
||||||
|
},
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Services: map[string]*dynamic.Service{},
|
||||||
|
},
|
||||||
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "HTTP middleware cross namespace disallowed",
|
||||||
|
paths: []string{"services.yml", "with_middleware_cross_namespace.yml"},
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.UDPRouter{},
|
||||||
|
Services: map[string]*dynamic.UDPService{},
|
||||||
|
},
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.Router{
|
||||||
|
"default-test-crossnamespace-route-9313b71dbe6a649d5049": {
|
||||||
|
EntryPoints: []string{"foo"},
|
||||||
|
Service: "default-test-crossnamespace-route-9313b71dbe6a649d5049",
|
||||||
|
Rule: "Host(`foo.com`) && PathPrefix(`/bir`)",
|
||||||
|
Priority: 12,
|
||||||
|
Middlewares: []string{"default-test-errorpage"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{
|
||||||
|
"cross-ns-stripprefix": {
|
||||||
|
StripPrefix: &dynamic.StripPrefix{
|
||||||
|
Prefixes: []string{"/stripit"},
|
||||||
|
ForceSlash: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: map[string]*dynamic.Service{
|
||||||
|
"default-test-crossnamespace-route-9313b71dbe6a649d5049": {
|
||||||
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
|
Servers: []dynamic.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PassHostHeader: Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "HTTP middleware cross namespace allowed",
|
||||||
|
paths: []string{"services.yml", "with_middleware_cross_namespace.yml"},
|
||||||
|
allowCrossNamespace: true,
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.UDPRouter{},
|
||||||
|
Services: map[string]*dynamic.UDPService{},
|
||||||
|
},
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.Router{
|
||||||
|
"default-test-crossnamespace-route-6b204d94623b3df4370c": {
|
||||||
|
EntryPoints: []string{"foo"},
|
||||||
|
Service: "default-test-crossnamespace-route-6b204d94623b3df4370c",
|
||||||
|
Rule: "Host(`foo.com`) && PathPrefix(`/bar`)",
|
||||||
|
Priority: 12,
|
||||||
|
Middlewares: []string{
|
||||||
|
"cross-ns-stripprefix",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"default-test-crossnamespace-route-9313b71dbe6a649d5049": {
|
||||||
|
EntryPoints: []string{"foo"},
|
||||||
|
Service: "default-test-crossnamespace-route-9313b71dbe6a649d5049",
|
||||||
|
Rule: "Host(`foo.com`) && PathPrefix(`/bir`)",
|
||||||
|
Priority: 12,
|
||||||
|
Middlewares: []string{"default-test-errorpage"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{
|
||||||
|
"cross-ns-stripprefix": {
|
||||||
|
StripPrefix: &dynamic.StripPrefix{
|
||||||
|
Prefixes: []string{"/stripit"},
|
||||||
|
ForceSlash: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"default-test-errorpage": {
|
||||||
|
Errors: &dynamic.ErrorPage{
|
||||||
|
Status: []string{"500-599"},
|
||||||
|
Service: "default-test-errorpage-errorpage-service",
|
||||||
|
Query: "/{status}.html",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: map[string]*dynamic.Service{
|
||||||
|
"default-test-crossnamespace-route-6b204d94623b3df4370c": {
|
||||||
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
|
Servers: []dynamic.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PassHostHeader: Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"default-test-crossnamespace-route-9313b71dbe6a649d5049": {
|
||||||
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
|
Servers: []dynamic.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PassHostHeader: Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"default-test-errorpage-errorpage-service": {
|
||||||
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
|
Servers: []dynamic.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PassHostHeader: Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "HTTP cross namespace allowed",
|
||||||
|
paths: []string{"services.yml", "with_cross_namespace.yml"},
|
||||||
|
allowCrossNamespace: true,
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.UDPRouter{},
|
||||||
|
Services: map[string]*dynamic.UDPService{},
|
||||||
|
},
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.Router{
|
||||||
|
"default-cross-ns-route-6b204d94623b3df4370c": {
|
||||||
|
EntryPoints: []string{"foo"},
|
||||||
|
Service: "default-cross-ns-route-6b204d94623b3df4370c",
|
||||||
|
Rule: "Host(`foo.com`) && PathPrefix(`/bar`)",
|
||||||
|
Priority: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Services: map[string]*dynamic.Service{
|
||||||
|
"default-cross-ns-route-6b204d94623b3df4370c": {
|
||||||
|
Weighted: &dynamic.WeightedRoundRobin{
|
||||||
|
Services: []dynamic.WRRService{
|
||||||
|
{
|
||||||
|
Name: "cross-ns-whoami-svc-80",
|
||||||
|
Weight: func(i int) *int { return &i }(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "default-tr-svc-wrr1",
|
||||||
|
Weight: func(i int) *int { return &i }(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "cross-ns-tr-svc-wrr2",
|
||||||
|
Weight: func(i int) *int { return &i }(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "default-tr-svc-mirror1",
|
||||||
|
Weight: func(i int) *int { return &i }(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "cross-ns-tr-svc-mirror2",
|
||||||
|
Weight: func(i int) *int { return &i }(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"cross-ns-whoami-svc-80": {
|
||||||
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
|
Servers: []dynamic.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PassHostHeader: Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"default-tr-svc-wrr1": {
|
||||||
|
Weighted: &dynamic.WeightedRoundRobin{
|
||||||
|
Services: []dynamic.WRRService{
|
||||||
|
{
|
||||||
|
Name: "cross-ns-whoami-svc-80",
|
||||||
|
Weight: func(i int) *int { return &i }(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"cross-ns-tr-svc-wrr2": {
|
||||||
|
Weighted: &dynamic.WeightedRoundRobin{
|
||||||
|
Services: []dynamic.WRRService{
|
||||||
|
{
|
||||||
|
Name: "cross-ns-whoami-svc-80",
|
||||||
|
Weight: func(i int) *int { return &i }(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"default-tr-svc-mirror1": {
|
||||||
|
Mirroring: &dynamic.Mirroring{
|
||||||
|
Service: "default-whoami-80",
|
||||||
|
Mirrors: []dynamic.MirrorService{
|
||||||
|
{
|
||||||
|
Name: "cross-ns-whoami-svc-80",
|
||||||
|
Percent: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"cross-ns-tr-svc-mirror2": {
|
||||||
|
Mirroring: &dynamic.Mirroring{
|
||||||
|
Service: "cross-ns-whoami-svc-80",
|
||||||
|
Mirrors: []dynamic.MirrorService{
|
||||||
|
{
|
||||||
|
Name: "cross-ns-whoami-svc-80",
|
||||||
|
Percent: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"default-whoami-80": {
|
||||||
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
|
Servers: []dynamic.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PassHostHeader: Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "HTTP cross namespace disallowed",
|
||||||
|
paths: []string{"services.yml", "with_cross_namespace.yml"},
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.UDPRouter{},
|
||||||
|
Services: map[string]*dynamic.UDPService{},
|
||||||
|
},
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Services: map[string]*dynamic.Service{
|
||||||
|
"cross-ns-tr-svc-wrr2": {
|
||||||
|
Weighted: &dynamic.WeightedRoundRobin{
|
||||||
|
Services: []dynamic.WRRService{
|
||||||
|
{
|
||||||
|
Name: "cross-ns-whoami-svc-80",
|
||||||
|
Weight: func(i int) *int { return &i }(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"cross-ns-whoami-svc-80": {
|
||||||
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
|
Servers: []dynamic.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PassHostHeader: Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"cross-ns-tr-svc-mirror2": {
|
||||||
|
Mirroring: &dynamic.Mirroring{
|
||||||
|
Service: "cross-ns-whoami-svc-80",
|
||||||
|
Mirrors: []dynamic.MirrorService{
|
||||||
|
{
|
||||||
|
Name: "cross-ns-whoami-svc-80",
|
||||||
|
Percent: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"default-whoami-80": {
|
||||||
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
|
Servers: []dynamic.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PassHostHeader: Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "TCP cross namespace allowed",
|
||||||
|
paths: []string{"tcp/services.yml", "tcp/with_cross_namespace.yml"},
|
||||||
|
allowCrossNamespace: true,
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.UDPRouter{},
|
||||||
|
Services: map[string]*dynamic.UDPService{},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Services: map[string]*dynamic.Service{},
|
||||||
|
},
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{
|
||||||
|
"default-test.route-fdd3e9338e47a45efefc": {
|
||||||
|
EntryPoints: []string{"foo"},
|
||||||
|
Service: "default-test.route-fdd3e9338e47a45efefc",
|
||||||
|
Rule: "HostSNI(`foo.com`)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: map[string]*dynamic.TCPService{
|
||||||
|
"default-test.route-fdd3e9338e47a45efefc": {
|
||||||
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
|
Servers: []dynamic.TCPServer{
|
||||||
|
{
|
||||||
|
Address: "10.10.0.1:8000",
|
||||||
|
Port: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Address: "10.10.0.2:8000",
|
||||||
|
Port: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "TCP cross namespace disallowed",
|
||||||
|
paths: []string{"tcp/services.yml", "tcp/with_cross_namespace.yml"},
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.UDPRouter{},
|
||||||
|
Services: map[string]*dynamic.UDPService{},
|
||||||
|
},
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
// The router that references the invalid service will be discarded.
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{
|
||||||
|
"default-test.route-fdd3e9338e47a45efefc": {
|
||||||
|
EntryPoints: []string{"foo"},
|
||||||
|
Service: "default-test.route-fdd3e9338e47a45efefc",
|
||||||
|
Rule: "HostSNI(`foo.com`)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Services: map[string]*dynamic.Service{},
|
||||||
|
},
|
||||||
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "UDP cross namespace allowed",
|
||||||
|
paths: []string{"udp/services.yml", "udp/with_cross_namespace.yml"},
|
||||||
|
allowCrossNamespace: true,
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.UDPRouter{
|
||||||
|
"default-test.route-0": {
|
||||||
|
EntryPoints: []string{"foo"},
|
||||||
|
Service: "default-test.route-0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: map[string]*dynamic.UDPService{
|
||||||
|
"default-test.route-0": {
|
||||||
|
LoadBalancer: &dynamic.UDPServersLoadBalancer{
|
||||||
|
Servers: []dynamic.UDPServer{
|
||||||
|
{
|
||||||
|
Address: "10.10.0.1:8000",
|
||||||
|
Port: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Address: "10.10.0.2:8000",
|
||||||
|
Port: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Services: map[string]*dynamic.Service{},
|
||||||
|
},
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
},
|
||||||
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "UDP cross namespace disallowed",
|
||||||
|
paths: []string{"udp/services.yml", "udp/with_cross_namespace.yml"},
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
// The router that references the invalid service will be discarded.
|
||||||
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.UDPRouter{
|
||||||
|
"default-test.route-0": {
|
||||||
|
EntryPoints: []string{"foo"},
|
||||||
|
Service: "default-test.route-0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: map[string]*dynamic.UDPService{},
|
||||||
|
},
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Services: map[string]*dynamic.Service{},
|
||||||
|
},
|
||||||
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var k8sObjects []runtime.Object
|
||||||
|
var crdObjects []runtime.Object
|
||||||
|
for _, path := range test.paths {
|
||||||
|
yamlContent, err := ioutil.ReadFile(filepath.FromSlash("./fixtures/" + path))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
objects := k8s.MustParseYaml(yamlContent)
|
||||||
|
for _, obj := range objects {
|
||||||
|
switch o := obj.(type) {
|
||||||
|
case *corev1.Service, *corev1.Endpoints, *corev1.Secret:
|
||||||
|
k8sObjects = append(k8sObjects, o)
|
||||||
|
case *v1alpha1.IngressRoute:
|
||||||
|
crdObjects = append(crdObjects, o)
|
||||||
|
case *v1alpha1.IngressRouteTCP:
|
||||||
|
crdObjects = append(crdObjects, o)
|
||||||
|
case *v1alpha1.IngressRouteUDP:
|
||||||
|
crdObjects = append(crdObjects, o)
|
||||||
|
case *v1alpha1.Middleware:
|
||||||
|
crdObjects = append(crdObjects, o)
|
||||||
|
case *v1alpha1.TraefikService:
|
||||||
|
crdObjects = append(crdObjects, o)
|
||||||
|
case *v1alpha1.TLSOption:
|
||||||
|
crdObjects = append(crdObjects, o)
|
||||||
|
case *v1alpha1.TLSStore:
|
||||||
|
crdObjects = append(crdObjects, o)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kubeClient := kubefake.NewSimpleClientset(k8sObjects...)
|
||||||
|
crdClient := crdfake.NewSimpleClientset(crdObjects...)
|
||||||
|
|
||||||
|
client := newClientImpl(kubeClient, crdClient)
|
||||||
|
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
|
||||||
|
eventCh, err := client.WatchAll([]string{"default", "cross-ns"}, stopCh)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
if k8sObjects != nil || crdObjects != nil {
|
||||||
|
// just wait for the first event
|
||||||
|
<-eventCh
|
||||||
|
}
|
||||||
|
|
||||||
|
p := Provider{}
|
||||||
|
p.SetDefaults()
|
||||||
|
|
||||||
|
p.AllowCrossNamespace = func(b bool) *bool { return &b }(test.allowCrossNamespace)
|
||||||
|
conf := p.loadConfigurationFromCRD(context.Background(), client)
|
||||||
|
assert.Equal(t, test.expected, conf)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ func (p *Provider) loadIngressRouteUDPConfiguration(ctx context.Context, client
|
||||||
serviceName := makeID(ingressRouteUDP.Namespace, key)
|
serviceName := makeID(ingressRouteUDP.Namespace, key)
|
||||||
|
|
||||||
for _, service := range route.Services {
|
for _, service := range route.Services {
|
||||||
balancerServerUDP, err := createLoadBalancerServerUDP(client, ingressRouteUDP.Namespace, service)
|
balancerServerUDP, err := p.createLoadBalancerServerUDP(client, ingressRouteUDP.Namespace, service)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.
|
logger.
|
||||||
WithField("serviceName", service.Name).
|
WithField("serviceName", service.Name).
|
||||||
|
@ -77,9 +77,13 @@ func (p *Provider) loadIngressRouteUDPConfiguration(ctx context.Context, client
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
func createLoadBalancerServerUDP(client Client, namespace string, service v1alpha1.ServiceUDP) (*dynamic.UDPService, error) {
|
func (p *Provider) createLoadBalancerServerUDP(client Client, parentNamespace string, service v1alpha1.ServiceUDP) (*dynamic.UDPService, error) {
|
||||||
ns := namespace
|
ns := parentNamespace
|
||||||
if len(service.Namespace) > 0 {
|
if len(service.Namespace) > 0 {
|
||||||
|
if !isNamespaceAllowed(p.AllowCrossNamespace, parentNamespace, service.Namespace) {
|
||||||
|
return nil, fmt.Errorf("udp service %s/%s is not in the parent resource namespace %s", service.Namespace, service.Name, ns)
|
||||||
|
}
|
||||||
|
|
||||||
ns = service.Namespace
|
ns = service.Namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue