diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 058f375da..d1de1ff81 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -73,6 +73,10 @@ blocks: run: when: "tag =~ '.*'" task: + agent: + machine: + type: e1-standard-8 + os_image: ubuntu1804 secrets: - name: traefik env_vars: diff --git a/pkg/config/kv/kv_node.go b/pkg/config/kv/kv_node.go index ef1571c1e..229525ef6 100644 --- a/pkg/config/kv/kv_node.go +++ b/pkg/config/kv/kv_node.go @@ -24,7 +24,7 @@ func DecodeToNode(pairs []*store.KVPair, rootName string, filters ...string) (*p return nil, fmt.Errorf("invalid label root %s", rootName) } - split := strings.Split(pair.Key[len(rootName)+1:], "/") + split := strings.FieldsFunc(pair.Key[len(rootName)+1:], func(c rune) bool { return c == '/' }) parts := []string{rootName} for _, fragment := range split { diff --git a/pkg/config/kv/kv_test.go b/pkg/config/kv/kv_test.go index c94da1bce..f7c0d7ed9 100644 --- a/pkg/config/kv/kv_test.go +++ b/pkg/config/kv/kv_test.go @@ -28,6 +28,7 @@ func TestDecode(t *testing.T) { "traefik/fieldf/Test2": "B", "traefik/fieldg/0/name": "A", "traefik/fieldg/1/name": "B", + "traefik/fieldh/": "foo", }, expected: &sample{ FieldA: "bar", @@ -45,6 +46,7 @@ func TestDecode(t *testing.T) { {Name: "A"}, {Name: "B"}, }, + FieldH: "foo", }, }, { @@ -61,6 +63,7 @@ func TestDecode(t *testing.T) { "foo/bar/traefik/fieldf/Test2": "B", "foo/bar/traefik/fieldg/0/name": "A", "foo/bar/traefik/fieldg/1/name": "B", + "foo/bar/traefik/fieldh/": "foo", }, expected: &sample{ FieldA: "bar", @@ -78,6 +81,7 @@ func TestDecode(t *testing.T) { {Name: "A"}, {Name: "B"}, }, + FieldH: "foo", }, }, } @@ -107,6 +111,7 @@ type sample struct { } `label:"allowEmpty"` FieldF map[string]string FieldG []sub + FieldH string } type sub struct { diff --git a/pkg/provider/kubernetes/crd/fixtures/with_middleware_cross_namespace.yml b/pkg/provider/kubernetes/crd/fixtures/with_middleware_cross_namespace.yml index aaed4a1d8..25a2ac448 100644 --- a/pkg/provider/kubernetes/crd/fixtures/with_middleware_cross_namespace.yml +++ b/pkg/provider/kubernetes/crd/fixtures/with_middleware_cross_namespace.yml @@ -28,6 +28,15 @@ spec: port: 80 middlewares: - name: test-errorpage + - match: Host(`foo.com`) && PathPrefix(`/bur`) + kind: Rule + priority: 12 + services: + - name: whoami + namespace: default + port: 80 + middlewares: + - name: cross-ns-stripprefix@kubernetescrd --- apiVersion: traefik.containo.us/v1alpha1 diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index 3f969d7e8..ba32bba33 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -147,13 +147,23 @@ func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace str var mds []string for _, mi := range middlewares { - if strings.Contains(mi.Name, providerNamespaceSeparator) { + name := mi.Name + + if !p.AllowCrossNamespace && strings.HasSuffix(mi.Name, providerNamespaceSeparator+providerName) { + // Since we are not able to know if another namespace is in the name (namespace-name@kubernetescrd), + // if the provider namespace kubernetescrd is used, + // we don't allow this format to avoid cross namespace references. + return nil, fmt.Errorf("invalid reference to middleware %s: with crossnamespace disallowed, the namespace field needs to be explicitly specified", mi.Name) + } + + if strings.Contains(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) + + mds = append(mds, name) continue } @@ -166,7 +176,7 @@ func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace str ns = mi.Namespace } - mds = append(mds, makeID(ns, mi.Name)) + mds = append(mds, makeID(ns, name)) } return mds, nil diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index 504e317da..8d62e6cf1 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -4313,6 +4313,13 @@ func TestCrossNamespace(t *testing.T) { Priority: 12, Middlewares: []string{"default-test-errorpage"}, }, + "default-test-crossnamespace-route-a1963878aac7331b7950": { + EntryPoints: []string{"foo"}, + Service: "default-test-crossnamespace-route-a1963878aac7331b7950", + Rule: "Host(`foo.com`) && PathPrefix(`/bur`)", + Priority: 12, + Middlewares: []string{"cross-ns-stripprefix@kubernetescrd"}, + }, }, Middlewares: map[string]*dynamic.Middleware{ "cross-ns-stripprefix": { @@ -4369,6 +4376,19 @@ func TestCrossNamespace(t *testing.T) { PassHostHeader: Bool(true), }, }, + "default-test-crossnamespace-route-a1963878aac7331b7950": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.1:80", + }, + { + URL: "http://10.10.0.2:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, }, ServersTransports: map[string]*dynamic.ServersTransport{}, },