From 55360c1eaf84f26adb3a59169f511eaaff9b1ece Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Doumenjou <925513+jbdoumenjou@users.noreply.github.com> Date: Tue, 14 Sep 2021 10:42:14 +0200 Subject: [PATCH 01/12] Add Tom Moulard in maintainers team --- docs/content/contributing/maintainers.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/content/contributing/maintainers.md b/docs/content/contributing/maintainers.md index 179056779..3a595dffe 100644 --- a/docs/content/contributing/maintainers.md +++ b/docs/content/contributing/maintainers.md @@ -19,6 +19,7 @@ * Romain Tribotté [@rtribotte](https://github.com/rtribotte) * Kevin Pollet [@kevinpollet](https://github.com/kevinpollet) * Harold Ozouf [@jspdown](https://github.com/jspdown) +* Tom Moulard [@tommoulard](https://github.com/tommoulard) ## Maintainer's Guidelines From a72d12455169cad59d2195c8e448cee30163001e Mon Sep 17 00:00:00 2001 From: Tom Moulard Date: Tue, 14 Sep 2021 17:12:12 +0200 Subject: [PATCH 02/12] Fix certChan defaulting on consul catalog provider --- pkg/provider/consulcatalog/consul_catalog.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/provider/consulcatalog/consul_catalog.go b/pkg/provider/consulcatalog/consul_catalog.go index b931e730b..3ae86aee3 100644 --- a/pkg/provider/consulcatalog/consul_catalog.go +++ b/pkg/provider/consulcatalog/consul_catalog.go @@ -87,7 +87,6 @@ func (p *Provider) SetDefaults() { p.ExposedByDefault = true p.DefaultRule = DefaultTemplateRule p.ServiceName = "traefik" - p.certChan = make(chan *connectCert) } // Init the provider. @@ -98,6 +97,7 @@ func (p *Provider) Init() error { } p.defaultRuleTpl = defaultRuleTpl + p.certChan = make(chan *connectCert) return nil } From 6e28db513c0839c6ebf20d4ddb489915cc3bbfd4 Mon Sep 17 00:00:00 2001 From: Tom Moulard Date: Wed, 15 Sep 2021 17:26:06 +0200 Subject: [PATCH 03/12] Metrics router fix Co-authored-by: Michael Co-authored-by: Romain --- integration/simple_test.go | 27 +++++++++++++++------------ pkg/metrics/prometheus.go | 11 +++++++++++ pkg/metrics/prometheus_test.go | 15 ++++++++++++--- pkg/server/router/router.go | 2 +- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/integration/simple_test.go b/integration/simple_test.go index 0972abf71..9bbce5ba5 100644 --- a/integration/simple_test.go +++ b/integration/simple_test.go @@ -333,21 +333,24 @@ func (s *SimpleSuite) TestMetricsPrometheusTwoRoutersOneService(c *check.C) { err = try.GetRequest("http://127.0.0.1:8000/whoami2", 1*time.Second, try.StatusCodeIs(http.StatusOK)) c.Assert(err, checker.IsNil) - request, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/metrics", nil) - c.Assert(err, checker.IsNil) + // adding a loop to test if metrics are not deleted + for i := 0; i < 10; i++ { + request, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/metrics", nil) + c.Assert(err, checker.IsNil) - response, err := http.DefaultClient.Do(request) - c.Assert(err, checker.IsNil) - c.Assert(response.StatusCode, checker.Equals, http.StatusOK) + response, err := http.DefaultClient.Do(request) + c.Assert(err, checker.IsNil) + c.Assert(response.StatusCode, checker.Equals, http.StatusOK) - body, err := io.ReadAll(response.Body) - c.Assert(err, checker.IsNil) + body, err := io.ReadAll(response.Body) + c.Assert(err, checker.IsNil) - // Reqs count of 1 for both routers - c.Assert(string(body), checker.Contains, "traefik_router_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",router=\"router1@docker\",service=\"whoami1-integrationtestbase\"} 1") - c.Assert(string(body), checker.Contains, "traefik_router_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",router=\"router2@docker\",service=\"whoami1-integrationtestbase\"} 1") - // Reqs count of 2 for service behind both routers - c.Assert(string(body), checker.Contains, "traefik_service_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"whoami1-integrationtestbase@docker\"} 2") + // Reqs count of 1 for both routers + c.Assert(string(body), checker.Contains, "traefik_router_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",router=\"router1@docker\",service=\"whoami1-integrationtestbase@docker\"} 1") + c.Assert(string(body), checker.Contains, "traefik_router_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",router=\"router2@docker\",service=\"whoami1-integrationtestbase@docker\"} 1") + // Reqs count of 2 for service behind both routers + c.Assert(string(body), checker.Contains, "traefik_service_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"whoami1-integrationtestbase@docker\"} 2") + } } func (s *SimpleSuite) TestMultipleProviderSameBackendName(c *check.C) { diff --git a/pkg/metrics/prometheus.go b/pkg/metrics/prometheus.go index f98752726..745a74c2f 100644 --- a/pkg/metrics/prometheus.go +++ b/pkg/metrics/prometheus.go @@ -380,6 +380,12 @@ func (ps *prometheusState) isOutdated(collector *collector) bool { return true } + if routerName, ok := labels["router"]; ok { + if !ps.dynamicConfig.hasRouter(routerName) { + return true + } + } + if serviceName, ok := labels["service"]; ok { if !ps.dynamicConfig.hasService(serviceName) { return true @@ -420,6 +426,11 @@ func (d *dynamicConfig) hasService(serviceName string) bool { return ok } +func (d *dynamicConfig) hasRouter(routerName string) bool { + _, ok := d.routers[routerName] + return ok +} + func (d *dynamicConfig) hasServerURL(serviceName, serverURL string) bool { if service, hasService := d.services[serviceName]; hasService { _, ok := service[serverURL] diff --git a/pkg/metrics/prometheus_test.go b/pkg/metrics/prometheus_test.go index 4a4ce235d..7e1154a03 100644 --- a/pkg/metrics/prometheus_test.go +++ b/pkg/metrics/prometheus_test.go @@ -364,7 +364,7 @@ func TestPrometheusMetricRemoval(t *testing.T) { // Reset state of global promState. defer promState.reset() - prometheusRegistry := RegisterPrometheus(context.Background(), &types.Prometheus{AddEntryPointsLabels: true, AddServicesLabels: true}) + prometheusRegistry := RegisterPrometheus(context.Background(), &types.Prometheus{AddEntryPointsLabels: true, AddServicesLabels: true, AddRoutersLabels: true}) defer promRegistry.Unregister(promState) conf := dynamic.Configuration{ @@ -401,11 +401,14 @@ func TestPrometheusMetricRemoval(t *testing.T) { ServiceServerUpGauge(). With("service", "service1", "url", "http://localhost:9999"). Set(1) - - delayForTrackingCompletion() + prometheusRegistry. + RouterReqsCounter(). + With("router", "router2", "service", "service2", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet, "protocol", "http"). + Add(1) assertMetricsExist(t, mustScrape(), entryPointReqsTotalName, serviceReqsTotalName, serviceServerUpName) assertMetricsAbsent(t, mustScrape(), entryPointReqsTotalName, serviceReqsTotalName, serviceServerUpName) + assertMetricsAbsent(t, mustScrape(), routerReqsTotalName, routerReqDurationName, routerOpenConnsName) // To verify that metrics belonging to active configurations are not removed // here the counter examples. @@ -413,11 +416,17 @@ func TestPrometheusMetricRemoval(t *testing.T) { EntryPointReqsCounter(). With("entrypoint", "entrypoint1", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet, "protocol", "http"). Add(1) + prometheusRegistry. + RouterReqsCounter(). + With("router", "foo@providerName", "service", "bar@providerName", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet, "protocol", "http"). + Add(1) delayForTrackingCompletion() assertMetricsExist(t, mustScrape(), entryPointReqsTotalName) assertMetricsExist(t, mustScrape(), entryPointReqsTotalName) + assertMetricsExist(t, mustScrape(), routerReqsTotalName) + assertMetricsExist(t, mustScrape(), routerReqsTotalName) } func TestPrometheusRemovedMetricsReset(t *testing.T) { diff --git a/pkg/server/router/router.go b/pkg/server/router/router.go index 285120989..7f9c40d0f 100644 --- a/pkg/server/router/router.go +++ b/pkg/server/router/router.go @@ -184,7 +184,7 @@ func (m *Manager) buildHTTPHandler(ctx context.Context, router *runtime.RouterIn chain := alice.New() if m.metricsRegistry != nil && m.metricsRegistry.IsRouterEnabled() { - chain = chain.Append(metricsMiddle.WrapRouterHandler(ctx, m.metricsRegistry, routerName, router.Service)) + chain = chain.Append(metricsMiddle.WrapRouterHandler(ctx, m.metricsRegistry, routerName, provider.GetQualifiedName(ctx, router.Service))) } return chain.Extend(*mHandler).Append(tHandler).Then(sHandler) From 6f4a7fb60485e54720ca5350811db2c79dcf188d Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Thu, 16 Sep 2021 09:16:07 +0200 Subject: [PATCH 04/12] chore: upgrade linter --- .github/workflows/validate.yaml | 2 +- .golangci.toml | 3 --- .semaphore/semaphore.yml | 2 +- build.Dockerfile | 2 +- cmd/internal/gen/centrifuge.go | 2 +- cmd/internal/gen/main.go | 2 +- integration/log_rotation_test.go | 1 + pkg/anonymize/anonymize_config_test.go | 4 ++-- pkg/provider/acme/local_store_unix.go | 1 + pkg/server/server_signals.go | 1 + 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index 4bf9abf12..da4009c91 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -7,7 +7,7 @@ on: env: GO_VERSION: 1.17 - GOLANGCI_LINT_VERSION: v1.41.1 + GOLANGCI_LINT_VERSION: v1.42.1 MISSSPELL_VERSION: v0.3.4 PRE_TARGET: "" diff --git a/.golangci.toml b/.golangci.toml index 4ce89734e..9caed7360 100644 --- a/.golangci.toml +++ b/.golangci.toml @@ -16,9 +16,6 @@ [linters-settings.gocyclo] min-complexity = 14.0 - [linters-settings.maligned] - suggest-new = true - [linters-settings.goconst] min-len = 3.0 min-occurrences = 4.0 diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 0210c1ffa..30d7cd1f5 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -25,7 +25,7 @@ global_job_config: - export "PATH=${GOPATH}/bin:${PATH}" - mkdir -vp "${SEMAPHORE_GIT_DIR}" "${GOPATH}/bin" - export GOPROXY=https://proxy.golang.org,direct - - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOPATH}/bin" v1.41.1 + - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOPATH}/bin" v1.42.1 - curl -sfL https://install.goreleaser.com/github.com/goreleaser/goreleaser.sh | bash -s -- -b "${GOPATH}/bin" - go install github.com/containous/go-bindata/go-bindata@v1.0.0 - checkout diff --git a/build.Dockerfile b/build.Dockerfile index cd9c84c98..30cc0056f 100644 --- a/build.Dockerfile +++ b/build.Dockerfile @@ -19,7 +19,7 @@ RUN mkdir -p /usr/local/bin \ && chmod +x /usr/local/bin/go-bindata # Download golangci-lint binary to bin folder in $GOPATH -RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.41.1 +RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.42.1 # Download misspell binary to bin folder in $GOPATH RUN curl -sfL https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | bash -s -- -b $GOPATH/bin v0.3.4 diff --git a/cmd/internal/gen/centrifuge.go b/cmd/internal/gen/centrifuge.go index ddef63429..bb58f9c19 100644 --- a/cmd/internal/gen/centrifuge.go +++ b/cmd/internal/gen/centrifuge.go @@ -258,7 +258,7 @@ type fileWriter struct { } func (f fileWriter) Write(files map[string]*File) error { - err := os.MkdirAll(f.baseDir, 0755) + err := os.MkdirAll(f.baseDir, 0o755) if err != nil { return err } diff --git a/cmd/internal/gen/main.go b/cmd/internal/gen/main.go index fb0b95773..add14c44b 100644 --- a/cmd/internal/gen/main.go +++ b/cmd/internal/gen/main.go @@ -83,7 +83,7 @@ func run(dest string) error { return err } - return ioutil.WriteFile(filepath.Join(dest, "marshaler.go"), []byte(fmt.Sprintf(marsh, destPkg)), 0666) + return ioutil.WriteFile(filepath.Join(dest, "marshaler.go"), []byte(fmt.Sprintf(marsh, destPkg)), 0o666) } func cleanType(typ types.Type, base string) string { diff --git a/integration/log_rotation_test.go b/integration/log_rotation_test.go index 2fe27d78b..cc0e027d4 100644 --- a/integration/log_rotation_test.go +++ b/integration/log_rotation_test.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package integration diff --git a/pkg/anonymize/anonymize_config_test.go b/pkg/anonymize/anonymize_config_test.go index 8a1133fca..941fef1b6 100644 --- a/pkg/anonymize/anonymize_config_test.go +++ b/pkg/anonymize/anonymize_config_test.go @@ -468,7 +468,7 @@ func TestDo_dynamicConfiguration(t *testing.T) { require.NoError(t, err) if *updateExpected { - require.NoError(t, os.WriteFile("testdata/anonymized-dynamic-config.json", []byte(cleanJSON), 0666)) + require.NoError(t, os.WriteFile("testdata/anonymized-dynamic-config.json", []byte(cleanJSON), 0o666)) } expected := strings.TrimSuffix(string(expectedConfiguration), "\n") @@ -975,7 +975,7 @@ func TestDo_staticConfiguration(t *testing.T) { require.NoError(t, err) if *updateExpected { - require.NoError(t, os.WriteFile("testdata/anonymized-static-config.json", []byte(cleanJSON), 0666)) + require.NoError(t, os.WriteFile("testdata/anonymized-static-config.json", []byte(cleanJSON), 0o666)) } expected := strings.TrimSuffix(string(expectedConfiguration), "\n") diff --git a/pkg/provider/acme/local_store_unix.go b/pkg/provider/acme/local_store_unix.go index 35c3d24bd..f6d590536 100644 --- a/pkg/provider/acme/local_store_unix.go +++ b/pkg/provider/acme/local_store_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package acme diff --git a/pkg/server/server_signals.go b/pkg/server/server_signals.go index 3b5adca59..d1ad8d51a 100644 --- a/pkg/server/server_signals.go +++ b/pkg/server/server_signals.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package server From 7d09132a5c00dc154d70f6657b27b775a6ebb904 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Thu, 16 Sep 2021 10:20:07 +0200 Subject: [PATCH 05/12] Update yaegi to v0.10.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b6e105ebd..71932780d 100644 --- a/go.mod +++ b/go.mod @@ -73,7 +73,7 @@ require ( github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 github.com/tinylib/msgp v1.0.2 // indirect github.com/traefik/paerser v0.1.4 - github.com/traefik/yaegi v0.9.23 + github.com/traefik/yaegi v0.10.0 github.com/uber/jaeger-client-go v2.29.1+incompatible github.com/uber/jaeger-lib v2.2.0+incompatible github.com/unrolled/render v1.0.2 diff --git a/go.sum b/go.sum index fa3d5ffbd..df04439de 100644 --- a/go.sum +++ b/go.sum @@ -1139,8 +1139,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/traefik/paerser v0.1.4 h1:/IXjV04Gf6di51H8Jl7jyS3OylsLjIasrwXIIwj1aT8= github.com/traefik/paerser v0.1.4/go.mod h1:FIdQ4Y92ulQUGSeZgxchtBKEcLw1o551PMNg9PoIq/4= -github.com/traefik/yaegi v0.9.23 h1:QM2DZCZZJBwAxiST2JhHnL1yze2XkeNZcnUPlB+2fCE= -github.com/traefik/yaegi v0.9.23/go.mod h1:FAYnRlZyuVlEkvnkHq3bvJ1lW5be6XuwgLdkYgYG6Lk= +github.com/traefik/yaegi v0.10.0 h1:c/0rhUcj5+KJhJX++eCrPeKXnJaOZ17X8gYCznU9Xxc= +github.com/traefik/yaegi v0.10.0/go.mod h1:RuCwD8/wsX7b6KoQHOaIFUfuH3gQIK4KWnFFmJMw5VA= github.com/transip/gotransip/v6 v6.2.0 h1:0Z+qVsyeiQdWfcAUeJyF0IEKAPvhJwwpwPi2WGtBIiE= github.com/transip/gotransip/v6 v6.2.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= From 8e7881094f1d8fe20a95855f74b6d5028e5b9f00 Mon Sep 17 00:00:00 2001 From: Aaron Raff Date: Thu, 16 Sep 2021 05:18:12 -0400 Subject: [PATCH 06/12] docs: add default proxy headers --- docs/content/getting-started/faq.md | 17 ++++++++++++++++- docs/content/middlewares/http/headers.md | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/content/getting-started/faq.md b/docs/content/getting-started/faq.md index 74ad989bf..4bb9b2fec 100644 --- a/docs/content/getting-started/faq.md +++ b/docs/content/getting-started/faq.md @@ -125,7 +125,7 @@ http: the principle of the above example above (a catchall router) still stands, but the `unavailable` service should be adapted to fit such a need. -## Why Is My TLS Certificate Not Reloaded When Its Contents Change ? +## Why Is My TLS Certificate Not Reloaded When Its Contents Change? With the file provider, a configuration update is only triggered when one of the [watched](../providers/file.md#provider-configuration) configuration files is modified. @@ -137,3 +137,18 @@ a configuration update is _not_ triggered. To take into account the new certificate contents, the update of the dynamic configuration must be forced. One way to achieve that, is to trigger a file notification, for example, by using the `touch` command on the configuration file. + +## What Are the Forwarded Headers When Proxying HTTP Requests? + +By default, the following headers are automatically added when proxying requests: + +| Property | HTTP Header | +|---------------------------|----------------------------| +| Client's IP | X-Forwarded-For, X-Real-Ip | +| Host | X-Forwarded-Host | +| Port | X-Forwarded-Port | +| Protocol | X-Forwarded-Proto | +| Proxy Server's Hostname | X-Forwarded-Server | + +For more details, +please check out the [forwarded header](../routing/entrypoints.md#forwarded-headers) documentation. diff --git a/docs/content/middlewares/http/headers.md b/docs/content/middlewares/http/headers.md index db7620617..a728ced1c 100644 --- a/docs/content/middlewares/http/headers.md +++ b/docs/content/middlewares/http/headers.md @@ -7,6 +7,8 @@ Managing Request/Response headers The Headers middleware manages the headers of requests and responses. +A set of forwarded headers are automatically added by default. See the [FAQ](../../getting-started/faq.md#what-are-the-forwarded-headers-when-proxying-http-requests) for more information. + ## Configuration Examples ### Adding Headers to the Request and the Response From 6f8e8ea2521a141856cebe5e16b5518d089e2139 Mon Sep 17 00:00:00 2001 From: Simon Stender Boisen Date: Thu, 16 Sep 2021 12:18:08 +0200 Subject: [PATCH 07/12] Ensure disableHTTP2 works with k8s crd --- docs/content/routing/providers/kubernetes-crd.md | 6 ++++-- .../kubernetes/crd/fixtures/with_servers_transport.yml | 1 + pkg/provider/kubernetes/crd/kubernetes.go | 1 + pkg/provider/kubernetes/crd/kubernetes_test.go | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index 4db55f7f2..4fbfe2d0d 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -1709,13 +1709,14 @@ or referencing TLS stores in the [`IngressRoute`](#kind-ingressroute) / [`Ingres responseHeaderTimeout: 42s # [8] idleConnTimeout: 42s # [9] peerCertURI: foobar # [10] + disableHTTP2: true # [11] ``` | Ref | Attribute | Purpose | |------|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| | [1] | `serverName` | ServerName used to contact the server. | -| [2] | `insecureSkipVerify` | Disable SSL certificate verification. | -| [3] | `rootCAsSecrets` | Add cert file for self-signed certificate. The secret must contain a certificate under either a tls.ca or a ca.crt key. | +| [2] | `insecureSkipVerify` | Disables SSL certificate verification. | +| [3] | `rootCAsSecrets` | Adds cert file for self-signed certificate. The secret must contain a certificate under either a tls.ca or a ca.crt key. | | [4] | `certificatesSecrets` | Certificates for mTLS. | | [5] | `maxIdleConnsPerHost` | If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, `defaultMaxIdleConnsPerHost` is used. | | [6] | `forwardingTimeouts` | Timeouts for requests forwarded to the backend servers. | @@ -1723,6 +1724,7 @@ or referencing TLS stores in the [`IngressRoute`](#kind-ingressroute) / [`Ingres | [8] | `responseHeaderTimeout` | The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists. | | [9] | `idleConnTimeout` | The maximum period for which an idle HTTP keep-alive connection will remain open before closing itself. | | [10] | `peerCertURI` | URI used to match with service certificate. | +| [11] | `disableHTTP2` | Disables HTTP/2 for connections with backend servers. | !!! info "CA Secret" diff --git a/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml b/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml index cddf9f5f5..25e4f6a25 100644 --- a/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml +++ b/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml @@ -93,6 +93,7 @@ spec: serverName: "test" insecureSkipVerify: true maxIdleConnsPerHost: 42 + disableHTTP2: true rootCAsSecrets: - root-ca0 - root-ca1 diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index 4dd1d208d..1f33d0993 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -344,6 +344,7 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client) InsecureSkipVerify: serversTransport.Spec.InsecureSkipVerify, RootCAs: rootCAs, Certificates: certs, + DisableHTTP2: serversTransport.Spec.DisableHTTP2, MaxIdleConnsPerHost: serversTransport.Spec.MaxIdleConnsPerHost, ForwardingTimeouts: forwardingTimeout, } diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index c02f9889d..d0f7d18c0 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -3505,6 +3505,7 @@ func TestLoadIngressRoutes(t *testing.T) { {CertFile: "TESTCERT3", KeyFile: "TESTKEY3"}, }, MaxIdleConnsPerHost: 42, + DisableHTTP2: true, ForwardingTimeouts: &dynamic.ForwardingTimeouts{ DialTimeout: types.Duration(42 * time.Second), ResponseHeaderTimeout: types.Duration(42 * time.Second), From 76867e39eac16bdf136050b3aa2ce0d3d7ba2ae8 Mon Sep 17 00:00:00 2001 From: Romain Date: Thu, 16 Sep 2021 15:12:13 +0200 Subject: [PATCH 08/12] Fix ServersTransport reference from IngressRoute service definition Co-authored-by: Jean-Baptiste Doumenjou <925513+jbdoumenjou@users.noreply.github.com> --- .../routing/providers/kubernetes-crd.md | 73 ++++--- integration/testdata/rawdata-crd.json | 2 +- .../crd/fixtures/with_cross_namespace.yml | 52 +++-- .../crd/fixtures/with_servers_transport.yml | 36 ++++ pkg/provider/kubernetes/crd/kubernetes.go | 3 +- .../kubernetes/crd/kubernetes_http.go | 25 ++- .../kubernetes/crd/kubernetes_test.go | 198 ++++++++++++++++-- 7 files changed, 323 insertions(+), 66 deletions(-) diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index 4fbfe2d0d..e9223d76a 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -337,7 +337,7 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne responseForwarding: flushInterval: 1ms scheme: https - serversTransport: transport + serversTransport: transport # [10] sticky: cookie: httpOnly: true @@ -346,39 +346,40 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne sameSite: none strategy: RoundRobin weight: 10 - tls: # [10] - secretName: supersecret # [11] - options: # [12] - name: opt # [13] - namespace: default # [14] - certResolver: foo # [15] - domains: # [16] - - main: example.net # [17] - sans: # [18] + tls: # [11] + secretName: supersecret # [12] + options: # [13] + name: opt # [14] + namespace: default # [15] + certResolver: foo # [16] + domains: # [17] + - main: example.net # [18] + sans: # [19] - a.example.net - b.example.net ``` -| Ref | Attribute | Purpose | -|------|------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [1] | `entryPoints` | List of [entry points](../routers/index.md#entrypoints) names | -| [2] | `routes` | List of routes | -| [3] | `routes[n].match` | Defines the [rule](../routers/index.md#rule) corresponding to an underlying router. | -| [4] | `routes[n].priority` | [Disambiguate](../routers/index.md#priority) rules of the same length, for route matching | -| [5] | `routes[n].middlewares` | List of reference to [Middleware](#kind-middleware) | -| [6] | `middlewares[n].name` | Defines the [Middleware](#kind-middleware) name | -| [7] | `middlewares[n].namespace` | Defines the [Middleware](#kind-middleware) namespace | -| [8] | `routes[n].services` | List of any combination of [TraefikService](#kind-traefikservice) and reference to a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) (See below for `ExternalName Service` setup) | -| [9] | `services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port. | -| [10] | `tls` | Defines [TLS](../routers/index.md#tls) certificate configuration | -| [11] | `tls.secretName` | Defines the [secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the `IngressRoute` namespace) | -| [12] | `tls.options` | Defines the reference to a [TLSOption](#kind-tlsoption) | -| [13] | `options.name` | Defines the [TLSOption](#kind-tlsoption) name | -| [14] | `options.namespace` | Defines the [TLSOption](#kind-tlsoption) namespace | -| [15] | `tls.certResolver` | Defines the reference to a [CertResolver](../routers/index.md#certresolver) | -| [16] | `tls.domains` | List of [domains](../routers/index.md#domains) | -| [17] | `domains[n].main` | Defines the main domain name | -| [18] | `domains[n].sans` | List of SANs (alternative domains) | +| Ref | Attribute | Purpose | +|------|--------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [1] | `entryPoints` | List of [entry points](../routers/index.md#entrypoints) names | +| [2] | `routes` | List of routes | +| [3] | `routes[n].match` | Defines the [rule](../routers/index.md#rule) corresponding to an underlying router. | +| [4] | `routes[n].priority` | [Disambiguate](../routers/index.md#priority) rules of the same length, for route matching | +| [5] | `routes[n].middlewares` | List of reference to [Middleware](#kind-middleware) | +| [6] | `middlewares[n].name` | Defines the [Middleware](#kind-middleware) name | +| [7] | `middlewares[n].namespace` | Defines the [Middleware](#kind-middleware) namespace | +| [8] | `routes[n].services` | List of any combination of [TraefikService](#kind-traefikservice) and reference to a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) (See below for `ExternalName Service` setup) | +| [9] | `services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port. | +| [10] | `services[n].serversTransport` | Defines the reference to a [ServersTransport](#kind-serverstransport). The ServersTransport namespace is assumed to be the [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) namespace (see [ServersTransport reference](#serverstransport-reference)). | +| [11] | `tls` | Defines [TLS](../routers/index.md#tls) certificate configuration | +| [12] | `tls.secretName` | Defines the [secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the `IngressRoute` namespace) | +| [13] | `tls.options` | Defines the reference to a [TLSOption](#kind-tlsoption) | +| [14] | `options.name` | Defines the [TLSOption](#kind-tlsoption) name | +| [15] | `options.namespace` | Defines the [TLSOption](#kind-tlsoption) namespace | +| [16] | `tls.certResolver` | Defines the reference to a [CertResolver](../routers/index.md#certresolver) | +| [17] | `tls.domains` | List of [domains](../routers/index.md#domains) | +| [18] | `domains[n].main` | Defines the main domain name | +| [19] | `domains[n].sans` | List of SANs (alternative domains) | ??? example "Declaring an IngressRoute" @@ -1687,7 +1688,7 @@ or referencing TLS stores in the [`IngressRoute`](#kind-ingressroute) / [`Ingres !!! info "ServersTransport Attributes" - ```yaml tab="TLSStore" + ```yaml tab="ServersTransport" apiVersion: traefik.containo.us/v1alpha1 kind: ServersTransport metadata: @@ -1763,6 +1764,16 @@ or referencing TLS stores in the [`IngressRoute`](#kind-ingressroute) / [`Ingres serversTransport: mytransport ``` +#### ServersTransport reference + +By default, the referenced ServersTransport CRD must be defined in the same [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) namespace. + +To reference a ServersTransport CRD from another namespace, +the value must be of form `namespace-name@kubernetescrd`, +and the [cross-namespace](../../../providers/kubernetes-crd/#allowcrossnamespace) option must be enabled. + +If the ServersTransport CRD is defined in another provider the cross-provider format `name@provider` should be used. + ## Further Also see the [full example](../../user-guides/crd-acme/index.md) with Let's Encrypt. diff --git a/integration/testdata/rawdata-crd.json b/integration/testdata/rawdata-crd.json index 8b7eaad6e..2b1139b0d 100644 --- a/integration/testdata/rawdata-crd.json +++ b/integration/testdata/rawdata-crd.json @@ -180,7 +180,7 @@ } ], "passHostHeader": true, - "serversTransport": "mytransport@kubernetescrd" + "serversTransport": "default-mytransport@kubernetescrd" }, "status": "enabled", "usedBy": [ diff --git a/pkg/provider/kubernetes/crd/fixtures/with_cross_namespace.yml b/pkg/provider/kubernetes/crd/fixtures/with_cross_namespace.yml index bdc98ca1e..5054eb819 100644 --- a/pkg/provider/kubernetes/crd/fixtures/with_cross_namespace.yml +++ b/pkg/provider/kubernetes/crd/fixtures/with_cross_namespace.yml @@ -9,23 +9,31 @@ spec: - 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 + - 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 + + - match: Host(`bar.com`) && PathPrefix(`/foo`) + kind: Rule + services: + - name: whoami-svc + namespace: cross-ns + port: 80 + serversTransport: test --- apiVersion: traefik.containo.us/v1alpha1 @@ -89,3 +97,13 @@ spec: namespace: cross-ns percent: 20 port: 80 + +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: ServersTransport +metadata: + name: test + namespace: foo + +spec: + serverName: "test" diff --git a/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml b/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml index 25e4f6a25..bc8dd786a 100644 --- a/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml +++ b/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml @@ -109,3 +109,39 @@ spec: dialTimeout: 42 responseHeaderTimeout: 42s idleConnTimeout: 42ms + +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: ServersTransport +metadata: + name: test + namespace: default + +spec: + serverName: "test" + +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: test.route + namespace: default + +spec: + entryPoints: + - foo + + routes: + - match: Host(`foo.com`) + kind: Rule + services: + - name: external-svc-with-https + port: 443 + serversTransport: test + - name: whoamitls + port: 443 + serversTransport: default-test + - name: whoami3 + port: 8443 + serversTransport: foo-test@kubernetescrd + diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index 1f33d0993..61d2de85d 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -339,7 +339,8 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client) } } - conf.HTTP.ServersTransports[serversTransport.Name] = &dynamic.ServersTransport{ + id := provider.Normalize(makeID(serversTransport.Namespace, serversTransport.Name)) + conf.HTTP.ServersTransports[id] = &dynamic.ServersTransport{ ServerName: serversTransport.Spec.ServerName, InsecureSkipVerify: serversTransport.Spec.InsecureSkipVerify, RootCAs: rootCAs, diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index ba32bba33..eee924546 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -297,11 +297,34 @@ func (c configBuilder) buildServersLB(namespace string, svc v1alpha1.LoadBalance lb.ResponseForwarding = conf.ResponseForwarding lb.Sticky = svc.Sticky - lb.ServersTransport = svc.ServersTransport + + lb.ServersTransport, err = c.makeServersTransportKey(namespace, svc.ServersTransport) + if err != nil { + return nil, err + } return &dynamic.Service{LoadBalancer: lb}, nil } +func (c *configBuilder) makeServersTransportKey(parentNamespace string, serversTransportName string) (string, error) { + if serversTransportName == "" { + return "", nil + } + + if !c.allowCrossNamespace && strings.HasSuffix(serversTransportName, 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 "", fmt.Errorf("invalid reference to serversTransport %s: namespace-name@kubernetescrd format is not allowed when crossnamespace is disallowed", serversTransportName) + } + + if strings.Contains(serversTransportName, providerNamespaceSeparator) { + return serversTransportName, nil + } + + return provider.Normalize(makeID(parentNamespace, serversTransportName)), nil +} + func (c configBuilder) loadServers(parentNamespace string, svc v1alpha1.LoadBalancerSpec) ([]dynamic.Server, error) { strategy := svc.Strategy if strategy == "" { diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index d0f7d18c0..829e37aec 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -1330,10 +1330,11 @@ func TestLoadIngressRouteTCPs(t *testing.T) { func TestLoadIngressRoutes(t *testing.T) { testCases := []struct { - desc string - ingressClass string - paths []string - expected *dynamic.Configuration + desc string + ingressClass string + paths []string + expected *dynamic.Configuration + AllowCrossNamespace bool }{ { desc: "Empty", @@ -1400,8 +1401,9 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, { - desc: "Simple Ingress Route with middleware", - paths: []string{"services.yml", "with_middleware.yml"}, + desc: "Simple Ingress Route with middleware", + AllowCrossNamespace: true, + paths: []string{"services.yml", "with_middleware.yml"}, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -1455,8 +1457,9 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, { - desc: "Simple Ingress Route with middleware crossprovider", - paths: []string{"services.yml", "with_middleware_crossprovider.yml"}, + desc: "Simple Ingress Route with middleware crossprovider", + AllowCrossNamespace: true, + paths: []string{"services.yml", "with_middleware_crossprovider.yml"}, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -2024,8 +2027,9 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, { - desc: "services lb, servers lb, and mirror service, all in a wrr with different namespaces", - paths: []string{"with_namespaces.yml"}, + desc: "services lb, servers lb, and mirror service, all in a wrr with different namespaces", + AllowCrossNamespace: true, + paths: []string{"with_namespaces.yml"}, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -3481,8 +3485,9 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, { - desc: "ServersTransport", - paths: []string{"services.yml", "with_servers_transport.yml"}, + desc: "ServersTransport", + AllowCrossNamespace: true, + paths: []string{"services.yml", "with_servers_transport.yml"}, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -3495,7 +3500,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ ServersTransports: map[string]*dynamic.ServersTransport{ - "test": { + "foo-test": { ServerName: "test", InsecureSkipVerify: true, RootCAs: []tls.FileOrContent{"TESTROOTCAS0", "TESTROOTCAS1", "TESTROOTCAS2", "TESTROOTCAS3", "TESTROOTCAS5", "TESTALLCERTS"}, @@ -3512,10 +3517,154 @@ func TestLoadIngressRoutes(t *testing.T) { IdleConnTimeout: types.Duration(42 * time.Millisecond), }, }, + "default-test": { + ServerName: "test", + ForwardingTimeouts: &dynamic.ForwardingTimeouts{ + DialTimeout: types.Duration(30 * time.Second), + IdleConnTimeout: types.Duration(90 * time.Second), + }, + }, + }, + Routers: map[string]*dynamic.Router{ + "default-test-route-6f97418635c7e18853da": { + EntryPoints: []string{"foo"}, + Service: "default-test-route-6f97418635c7e18853da", + Rule: "Host(`foo.com`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "default-external-svc-with-https-443": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "https://external.domain:443", + }, + }, + PassHostHeader: Bool(true), + ServersTransport: "default-test", + }, + }, + "default-whoami3-8443": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.7:8443", + }, + { + URL: "http://10.10.0.8:8443", + }, + }, + PassHostHeader: Bool(true), + ServersTransport: "foo-test@kubernetescrd", + }, + }, + "default-whoamitls-443": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "https://10.10.0.5:8443", + }, + { + URL: "https://10.10.0.6:8443", + }, + }, + PassHostHeader: Bool(true), + ServersTransport: "default-default-test", + }, + }, + "default-test-route-6f97418635c7e18853da": { + Weighted: &dynamic.WeightedRoundRobin{ + Services: []dynamic.WRRService{ + { + Name: "default-external-svc-with-https-443", + Weight: Int(1), + }, + { + Name: "default-whoamitls-443", + Weight: Int(1), + }, + { + Name: "default-whoami3-8443", + Weight: Int(1), + }, + }, + }, + }, + }, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "ServersTransport without crossnamespace", + paths: []string{"services.yml", "with_servers_transport.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{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + ServersTransports: map[string]*dynamic.ServersTransport{ + "foo-test": { + ServerName: "test", + InsecureSkipVerify: true, + RootCAs: []tls.FileOrContent{"TESTROOTCAS0", "TESTROOTCAS1", "TESTROOTCAS2", "TESTROOTCAS3", "TESTROOTCAS5", "TESTALLCERTS"}, + Certificates: tls.Certificates{ + {CertFile: "TESTCERT1", KeyFile: "TESTKEY1"}, + {CertFile: "TESTCERT2", KeyFile: "TESTKEY2"}, + {CertFile: "TESTCERT3", KeyFile: "TESTKEY3"}, + }, + MaxIdleConnsPerHost: 42, + ForwardingTimeouts: &dynamic.ForwardingTimeouts{ + DialTimeout: types.Duration(42 * time.Second), + ResponseHeaderTimeout: types.Duration(42 * time.Second), + IdleConnTimeout: types.Duration(42 * time.Millisecond), + }, + DisableHTTP2: true, + }, + "default-test": { + ServerName: "test", + ForwardingTimeouts: &dynamic.ForwardingTimeouts{ + DialTimeout: types.Duration(30 * time.Second), + IdleConnTimeout: types.Duration(90 * time.Second), + }, + }, }, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Services: map[string]*dynamic.Service{ + "default-external-svc-with-https-443": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "https://external.domain:443", + }, + }, + PassHostHeader: Bool(true), + ServersTransport: "default-test", + }, + }, + "default-whoamitls-443": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "https://10.10.0.5:8443", + }, + { + URL: "https://10.10.0.6:8443", + }, + }, + PassHostHeader: Bool(true), + ServersTransport: "default-default-test", + }, + }, + }, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -3531,7 +3680,7 @@ func TestLoadIngressRoutes(t *testing.T) { return } - p := Provider{IngressClass: test.ingressClass, AllowCrossNamespace: true, AllowExternalNameServices: true} + p := Provider{IngressClass: test.ingressClass, AllowCrossNamespace: test.AllowCrossNamespace, AllowExternalNameServices: true} clientMock := newClientMock(test.paths...) conf := p.loadConfigurationFromCRD(context.Background(), clientMock) @@ -4473,6 +4622,11 @@ func TestCrossNamespace(t *testing.T) { Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", Priority: 12, }, + "default-cross-ns-route-1bc3efa892379bb93c6e": { + EntryPoints: []string{"foo"}, + Service: "default-cross-ns-route-1bc3efa892379bb93c6e", + Rule: "Host(`bar.com`) && PathPrefix(`/foo`)", + }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ @@ -4502,6 +4656,20 @@ func TestCrossNamespace(t *testing.T) { }, }, }, + "default-cross-ns-route-1bc3efa892379bb93c6e": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.1:80", + }, + { + URL: "http://10.10.0.2:80", + }, + }, + PassHostHeader: Bool(true), + ServersTransport: "cross-ns-test", + }, + }, "cross-ns-whoami-svc-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ From bda0dba1310451b54d4cc288fc336f5bca8d5643 Mon Sep 17 00:00:00 2001 From: Kevin Pollet Date: Fri, 17 Sep 2021 08:56:07 +0200 Subject: [PATCH 09/12] fix: add peerCertURI config to k8s crd provider Co-authored-by: Jean-Baptiste Doumenjou <925513+jbdoumenjou@users.noreply.github.com> --- .../routing/providers/kubernetes-crd.md | 26 +++++++++---------- docs/content/routing/services/index.md | 14 +++++----- .../crd/fixtures/with_servers_transport.yml | 19 +++++++------- pkg/provider/kubernetes/crd/kubernetes.go | 1 + .../kubernetes/crd/kubernetes_test.go | 2 ++ 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index e9223d76a..7b17d3b3f 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -1713,19 +1713,19 @@ or referencing TLS stores in the [`IngressRoute`](#kind-ingressroute) / [`Ingres disableHTTP2: true # [11] ``` -| Ref | Attribute | Purpose | -|------|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| -| [1] | `serverName` | ServerName used to contact the server. | -| [2] | `insecureSkipVerify` | Disables SSL certificate verification. | -| [3] | `rootCAsSecrets` | Adds cert file for self-signed certificate. The secret must contain a certificate under either a tls.ca or a ca.crt key. | -| [4] | `certificatesSecrets` | Certificates for mTLS. | -| [5] | `maxIdleConnsPerHost` | If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, `defaultMaxIdleConnsPerHost` is used. | -| [6] | `forwardingTimeouts` | Timeouts for requests forwarded to the backend servers. | -| [7] | `dialTimeout` | The amount of time to wait until a connection to a backend server can be established. If zero, no timeout exists. | -| [8] | `responseHeaderTimeout` | The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists. | -| [9] | `idleConnTimeout` | The maximum period for which an idle HTTP keep-alive connection will remain open before closing itself. | -| [10] | `peerCertURI` | URI used to match with service certificate. | -| [11] | `disableHTTP2` | Disables HTTP/2 for connections with backend servers. | +| Ref | Attribute | Purpose | +|------|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [1] | `serverName` | ServerName used to contact the server. | +| [2] | `insecureSkipVerify` | Controls whether the server's certificate chain and host name is verified. | +| [3] | `rootCAsSecrets` | Defines the set of root certificate authorities to use when verifying server certificates. The secret must contain a certificate under either a tls.ca or a ca.crt key. | +| [4] | `certificatesSecrets` | Certificates to present to the server for mTLS. | +| [5] | `maxIdleConnsPerHost` | Controls the maximum idle (keep-alive) connections to keep per-host. If zero, `defaultMaxIdleConnsPerHost` is used. | +| [6] | `forwardingTimeouts` | Timeouts for requests forwarded to the servers. | +| [7] | `dialTimeout` | The amount of time to wait until a connection to a server can be established. If zero, no timeout exists. | +| [8] | `responseHeaderTimeout` | The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists. | +| [9] | `idleConnTimeout` | The maximum amount of time an idle (keep-alive) connection will remain idle before closing itself. If zero, no timeout exists. | +| [10] | `peerCertURI` | URI used to match against SAN URIs during the server's certificate verification. | +| [11] | `disableHTTP2` | Disables HTTP/2 for connections with servers. | !!! info "CA Secret" diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index ef3b0978b..d6a639796 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -606,7 +606,7 @@ metadata: _Optional_ -`insecureSkipVerify` disables SSL certificate verification. +`insecureSkipVerify` controls whether the server's certificate chain and host name is verified. ```yaml tab="File (YAML)" ## Dynamic configuration @@ -637,8 +637,7 @@ spec: _Optional_ -`rootCAs` is the list of certificates (as file paths, or data bytes) -that will be set as Root Certificate Authorities when using a self-signed TLS certificate. +`rootCAs` defines the set of root certificate authorities (as file paths, or data bytes) to use when verifying server certificates. ```yaml tab="File (YAML)" ## Dynamic configuration @@ -711,7 +710,7 @@ spec: _Optional, Default=false_ -`disableHTTP2` disables HTTP/2 for connections with backend servers. +`disableHTTP2` disables HTTP/2 for connections with servers. ```toml tab="File (TOML)" ## Dynamic configuration @@ -742,7 +741,7 @@ spec: _Optional, Default=false_ -`peerCertURI` defines the URI used to match against SAN URI during the peer certificate verification. +`peerCertURI` defines the URI used to match against SAN URIs during the server's certificate verification. ```toml tab="File (TOML)" ## Dynamic configuration @@ -771,7 +770,7 @@ spec: #### `forwardingTimeouts` -`forwardingTimeouts` is about a number of timeouts relevant to when forwarding requests to the backend servers. +`forwardingTimeouts` are the timeouts applied when forwarding requests to the servers. ##### `forwardingTimeouts.dialTimeout` @@ -847,8 +846,7 @@ spec: _Optional, Default=90s_ -`idleConnTimeout`, is the maximum amount of time an idle (keep-alive) connection -will remain idle before closing itself. +`idleConnTimeout` is the maximum amount of time an idle (keep-alive) connection will remain idle before closing itself. Zero means no limit. ```yaml tab="File (YAML)" diff --git a/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml b/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml index bc8dd786a..c7c58219d 100644 --- a/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml +++ b/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml @@ -94,17 +94,18 @@ spec: insecureSkipVerify: true maxIdleConnsPerHost: 42 disableHTTP2: true + peerCertURI: foo://bar rootCAsSecrets: - - root-ca0 - - root-ca1 - - root-ca2 - - root-ca3 - - root-ca4 - - allcerts + - root-ca0 + - root-ca1 + - root-ca2 + - root-ca3 + - root-ca4 + - allcerts certificatesSecrets: - - mtls1 - - mtls2 - - allcerts + - mtls1 + - mtls2 + - allcerts forwardingTimeouts: dialTimeout: 42 responseHeaderTimeout: 42s diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index 61d2de85d..5671e586b 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -348,6 +348,7 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client) DisableHTTP2: serversTransport.Spec.DisableHTTP2, MaxIdleConnsPerHost: serversTransport.Spec.MaxIdleConnsPerHost, ForwardingTimeouts: forwardingTimeout, + PeerCertURI: serversTransport.Spec.PeerCertURI, } } diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index 829e37aec..3a4678d35 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -3516,6 +3516,7 @@ func TestLoadIngressRoutes(t *testing.T) { ResponseHeaderTimeout: types.Duration(42 * time.Second), IdleConnTimeout: types.Duration(42 * time.Millisecond), }, + PeerCertURI: "foo://bar", }, "default-test": { ServerName: "test", @@ -3627,6 +3628,7 @@ func TestLoadIngressRoutes(t *testing.T) { IdleConnTimeout: types.Duration(42 * time.Millisecond), }, DisableHTTP2: true, + PeerCertURI: "foo://bar", }, "default-test": { ServerName: "test", From 6906a022ca49bf5c52b3cba7bb83536e15e9afff Mon Sep 17 00:00:00 2001 From: Tom Moulard Date: Mon, 20 Sep 2021 12:54:05 +0200 Subject: [PATCH 10/12] Add cross namespace verification in Kubernetes CRD --- .../tcp/with_tls_options_cross_namespace.yml | 30 ++ .../crd/fixtures/with_cross_namespace.yml | 2 +- .../crd/fixtures/with_servers_transport.yml | 3 - ...with_servers_transport_cross_namespace.yml | 29 ++ .../with_tls_options_cross_namespace.yml | 31 ++ .../kubernetes/crd/kubernetes_http.go | 19 +- pkg/provider/kubernetes/crd/kubernetes_tcp.go | 44 +- .../kubernetes/crd/kubernetes_test.go | 389 +++++++++++++----- 8 files changed, 418 insertions(+), 129 deletions(-) create mode 100644 pkg/provider/kubernetes/crd/fixtures/tcp/with_tls_options_cross_namespace.yml create mode 100644 pkg/provider/kubernetes/crd/fixtures/with_servers_transport_cross_namespace.yml create mode 100644 pkg/provider/kubernetes/crd/fixtures/with_tls_options_cross_namespace.yml diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_tls_options_cross_namespace.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_tls_options_cross_namespace.yml new file mode 100644 index 000000000..13958f95d --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/tcp/with_tls_options_cross_namespace.yml @@ -0,0 +1,30 @@ +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRouteTCP +metadata: + name: test.route + namespace: default + +spec: + entryPoints: + - foo + + routes: + - match: HostSNI(`foo.com`) + services: + - name: whoamitcp + port: 8000 + + tls: + options: + name: tls-options-cn + namespace: cross-ns + +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: TLSOption +metadata: + name: tls-options-cn + namespace: cross-ns + +spec: + minVersion: VersionTLS12 diff --git a/pkg/provider/kubernetes/crd/fixtures/with_cross_namespace.yml b/pkg/provider/kubernetes/crd/fixtures/with_cross_namespace.yml index 5054eb819..543c5696c 100644 --- a/pkg/provider/kubernetes/crd/fixtures/with_cross_namespace.yml +++ b/pkg/provider/kubernetes/crd/fixtures/with_cross_namespace.yml @@ -33,7 +33,7 @@ spec: - name: whoami-svc namespace: cross-ns port: 80 - serversTransport: test + serversTransport: foo-test@kubernetescrd --- apiVersion: traefik.containo.us/v1alpha1 diff --git a/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml b/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml index c7c58219d..994941f06 100644 --- a/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml +++ b/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml @@ -142,7 +142,4 @@ spec: - name: whoamitls port: 443 serversTransport: default-test - - name: whoami3 - port: 8443 - serversTransport: foo-test@kubernetescrd diff --git a/pkg/provider/kubernetes/crd/fixtures/with_servers_transport_cross_namespace.yml b/pkg/provider/kubernetes/crd/fixtures/with_servers_transport_cross_namespace.yml new file mode 100644 index 000000000..7b9ab8891 --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/with_servers_transport_cross_namespace.yml @@ -0,0 +1,29 @@ +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: test.route + namespace: default + +spec: + entryPoints: + - foo + + routes: + - match: Host(`foo.com`) && PathPrefix(`/bar`) + kind: Rule + priority: 12 + services: + - name: whoami + port: 80 + serversTransport: cross-ns-st-cross-ns@kubernetescrd + +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: ServersTransport +metadata: + name: st-cross-ns + namespace: cross-ns + +spec: + disableHTTP2: true + diff --git a/pkg/provider/kubernetes/crd/fixtures/with_tls_options_cross_namespace.yml b/pkg/provider/kubernetes/crd/fixtures/with_tls_options_cross_namespace.yml new file mode 100644 index 000000000..57228278b --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/with_tls_options_cross_namespace.yml @@ -0,0 +1,31 @@ +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: test.route + namespace: default + +spec: + entryPoints: + - foo + + routes: + - match: Host(`foo.com`) && PathPrefix(`/bar`) + kind: Rule + priority: 12 + services: + - name: whoami + port: 80 + tls: + options: + name: tls-options-cn + namespace: cross-ns + +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: TLSOption +metadata: + name: tls-options-cn + namespace: cross-ns + +spec: + minVersion: VersionTLS12 diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index eee924546..b78d14c31 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -104,7 +104,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli } } - conf.Routers[normalized] = &dynamic.Router{ + r := &dynamic.Router{ Middlewares: mds, Priority: route.Priority, EntryPoints: ingressRoute.Spec.EntryPoints, @@ -113,7 +113,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli } if ingressRoute.Spec.TLS != nil { - tlsConf := &dynamic.RouterTLSConfig{ + r.TLS = &dynamic.RouterTLSConfig{ CertResolver: ingressRoute.Spec.TLS.CertResolver, Domains: ingressRoute.Spec.TLS.Domains, } @@ -129,14 +129,21 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli tlsOptionsName = makeID(ns, tlsOptionsName) } else if len(ns) > 0 { logger. - WithField("TLSoptions", ingressRoute.Spec.TLS.Options.Name). - Warnf("namespace %q is ignored in cross-provider context", ns) + WithField("TLSOption", ingressRoute.Spec.TLS.Options.Name). + Warnf("Namespace %q is ignored in cross-provider context", ns) } - tlsConf.Options = tlsOptionsName + if !isNamespaceAllowed(p.AllowCrossNamespace, ingressRoute.Namespace, ns) { + logger.Errorf("TLSOption %s/%s is not in the IngressRoute namespace %s", + ns, ingressRoute.Spec.TLS.Options.Name, ingressRoute.Namespace) + continue + } + + r.TLS.Options = tlsOptionsName } - conf.Routers[normalized].TLS = tlsConf } + + conf.Routers[normalized] = r } } diff --git a/pkg/provider/kubernetes/crd/kubernetes_tcp.go b/pkg/provider/kubernetes/crd/kubernetes_tcp.go index 181baa7d5..187020a2d 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_tcp.go +++ b/pkg/provider/kubernetes/crd/kubernetes_tcp.go @@ -93,7 +93,7 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client conf.Services[serviceName].Weighted.Services = append(conf.Services[serviceName].Weighted.Services, srv) } - conf.Routers[serviceName] = &dynamic.TCPRouter{ + r := &dynamic.TCPRouter{ EntryPoints: ingressRouteTCP.Spec.EntryPoints, Middlewares: mds, Rule: route.Match, @@ -101,32 +101,38 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client } if ingressRouteTCP.Spec.TLS != nil { - conf.Routers[serviceName].TLS = &dynamic.RouterTCPTLSConfig{ + r.TLS = &dynamic.RouterTCPTLSConfig{ Passthrough: ingressRouteTCP.Spec.TLS.Passthrough, CertResolver: ingressRouteTCP.Spec.TLS.CertResolver, Domains: ingressRouteTCP.Spec.TLS.Domains, } - if ingressRouteTCP.Spec.TLS.Options == nil || len(ingressRouteTCP.Spec.TLS.Options.Name) == 0 { - continue - } - - tlsOptionsName := ingressRouteTCP.Spec.TLS.Options.Name - // Is a Kubernetes CRD reference (i.e. not a cross-provider reference) - ns := ingressRouteTCP.Spec.TLS.Options.Namespace - if !strings.Contains(tlsOptionsName, "@") { - if len(ns) == 0 { - ns = ingressRouteTCP.Namespace + if ingressRouteTCP.Spec.TLS.Options != nil && len(ingressRouteTCP.Spec.TLS.Options.Name) > 0 { + tlsOptionsName := ingressRouteTCP.Spec.TLS.Options.Name + // Is a Kubernetes CRD reference (i.e. not a cross-provider reference) + ns := ingressRouteTCP.Spec.TLS.Options.Namespace + if !strings.Contains(tlsOptionsName, providerNamespaceSeparator) { + if len(ns) == 0 { + ns = ingressRouteTCP.Namespace + } + tlsOptionsName = makeID(ns, tlsOptionsName) + } else if len(ns) > 0 { + logger. + WithField("TLSOption", ingressRouteTCP.Spec.TLS.Options.Name). + Warnf("Namespace %q is ignored in cross-provider context", ns) } - tlsOptionsName = makeID(ns, tlsOptionsName) - } else if len(ns) > 0 { - logger. - WithField("TLSoptions", ingressRouteTCP.Spec.TLS.Options.Name). - Warnf("namespace %q is ignored in cross-provider context", ns) - } - conf.Routers[serviceName].TLS.Options = tlsOptionsName + if !isNamespaceAllowed(p.AllowCrossNamespace, ingressRouteTCP.Namespace, ns) { + logger.Errorf("TLSOption %s/%s is not in the IngressRouteTCP namespace %s", + ns, ingressRouteTCP.Spec.TLS.Options.Name, ingressRouteTCP.Namespace) + continue + } + + r.TLS.Options = tlsOptionsName + } } + + conf.Routers[serviceName] = r } } diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index 3a4678d35..dafa56653 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -2732,8 +2732,9 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, { - desc: "TLS with tls options and specific namespace", - paths: []string{"services.yml", "with_tls_options_and_specific_namespace.yml"}, + desc: "TLS with tls options and specific namespace", + paths: []string{"services.yml", "with_tls_options_and_specific_namespace.yml"}, + AllowCrossNamespace: true, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -2926,8 +2927,9 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, { - desc: "TLS with unknown tls options namespace", - paths: []string{"services.yml", "with_unknown_tls_options_namespace.yml"}, + desc: "TLS with unknown tls options namespace", + paths: []string{"services.yml", "with_unknown_tls_options_namespace.yml"}, + AllowCrossNamespace: true, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -3485,9 +3487,8 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, { - desc: "ServersTransport", - AllowCrossNamespace: true, - paths: []string{"services.yml", "with_servers_transport.yml"}, + desc: "ServersTransport", + paths: []string{"services.yml", "with_servers_transport.yml"}, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -3546,20 +3547,6 @@ func TestLoadIngressRoutes(t *testing.T) { ServersTransport: "default-test", }, }, - "default-whoami3-8443": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.7:8443", - }, - { - URL: "http://10.10.0.8:8443", - }, - }, - PassHostHeader: Bool(true), - ServersTransport: "foo-test@kubernetescrd", - }, - }, "default-whoamitls-443": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ @@ -3585,10 +3572,6 @@ func TestLoadIngressRoutes(t *testing.T) { Name: "default-whoamitls-443", Weight: Int(1), }, - { - Name: "default-whoami3-8443", - Weight: Int(1), - }, }, }, }, @@ -3597,80 +3580,6 @@ func TestLoadIngressRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, - { - desc: "ServersTransport without crossnamespace", - paths: []string{"services.yml", "with_servers_transport.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{}, - Middlewares: map[string]*dynamic.TCPMiddleware{}, - Services: map[string]*dynamic.TCPService{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{ - "foo-test": { - ServerName: "test", - InsecureSkipVerify: true, - RootCAs: []tls.FileOrContent{"TESTROOTCAS0", "TESTROOTCAS1", "TESTROOTCAS2", "TESTROOTCAS3", "TESTROOTCAS5", "TESTALLCERTS"}, - Certificates: tls.Certificates{ - {CertFile: "TESTCERT1", KeyFile: "TESTKEY1"}, - {CertFile: "TESTCERT2", KeyFile: "TESTKEY2"}, - {CertFile: "TESTCERT3", KeyFile: "TESTKEY3"}, - }, - MaxIdleConnsPerHost: 42, - ForwardingTimeouts: &dynamic.ForwardingTimeouts{ - DialTimeout: types.Duration(42 * time.Second), - ResponseHeaderTimeout: types.Duration(42 * time.Second), - IdleConnTimeout: types.Duration(42 * time.Millisecond), - }, - DisableHTTP2: true, - PeerCertURI: "foo://bar", - }, - "default-test": { - ServerName: "test", - ForwardingTimeouts: &dynamic.ForwardingTimeouts{ - DialTimeout: types.Duration(30 * time.Second), - IdleConnTimeout: types.Duration(90 * time.Second), - }, - }, - }, - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{ - "default-external-svc-with-https-443": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{ - { - URL: "https://external.domain:443", - }, - }, - PassHostHeader: Bool(true), - ServersTransport: "default-test", - }, - }, - "default-whoamitls-443": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{ - { - URL: "https://10.10.0.5:8443", - }, - { - URL: "https://10.10.0.6:8443", - }, - }, - PassHostHeader: Bool(true), - ServersTransport: "default-default-test", - }, - }, - }, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, } for _, test := range testCases { @@ -4669,7 +4578,7 @@ func TestCrossNamespace(t *testing.T) { }, }, PassHostHeader: Bool(true), - ServersTransport: "cross-ns-test", + ServersTransport: "foo-test@kubernetescrd", }, }, "cross-ns-whoami-svc-80": { @@ -4816,6 +4725,189 @@ func TestCrossNamespace(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, + { + desc: "HTTP ServersTransport cross namespace allowed", + paths: []string{"services.yml", "with_servers_transport_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{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "default-test-route-6b204d94623b3df4370c": { + EntryPoints: []string{"foo"}, + Service: "default-test-route-6b204d94623b3df4370c", + Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", + Priority: 12, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "default-test-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), + ServersTransport: "cross-ns-st-cross-ns@kubernetescrd", + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{ + "cross-ns-st-cross-ns": { + ForwardingTimeouts: &dynamic.ForwardingTimeouts{ + DialTimeout: 30000000000, + ResponseHeaderTimeout: 0, + IdleConnTimeout: 90000000000, + }, + DisableHTTP2: true, + }, + }, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "HTTP ServersTransport cross namespace disallowed", + paths: []string{"services.yml", "with_servers_transport_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{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{ + "cross-ns-st-cross-ns": { + ForwardingTimeouts: &dynamic.ForwardingTimeouts{ + DialTimeout: 30000000000, + ResponseHeaderTimeout: 0, + IdleConnTimeout: 90000000000, + }, + DisableHTTP2: true, + }, + }, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "HTTP TLSOption cross namespace allowed", + paths: []string{"services.yml", "with_tls_options_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{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "default-test-route-6b204d94623b3df4370c": { + EntryPoints: []string{"foo"}, + Service: "default-test-route-6b204d94623b3df4370c", + Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", + Priority: 12, + TLS: &dynamic.RouterTLSConfig{ + Options: "cross-ns-tls-options-cn", + }, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "default-test-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), + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{ + Options: map[string]tls.Options{ + "cross-ns-tls-options-cn": { + MinVersion: "VersionTLS12", + ALPNProtocols: []string{"h2", "http/1.1", "acme-tls/1"}, + }, + }, + }, + }, + }, + { + desc: "HTTP TLSOption cross namespace disallowed", + paths: []string{"services.yml", "with_tls_options_cross_namespace.yml"}, + allowCrossNamespace: false, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "default-test-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), + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{ + Options: map[string]tls.Options{ + "cross-ns-tls-options-cn": { + MinVersion: "VersionTLS12", + ALPNProtocols: []string{"h2", "http/1.1", "acme-tls/1"}, + }, + }, + }, + }, + }, { desc: "TCP middleware cross namespace disallowed", paths: []string{"tcp/services.yml", "tcp/with_middleware_with_cross_namespace.yml"}, @@ -5012,6 +5104,101 @@ func TestCrossNamespace(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, + { + desc: "TCP TLSOption cross namespace allowed", + paths: []string{"tcp/services.yml", "tcp/with_tls_options_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{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{ + "default-test.route-fdd3e9338e47a45efefc": { + EntryPoints: []string{"foo"}, + Service: "default-test.route-fdd3e9338e47a45efefc", + Rule: "HostSNI(`foo.com`)", + TLS: &dynamic.RouterTCPTLSConfig{ + Options: "cross-ns-tls-options-cn", + }, + }, + }, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{ + "default-test.route-fdd3e9338e47a45efefc": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "10.10.0.1:8000", + }, + { + Address: "10.10.0.2:8000", + }, + }, + }, + }, + }, + }, + TLS: &dynamic.TLSConfiguration{ + Options: map[string]tls.Options{ + "cross-ns-tls-options-cn": { + MinVersion: "VersionTLS12", + ALPNProtocols: []string{"h2", "http/1.1", "acme-tls/1"}, + }, + }, + }, + }, + }, + { + desc: "TCP TLSOption cross namespace disallowed", + paths: []string{"tcp/services.yml", "tcp/with_tls_options_cross_namespace.yml"}, + allowCrossNamespace: false, + 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{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{ + "default-test.route-fdd3e9338e47a45efefc": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "10.10.0.1:8000", + }, + { + Address: "10.10.0.2:8000", + }, + }, + }, + }, + }, + }, + TLS: &dynamic.TLSConfiguration{ + Options: map[string]tls.Options{ + "cross-ns-tls-options-cn": { + MinVersion: "VersionTLS12", + ALPNProtocols: []string{"h2", "http/1.1", "acme-tls/1"}, + }, + }, + }, + }, + }, { desc: "UDP cross namespace allowed", paths: []string{"udp/services.yml", "udp/with_cross_namespace.yml"}, @@ -5118,6 +5305,8 @@ func TestCrossNamespace(t *testing.T) { crdObjects = append(crdObjects, o) case *v1alpha1.TLSStore: crdObjects = append(crdObjects, o) + case *v1alpha1.ServersTransport: + crdObjects = append(crdObjects, o) default: } } From c7e13eb082122ed0b87f9ffd0388e37a55ce5e92 Mon Sep 17 00:00:00 2001 From: Romain Date: Mon, 20 Sep 2021 17:30:06 +0200 Subject: [PATCH 11/12] Prepare release v2.5.3 --- CHANGELOG.md | 18 ++++++++++++++++++ script/gcg/traefik-bugfix.toml | 6 +++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4387ad381..e5a696986 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## [v2.5.3](https://github.com/traefik/traefik/tree/v2.5.3) (2021-09-20) +[All Commits](https://github.com/traefik/traefik/compare/v2.5.2...v2.5.3) + +**Bug fixes:** +- **[consulcatalog]** Fix certChan defaulting on consul catalog provider ([#8439](https://github.com/traefik/traefik/pull/8439) by [tomMoulard](https://github.com/tomMoulard)) +- **[k8s/crd,k8s]** Fix peerCertURI config for k8s crd provider ([#8454](https://github.com/traefik/traefik/pull/8454) by [kevinpollet](https://github.com/kevinpollet)) +- **[k8s/crd,k8s]** Ensure disableHTTP2 works with k8s crd ([#8448](https://github.com/traefik/traefik/pull/8448) by [ssboisen](https://github.com/ssboisen)) +- **[k8s/crd,k8s]** Fix ServersTransport reference from IngressRoute service definition ([#8431](https://github.com/traefik/traefik/pull/8431) by [rtribotte](https://github.com/rtribotte)) +- **[k8s/crd,k8s]** Add cross namespace verification in Kubernetes CRD ([#8422](https://github.com/traefik/traefik/pull/8422) by [tomMoulard](https://github.com/tomMoulard)) +- **[metrics]** Fix Prometheus router's metrics ([#8425](https://github.com/traefik/traefik/pull/8425) by [tomMoulard](https://github.com/tomMoulard)) +- **[plugins]** Update yaegi to v0.10.0 ([#8452](https://github.com/traefik/traefik/pull/8452) by [ldez](https://github.com/ldez)) + +**Documentation:** +- **[middleware,file]** Fix TCP middleware whitelist example ([#8421](https://github.com/traefik/traefik/pull/8421) by [tribal2](https://github.com/tribal2)) +- **[middleware]** Add default proxy headers list ([#8418](https://github.com/traefik/traefik/pull/8418) by [aaronraff](https://github.com/aaronraff)) +- Add Tom Moulard in maintainers team ([#8442](https://github.com/traefik/traefik/pull/8442) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- Fix golang doc URLs ([#8434](https://github.com/traefik/traefik/pull/8434) by [jbdoumenjou](https://github.com/jbdoumenjou)) + ## [v2.5.2](https://github.com/traefik/traefik/tree/v2.5.2) (2021-09-02) [All Commits](https://github.com/traefik/traefik/compare/v2.5.1...v2.5.2) diff --git a/script/gcg/traefik-bugfix.toml b/script/gcg/traefik-bugfix.toml index e7f1ac14f..d9678b286 100644 --- a/script/gcg/traefik-bugfix.toml +++ b/script/gcg/traefik-bugfix.toml @@ -4,11 +4,11 @@ RepositoryName = "traefik" OutputType = "file" FileName = "traefik_changelog.md" -# example new bugfix v2.5.2 +# example new bugfix v2.5.3 CurrentRef = "v2.5" -PreviousRef = "v2.5.1" +PreviousRef = "v2.5.2" BaseBranch = "v2.5" -FutureCurrentRefName = "v2.5.2" +FutureCurrentRefName = "v2.5.3" ThresholdPreviousRef = 10 ThresholdCurrentRef = 10 From 61ceb7a32c8c72d4d4fc60ec4df777ca90a70c85 Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Tue, 21 Sep 2021 16:28:11 +0200 Subject: [PATCH 12/12] docs: replace links to French translation of k8s docs with English ones --- docs/content/routing/providers/kubernetes-crd.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index 7b17d3b3f..56b93dfc4 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -477,7 +477,7 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne !!! important "Using Kubernetes ExternalName Service" - Traefik backends creation needs a port to be set, however Kubernetes [ExternalName Service](https://kubernetes.io/fr/docs/concepts/services-networking/service/#externalname) could be defined without any port. + Traefik backends creation needs a port to be set, however Kubernetes [ExternalName Service](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) could be defined without any port. Accordingly, Traefik supports defining a port in two ways: - only on `IngressRoute` service @@ -1200,7 +1200,7 @@ Register the `IngressRouteTCP` [kind](../../reference/dynamic-configuration/kube !!! important "Using Kubernetes ExternalName Service" - Traefik backends creation needs a port to be set, however Kubernetes [ExternalName Service](https://kubernetes.io/fr/docs/concepts/services-networking/service/#externalname) could be defined without any port. + Traefik backends creation needs a port to be set, however Kubernetes [ExternalName Service](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) could be defined without any port. Accordingly, Traefik supports defining a port in two ways: - only on `IngressRouteTCP` service @@ -1379,7 +1379,7 @@ Register the `IngressRouteUDP` [kind](../../reference/dynamic-configuration/kube !!! important "Using Kubernetes ExternalName Service" - Traefik backends creation needs a port to be set, however Kubernetes [ExternalName Service](https://kubernetes.io/fr/docs/concepts/services-networking/service/#externalname) could be defined without any port. + Traefik backends creation needs a port to be set, however Kubernetes [ExternalName Service](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) could be defined without any port. Accordingly, Traefik supports defining a port in two ways: - only on `IngressRouteUDP` service