From 25e12aee14e3656343641693e8a52cb1a375c2a1 Mon Sep 17 00:00:00 2001 From: Aaron Raff Date: Thu, 7 Oct 2021 09:40:05 -0400 Subject: [PATCH 01/20] kubernetes: normalize middleware names in ingress route config --- .../tcp/with_middleware_multiple_hyphens.yml | 29 ++++++ .../with_middleware_multiple_hyphens.yml | 31 ++++++ .../kubernetes/crd/kubernetes_http.go | 2 +- pkg/provider/kubernetes/crd/kubernetes_tcp.go | 3 +- .../kubernetes/crd/kubernetes_test.go | 99 +++++++++++++++++++ pkg/provider/kubernetes/k8s/parser.go | 2 +- 6 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 pkg/provider/kubernetes/crd/fixtures/tcp/with_middleware_multiple_hyphens.yml create mode 100644 pkg/provider/kubernetes/crd/fixtures/with_middleware_multiple_hyphens.yml diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_middleware_multiple_hyphens.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_middleware_multiple_hyphens.yml new file mode 100644 index 000000000..dc66091c2 --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/tcp/with_middleware_multiple_hyphens.yml @@ -0,0 +1,29 @@ +apiVersion: traefik.containo.us/v1alpha1 +kind: MiddlewareTCP +metadata: + name: multiple---hyphens + namespace: default +spec: + ipWhiteList: + sourceRange: + - 127.0.0.1/32 + +--- +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 + + middlewares: + - name: multiple---hyphens diff --git a/pkg/provider/kubernetes/crd/fixtures/with_middleware_multiple_hyphens.yml b/pkg/provider/kubernetes/crd/fixtures/with_middleware_multiple_hyphens.yml new file mode 100644 index 000000000..cee835136 --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/with_middleware_multiple_hyphens.yml @@ -0,0 +1,31 @@ +apiVersion: traefik.containo.us/v1alpha1 +kind: Middleware +metadata: + name: multiple---hyphens + namespace: default + +spec: + stripPrefix: + prefixes: + - /tobestripped + +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: test2.route + namespace: default + +spec: + entryPoints: + - web + + routes: + - match: Host(`foo.com`) && PathPrefix(`/tobestripped`) + priority: 12 + kind: Rule + services: + - name: whoami + port: 80 + middlewares: + - name: multiple---hyphens diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index b78d14c31..b95a6679a 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -183,7 +183,7 @@ func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace str ns = mi.Namespace } - mds = append(mds, makeID(ns, name)) + mds = append(mds, provider.Normalize(makeID(ns, name))) } return mds, nil diff --git a/pkg/provider/kubernetes/crd/kubernetes_tcp.go b/pkg/provider/kubernetes/crd/kubernetes_tcp.go index 187020a2d..37c584e17 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_tcp.go +++ b/pkg/provider/kubernetes/crd/kubernetes_tcp.go @@ -10,6 +10,7 @@ import ( "github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/log" + "github.com/traefik/traefik/v2/pkg/provider" "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1" "github.com/traefik/traefik/v2/pkg/tls" corev1 "k8s.io/api/core/v1" @@ -162,7 +163,7 @@ func (p *Provider) makeMiddlewareTCPKeys(ctx context.Context, ingRouteTCPNamespa ns = mi.Namespace } - mds = append(mds, makeID(ns, mi.Name)) + mds = append(mds, provider.Normalize(makeID(ns, mi.Name))) } return mds, nil diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index dafa56653..310ae48bd 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -149,6 +149,54 @@ func TestLoadIngressRouteTCPs(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, + { + desc: "Middlewares in ingress route config are normalized", + paths: []string{"tcp/services.yml", "tcp/with_middleware_multiple_hyphens.yml"}, + 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", + Middlewares: []string{"default-multiple-hyphens"}, + Rule: "HostSNI(`foo.com`)", + }, + }, + Middlewares: map[string]*dynamic.TCPMiddleware{ + "default-multiple-hyphens": { + IPWhiteList: &dynamic.TCPIPWhiteList{ + SourceRange: []string{"127.0.0.1/32"}, + }, + }, + }, + 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{}, + }, + }, { desc: "Simple Ingress Route, with foo entrypoint and crossprovider middleware", paths: []string{"tcp/services.yml", "tcp/with_middleware_crossprovider.yml"}, @@ -1456,6 +1504,57 @@ func TestLoadIngressRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, + { + desc: "Middlewares in ingress route config are normalized", + AllowCrossNamespace: true, + paths: []string{"services.yml", "with_middleware_multiple_hyphens.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{ + "default-test2-route-23c7f4c450289ee29016": { + EntryPoints: []string{"web"}, + Service: "default-test2-route-23c7f4c450289ee29016", + Rule: "Host(`foo.com`) && PathPrefix(`/tobestripped`)", + Priority: 12, + Middlewares: []string{"default-multiple-hyphens"}, + }, + }, + Middlewares: map[string]*dynamic.Middleware{ + "default-multiple-hyphens": { + StripPrefix: &dynamic.StripPrefix{ + Prefixes: []string{"/tobestripped"}, + }, + }, + }, + Services: map[string]*dynamic.Service{ + "default-test2-route-23c7f4c450289ee29016": { + 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{}, + }, + }, { desc: "Simple Ingress Route with middleware crossprovider", AllowCrossNamespace: true, diff --git a/pkg/provider/kubernetes/k8s/parser.go b/pkg/provider/kubernetes/k8s/parser.go index f7336c5bd..16941be7c 100644 --- a/pkg/provider/kubernetes/k8s/parser.go +++ b/pkg/provider/kubernetes/k8s/parser.go @@ -14,7 +14,7 @@ import ( func MustParseYaml(content []byte) []runtime.Object { acceptedK8sTypes := regexp.MustCompile(`^(Deployment|Endpoints|Service|Ingress|IngressRoute|IngressRouteTCP|IngressRouteUDP|Middleware|MiddlewareTCP|Secret|TLSOption|TLSStore|TraefikService|IngressClass|ServersTransport|GatewayClass|Gateway|HTTPRoute|TCPRoute|TLSRoute)$`) - files := strings.Split(string(content), "---") + files := strings.Split(string(content), "---\n") retVal := make([]runtime.Object, 0, len(files)) for _, file := range files { if file == "\n" || file == "" { From fe32a7e5844642e7435d6f028d5d32d80faee6e8 Mon Sep 17 00:00:00 2001 From: Daniel Tomcej Date: Fri, 8 Oct 2021 03:32:08 -0600 Subject: [PATCH 02/20] fix: use EscapedPath as header value when RawPath is empty --- pkg/middlewares/replacepath/replace_path.go | 8 ++++---- pkg/middlewares/replacepath/replace_path_test.go | 10 ++++++++++ .../replacepathregex/replace_path_regex.go | 13 ++++--------- .../replacepathregex/replace_path_regex_test.go | 10 ++++++++++ 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/pkg/middlewares/replacepath/replace_path.go b/pkg/middlewares/replacepath/replace_path.go index a7e82826a..d730a931c 100644 --- a/pkg/middlewares/replacepath/replace_path.go +++ b/pkg/middlewares/replacepath/replace_path.go @@ -41,12 +41,12 @@ func (r *replacePath) GetTracingInformation() (string, ext.SpanKindEnum) { } func (r *replacePath) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - if req.URL.RawPath == "" { - req.Header.Add(ReplacedPathHeader, req.URL.Path) - } else { - req.Header.Add(ReplacedPathHeader, req.URL.RawPath) + currentPath := req.URL.RawPath + if currentPath == "" { + currentPath = req.URL.EscapedPath() } + req.Header.Add(ReplacedPathHeader, currentPath) req.URL.RawPath = r.path var err error diff --git a/pkg/middlewares/replacepath/replace_path_test.go b/pkg/middlewares/replacepath/replace_path_test.go index 657b1c3b8..782733bc0 100644 --- a/pkg/middlewares/replacepath/replace_path_test.go +++ b/pkg/middlewares/replacepath/replace_path_test.go @@ -60,6 +60,16 @@ func TestReplacePath(t *testing.T) { expectedRawPath: "/foo%2Fbar", expectedHeader: "/path", }, + { + desc: "replacement with percent encoded backspace char", + path: "/path/%08bar", + config: dynamic.ReplacePath{ + Path: "/path/%08bar", + }, + expectedPath: "/path/\bbar", + expectedRawPath: "/path/%08bar", + expectedHeader: "/path/%08bar", + }, } for _, test := range testCases { diff --git a/pkg/middlewares/replacepathregex/replace_path_regex.go b/pkg/middlewares/replacepathregex/replace_path_regex.go index 4a10fc1c9..fea6fa2de 100644 --- a/pkg/middlewares/replacepathregex/replace_path_regex.go +++ b/pkg/middlewares/replacepathregex/replace_path_regex.go @@ -16,9 +16,7 @@ import ( "github.com/traefik/traefik/v2/pkg/tracing" ) -const ( - typeName = "ReplacePathRegex" -) +const typeName = "ReplacePathRegex" // ReplacePathRegex is a middleware used to replace the path of a URL request with a regular expression. type replacePathRegex struct { @@ -50,16 +48,13 @@ func (rp *replacePathRegex) GetTracingInformation() (string, ext.SpanKindEnum) { } func (rp *replacePathRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - var currentPath string - if req.URL.RawPath == "" { - currentPath = req.URL.Path - } else { - currentPath = req.URL.RawPath + currentPath := req.URL.RawPath + if currentPath == "" { + currentPath = req.URL.EscapedPath() } if rp.regexp != nil && len(rp.replacement) > 0 && rp.regexp.MatchString(currentPath) { req.Header.Add(replacepath.ReplacedPathHeader, currentPath) - req.URL.RawPath = rp.regexp.ReplaceAllString(currentPath, rp.replacement) // as replacement can introduce escaped characters diff --git a/pkg/middlewares/replacepathregex/replace_path_regex_test.go b/pkg/middlewares/replacepathregex/replace_path_regex_test.go index c22083ea5..59018d784 100644 --- a/pkg/middlewares/replacepathregex/replace_path_regex_test.go +++ b/pkg/middlewares/replacepathregex/replace_path_regex_test.go @@ -106,6 +106,16 @@ func TestReplacePathRegex(t *testing.T) { expectedPath: "/aaa/bbb", expectedRawPath: "/aaa%2Fbbb", }, + { + desc: "path with percent encoded backspace char", + path: "/foo/%08bar", + config: dynamic.ReplacePathRegex{ + Replacement: "/$1", + Regex: `^/foo/(.*)`, + }, + expectedPath: "/\bbar", + expectedRawPath: "/%08bar", + }, } for _, test := range testCases { From 207ac94ed0241543772ee43386726c36fce868c6 Mon Sep 17 00:00:00 2001 From: Tom Moulard Date: Fri, 8 Oct 2021 11:52:05 +0200 Subject: [PATCH 03/20] Fix remove http scheme urls in documentation --- docs/content/getting-started/install-traefik.md | 4 ++-- docs/content/getting-started/quick-start.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/content/getting-started/install-traefik.md b/docs/content/getting-started/install-traefik.md index e82168f8e..f32c507db 100644 --- a/docs/content/getting-started/install-traefik.md +++ b/docs/content/getting-started/install-traefik.md @@ -101,13 +101,13 @@ helm install traefik traefik/traefik This HelmChart does not expose the Traefik dashboard by default, for security concerns. Thus, there are multiple ways to expose the dashboard. -For instance, the dashboard access could be achieved through a port-forward : +For instance, the dashboard access could be achieved through a port-forward: ```shell kubectl port-forward $(kubectl get pods --selector "app.kubernetes.io/name=traefik" --output=name) 9000:9000 ``` -Accessible with the url: http://127.0.0.1:9000/dashboard/ +It can then be reached at: `http://127.0.0.1:9000/dashboard/` Another way would be to apply your own configuration, for instance, by defining and applying an IngressRoute CRD (`kubectl apply -f dashboard.yaml`): diff --git a/docs/content/getting-started/quick-start.md b/docs/content/getting-started/quick-start.md index ee0ee5923..4cce47022 100644 --- a/docs/content/getting-started/quick-start.md +++ b/docs/content/getting-started/quick-start.md @@ -36,7 +36,7 @@ Start your `reverse-proxy` with the following command: docker-compose up -d reverse-proxy ``` -You can open a browser and go to to see Traefik's API rawdata (we'll go back there once we have launched a service in step 2). +You can open a browser and go to `http://localhost:8080/api/rawdata` to see Traefik's API rawdata (we'll go back there once we have launched a service in step 2). ## Traefik Detects New Services and Creates the Route for You @@ -61,7 +61,7 @@ Start the `whoami` service with the following command: docker-compose up -d whoami ``` -Go back to your browser () and see that Traefik has automatically detected the new container and updated its own configuration. +Go back to your browser (`http://localhost:8080/api/rawdata`) and see that Traefik has automatically detected the new container and updated its own configuration. When Traefik detects new services, it creates the corresponding routes so you can call them ... _let's see!_ (Here, we're using curl) @@ -85,7 +85,7 @@ Run more instances of your `whoami` service with the following command: docker-compose up -d --scale whoami=2 ``` -Go back to your browser () and see that Traefik has automatically detected the new instance of the container. +Go back to your browser (`http://localhost:8080/api/rawdata`) and see that Traefik has automatically detected the new instance of the container. Finally, see that Traefik load-balances between the two instances of your service by running the following command twice: From 7377ab7b9522497b9e74c8eb6ccd511d71630c55 Mon Sep 17 00:00:00 2001 From: Anton Kindblad Date: Mon, 18 Oct 2021 14:52:14 +0200 Subject: [PATCH 04/20] fix(ui): bug parsing weighted service provider name --- webui/src/components/_commons/PanelWeightedServices.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webui/src/components/_commons/PanelWeightedServices.vue b/webui/src/components/_commons/PanelWeightedServices.vue index f5c9d3880..3f23ed697 100644 --- a/webui/src/components/_commons/PanelWeightedServices.vue +++ b/webui/src/components/_commons/PanelWeightedServices.vue @@ -64,7 +64,7 @@ export default { }, getProviderLogoPath (service) { const provider = this.getProvider(service) - const name = provider.name.toLowerCase() + const name = provider.toLowerCase() if (name.includes('plugin-')) { return 'statics/providers/plugin.svg' From c0ba4d177f1a1323dbe57f53b7902d7230f2f06a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20L=C3=B3pez=20Mareque?= Date: Tue, 19 Oct 2021 14:40:06 +0200 Subject: [PATCH 05/20] fix: sourceCriterion documentation for InFlightReq and RateLimit middlewares --- docs/content/middlewares/http/inflightreq.md | 2 +- docs/content/middlewares/http/ratelimit.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/middlewares/http/inflightreq.md b/docs/content/middlewares/http/inflightreq.md index 8095ccd15..f2f76a3cb 100644 --- a/docs/content/middlewares/http/inflightreq.md +++ b/docs/content/middlewares/http/inflightreq.md @@ -115,7 +115,7 @@ http: ### `sourceCriterion` The `sourceCriterion` option defines what criterion is used to group requests as originating from a common source. -The precedence order is `ipStrategy`, then `requestHeaderName`, then `requestHost`. +If several strategies are defined at the same time, an error will be raised. If none are set, the default is to use the `requestHost`. #### `sourceCriterion.ipStrategy` diff --git a/docs/content/middlewares/http/ratelimit.md b/docs/content/middlewares/http/ratelimit.md index 8c382de10..7c8a45906 100644 --- a/docs/content/middlewares/http/ratelimit.md +++ b/docs/content/middlewares/http/ratelimit.md @@ -250,7 +250,7 @@ http: ### `sourceCriterion` The `sourceCriterion` option defines what criterion is used to group requests as originating from a common source. -The precedence order is `ipStrategy`, then `requestHeaderName`, then `requestHost`. +If several strategies are defined at the same time, an error will be raised. If none are set, the default is to use the request's remote address field (as an `ipStrategy`). #### `sourceCriterion.ipStrategy` From d9b8435a7d3bc1fd8e34f634fe2d2ed8ed1ff619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20L=C3=B3pez=20Mareque?= Date: Thu, 21 Oct 2021 09:44:12 +0200 Subject: [PATCH 06/20] feat: rename networking.k8s.io/v1beta1 to networking.k8s.io/v1 --- docs/content/migration/v2.md | 12 +- docs/content/providers/kubernetes-ingress.md | 62 ++-- .../kubernetes-crd-rbac.yml | 4 +- .../kubernetes-gateway-rbac.yml | 2 +- .../routing/providers/kubernetes-ingress.md | 284 +++++++++--------- 5 files changed, 182 insertions(+), 182 deletions(-) diff --git a/docs/content/migration/v2.md b/docs/content/migration/v2.md index 77b715abb..c93b73afd 100644 --- a/docs/content/migration/v2.md +++ b/docs/content/migration/v2.md @@ -179,7 +179,7 @@ To enable HTTPS, it is not sufficient anymore to only rely on a TLS section in t #### Expose an Ingress on 80 and 443 -Define the default TLS configuration on the HTTPS entry point. +Define the default TLS configuration on the HTTPS entry point. ```yaml tab="Ingress" kind: Ingress @@ -335,7 +335,7 @@ The file parser has been changed, since v2.3 the unknown options/fields in a dyn ### IngressClass In `v2.3`, the support of `IngressClass`, which is available since Kubernetes version `1.18`, has been introduced. -In order to be able to use this new resource the [Kubernetes RBAC](../reference/dynamic-configuration/kubernetes-crd.md#rbac) must be updated. +In order to be able to use this new resource the [Kubernetes RBAC](../reference/dynamic-configuration/kubernetes-crd.md#rbac) must be updated. ## v2.3 to v2.4 @@ -350,7 +350,7 @@ It is therefore necessary to update [RBAC](../reference/dynamic-configuration/ku In `v2.4.8`, we introduced a new check on domain names used in HTTP router rule `Host` and `HostRegexp` expressions, and in TCP router rule `HostSNI` expression. -This check ensures that provided domain names don't contain non-ASCII characters. +This check ensures that provided domain names don't contain non-ASCII characters. If not, an error is raised, and the associated router will be shown as invalid in the dashboard. This new behavior is intended to show what was failing silently previously and to help troubleshooting configuration issues. @@ -380,8 +380,8 @@ To allow it, the `allowExternalNameServices` option should be set to `true`. ### Kubernetes CRD -In `v2.5`, the [Traefik CRDs](../reference/dynamic-configuration/kubernetes-crd.md#definitions) have been updated to support the new API version `apiextensions.k8s.io/v1`. -As required by `apiextensions.k8s.io/v1`, we have included the OpenAPI validation schema. +In `v2.5`, the [Traefik CRDs](../reference/dynamic-configuration/kubernetes-crd.md#definitions) have been updated to support the new API version `apiextensions.k8s.io/v1`. +As required by `apiextensions.k8s.io/v1`, we have included the OpenAPI validation schema. After deploying the new [Traefik CRDs](../reference/dynamic-configuration/kubernetes-crd.md#definitions), the resources will be validated only on creation or update. @@ -420,6 +420,6 @@ the legacy behavior related to the CommonName field can not be enabled at all an ### Errors middleware -In `v2.5.4`, when the errors service is configured with the [`PassHostHeader`](../routing/services/index.md#pass-host-header) option to `true` (default), +In `v2.5.4`, when the errors service is configured with the [`PassHostHeader`](../routing/services/index.md#pass-host-header) option to `true` (default), the forwarded Host header value is now set to the client request Host value and not `0.0.0.0`. Check out the [Errors middleware](../middlewares/http/errorpages.md#service) documentation for more details. diff --git a/docs/content/providers/kubernetes-ingress.md b/docs/content/providers/kubernetes-ingress.md index 63e4ef06a..bfbf0cff6 100644 --- a/docs/content/providers/kubernetes-ingress.md +++ b/docs/content/providers/kubernetes-ingress.md @@ -36,10 +36,10 @@ and derives the corresponding dynamic configuration from it, which in turn creates the resulting routers, services, handlers, etc. ```yaml tab="Ingress" +apiVersion: networking.k8s.io/v1 kind: Ingress -apiVersion: networking.k8s.io/v1beta1 metadata: - name: "foo" + name: foo namespace: production spec: @@ -48,20 +48,26 @@ spec: http: paths: - path: /bar + pathType: Exact backend: - serviceName: service1 - servicePort: 80 + service: + name: service1 + port: + number: 80 - path: /foo + pathType: Exact backend: - serviceName: service1 - servicePort: 80 + service: + name: service1 + port: + number: 80 ``` -```yaml tab="Ingress Kubernetes v1.19+" +```yaml tab="Ingress v1beta1 (deprecated)" +apiVersion: networking.k8s.io/v1beta1 kind: Ingress -apiVersion: networking.k8s.io/v1 metadata: - name: "foo" + name: foo namespace: production spec: @@ -70,19 +76,13 @@ spec: http: paths: - path: /bar - pathType: Exact backend: - service: - name: service1 - port: - number: 80 + serviceName: service1 + servicePort: 80 - path: /foo - pathType: Exact backend: - service: - name: service1 - port: - number: 80 + serviceName: service1 + servicePort: 80 ``` ## LetsEncrypt Support with the Ingress Provider @@ -272,19 +272,19 @@ Otherwise, Ingresses missing the annotation, having an empty value, or the value ``` ```yaml tab="Ingress" - apiVersion: "networking.k8s.io/v1beta1" - kind: "Ingress" + apiVersion: networking.k8s.io/v1beta1 + kind: Ingress metadata: - name: "example-ingress" + name: example-ingress spec: - ingressClassName: "traefik-lb" + ingressClassName: traefik-lb rules: - host: "*.example.com" http: paths: - - path: "/example" + - path: /example backend: - serviceName: "example-service" + serviceName: example-service servicePort: 80 ``` @@ -303,21 +303,21 @@ Otherwise, Ingresses missing the annotation, having an empty value, or the value ``` ```yaml tab="Ingress" - apiVersion: "networking.k8s.io/v1" - kind: "Ingress" + apiVersion: networking.k8s.io/v1 + kind: Ingress metadata: - name: "example-ingress" + name: example-ingress spec: - ingressClassName: "traefik-lb" + ingressClassName: traefik-lb rules: - host: "*.example.com" http: paths: - - path: "/example" + - path: /example pathType: Exact backend: service: - name: "example-service" + name: example-service port: number: 80 ``` diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml index 58548c561..4a5b80e6b 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml @@ -1,5 +1,5 @@ +apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: traefik-ingress-controller @@ -48,8 +48,8 @@ rules: - watch --- +apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: traefik-ingress-controller diff --git a/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml b/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml index c864f55af..72c35e5a5 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml @@ -38,8 +38,8 @@ rules: - update --- +apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: gateway-controller diff --git a/docs/content/routing/providers/kubernetes-ingress.md b/docs/content/routing/providers/kubernetes-ingress.md index e4a2fb585..d2448f3e0 100644 --- a/docs/content/routing/providers/kubernetes-ingress.md +++ b/docs/content/routing/providers/kubernetes-ingress.md @@ -15,8 +15,8 @@ which in turn will create the resulting routers, services, handlers, etc. ```yaml tab="RBAC" --- + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole - apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: traefik-ingress-controller rules: @@ -48,8 +48,8 @@ which in turn will create the resulting routers, services, handlers, etc. - update --- + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding - apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: traefik-ingress-controller roleRef: @@ -63,8 +63,37 @@ which in turn will create the resulting routers, services, handlers, etc. ``` ```yaml tab="Ingress" + apiVersion: networking.k8s.io/v1 kind: Ingress + metadata: + name: myingress + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: web + + spec: + rules: + - host: example.com + http: + paths: + - path: /bar + pathType: Exact + backend: + service: + name: whoami + port: + number: 80 + - path: /foo + pathType: Exact + backend: + service: + name: whoami + port: + number: 80 + ``` + + ```yaml tab="Ingress v1beta1 (deprecated)" apiVersion: networking.k8s.io/v1beta1 + kind: Ingress metadata: name: myingress annotations: @@ -84,36 +113,7 @@ which in turn will create the resulting routers, services, handlers, etc. serviceName: whoami servicePort: 80 ``` - - ```yaml tab="Ingress Kubernetes v1.19+" - kind: Ingress - apiVersion: networking.k8s.io/v1 - metadata: - name: myingress - annotations: - traefik.ingress.kubernetes.io/router.entrypoints: web - - spec: - rules: - - host: example.com - http: - paths: - - path: /bar - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 - - path: /foo - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 - ``` - + ```yaml tab="Traefik" apiVersion: v1 kind: ServiceAccount @@ -121,8 +121,8 @@ which in turn will create the resulting routers, services, handlers, etc. name: traefik-ingress-controller --- - kind: Deployment apiVersion: apps/v1 + kind: Deployment metadata: name: traefik labels: @@ -166,8 +166,8 @@ which in turn will create the resulting routers, services, handlers, etc. ``` ```yaml tab="Whoami" - kind: Deployment apiVersion: apps/v1 + kind: Deployment metadata: name: whoami labels: @@ -237,7 +237,7 @@ which in turn will create the resulting routers, services, handlers, etc. ??? info "`traefik.ingress.kubernetes.io/router.pathmatcher`" - Overrides the default router rule type used for a path. + Overrides the default router rule type used for a path. Only path-related matcher name can be specified: `Path`, `PathPrefix`. Default `PathPrefix` @@ -401,8 +401,8 @@ This way, any Ingress attached to this Entrypoint will have TLS termination by d ```yaml tab="RBAC" --- + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole - apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: traefik-ingress-controller rules: @@ -434,8 +434,8 @@ This way, any Ingress attached to this Entrypoint will have TLS termination by d - update --- + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding - apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: traefik-ingress-controller roleRef: @@ -449,8 +449,37 @@ This way, any Ingress attached to this Entrypoint will have TLS termination by d ``` ```yaml tab="Ingress" + apiVersion: networking.k8s.io/v1 kind: Ingress + metadata: + name: myingress + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + + spec: + rules: + - host: example.com + http: + paths: + - path: /bar + pathType: Exact + backend: + service: + name: whoami + port: + number: 80 + - path: /foo + pathType: Exact + backend: + service: + name: whoami + port: + number: 80 + ``` + + ```yaml tab="Ingress v1beta1 (deprecated)" apiVersion: networking.k8s.io/v1beta1 + kind: Ingress metadata: name: myingress annotations: @@ -470,36 +499,7 @@ This way, any Ingress attached to this Entrypoint will have TLS termination by d serviceName: whoami servicePort: 80 ``` - - ```yaml tab="Ingress Kubernetes v1.19+" - kind: Ingress - apiVersion: networking.k8s.io/v1 - metadata: - name: myingress - annotations: - traefik.ingress.kubernetes.io/router.entrypoints: websecure - - spec: - rules: - - host: example.com - http: - paths: - - path: /bar - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 - - path: /foo - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 - ``` - + ```yaml tab="Traefik" apiVersion: v1 kind: ServiceAccount @@ -507,8 +507,8 @@ This way, any Ingress attached to this Entrypoint will have TLS termination by d name: traefik-ingress-controller --- - kind: Deployment apiVersion: apps/v1 + kind: Deployment metadata: name: traefik labels: @@ -553,8 +553,8 @@ This way, any Ingress attached to this Entrypoint will have TLS termination by d ``` ```yaml tab="Whoami" - kind: Deployment apiVersion: apps/v1 + kind: Deployment metadata: name: whoami labels: @@ -608,8 +608,8 @@ For more options, please refer to the available [annotations](#on-ingress). ```yaml tab="RBAC" --- + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole - apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: traefik-ingress-controller rules: @@ -641,8 +641,8 @@ For more options, please refer to the available [annotations](#on-ingress). - update --- + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding - apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: traefik-ingress-controller roleRef: @@ -656,8 +656,38 @@ For more options, please refer to the available [annotations](#on-ingress). ``` ```yaml tab="Ingress" + apiVersion: networking.k8s.io/v1 kind: Ingress + metadata: + name: myingress + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: true + + spec: + rules: + - host: example.com + http: + paths: + - path: /bar + pathType: Exact + backend: + service: + name: whoami + port: + number: 80 + - path: /foo + pathType: Exact + backend: + service: + name: whoami + port: + number: 80 + ``` + + ```yaml tab="Ingress v1beta1 (deprecated)" apiVersion: networking.k8s.io/v1beta1 + kind: Ingress metadata: name: myingress annotations: @@ -678,37 +708,7 @@ For more options, please refer to the available [annotations](#on-ingress). serviceName: whoami servicePort: 80 ``` - - ```yaml tab="Ingress Kubernetes v1.19+" - kind: Ingress - apiVersion: networking.k8s.io/v1 - metadata: - name: myingress - annotations: - traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.tls: true - - spec: - rules: - - host: example.com - http: - paths: - - path: /bar - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 - - path: /foo - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 - ``` - + ```yaml tab="Traefik" apiVersion: v1 kind: ServiceAccount @@ -716,8 +716,8 @@ For more options, please refer to the available [annotations](#on-ingress). name: traefik-ingress-controller --- - kind: Deployment apiVersion: apps/v1 + kind: Deployment metadata: name: traefik labels: @@ -761,8 +761,8 @@ For more options, please refer to the available [annotations](#on-ingress). ``` ```yaml tab="Whoami" - kind: Deployment apiVersion: apps/v1 + kind: Deployment metadata: name: whoami labels: @@ -807,8 +807,34 @@ For more options, please refer to the available [annotations](#on-ingress). ??? example "Using a secret" ```yaml tab="Ingress" + apiVersion: networking.k8s.io/v1 kind: Ingress + metadata: + name: foo + namespace: production + + spec: + rules: + - host: example.net + http: + paths: + - path: /bar + pathType: Exact + backend: + service: + name: service1 + port: + number: 80 + # Only selects which certificate(s) should be loaded from the secret, in order to terminate TLS. + # Doesn't enable TLS for that ingress (hence for the underlying router). + # Please see the TLS annotations on ingress made for that purpose. + tls: + - secretName: supersecret + ``` + + ```yaml tab="Ingress v1beta1 (deprecated)" apiVersion: networking.k8s.io/v1beta1 + kind: Ingress metadata: name: foo namespace: production @@ -829,32 +855,6 @@ For more options, please refer to the available [annotations](#on-ingress). - secretName: supersecret ``` - ```yaml tab="Ingress Kubernetes v1.19+" - kind: Ingress - apiVersion: networking.k8s.io/v1 - metadata: - name: foo - namespace: production - - spec: - rules: - - host: example.net - http: - paths: - - path: /bar - pathType: Exact - backend: - service: - name: service1 - port: - number: 80 - # Only selects which certificate(s) should be loaded from the secret, in order to terminate TLS. - # Doesn't enable TLS for that ingress (hence for the underlying router). - # Please see the TLS annotations on ingress made for that purpose. - tls: - - secretName: supersecret - ``` - ```yaml tab="Secret" apiVersion: v1 kind: Secret @@ -900,18 +900,6 @@ and will connect via TLS automatically. Ingresses can be created that look like the following: ```yaml tab="Ingress" -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: cheese - -spec: - defaultBackend: - serviceName: stilton - serverPort: 80 -``` - -```yaml tab="Ingress Kubernetes v1.19+" apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -925,6 +913,18 @@ spec: number: 80 ``` +```yaml tab="Ingress v1beta1 (deprecated)" +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: cheese + +spec: + defaultBackend: + serviceName: stilton + serverPort: 80 +``` + This ingress follows the Global Default Backend property of ingresses. This will allow users to create a "default router" that will match all unmatched requests. From b537ccdb0c2332643fbd26064e5cb6391dbeefe1 Mon Sep 17 00:00:00 2001 From: Tom Moulard Date: Mon, 25 Oct 2021 17:18:12 +0200 Subject: [PATCH 07/20] doc: update traefik image version --- docs/content/getting-started/install-traefik.md | 2 +- docs/content/observability/access-logs.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/getting-started/install-traefik.md b/docs/content/getting-started/install-traefik.md index f32c507db..02a1aa243 100644 --- a/docs/content/getting-started/install-traefik.md +++ b/docs/content/getting-started/install-traefik.md @@ -24,7 +24,7 @@ For more details, go to the [Docker provider documentation](../providers/docker. !!! tip * Prefer a fixed version than the latest that could be an unexpected version. - ex: `traefik:v2.1.4` + ex: `traefik:v2.5` * Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine). * Any orchestrator using docker images can fetch the official Traefik docker image. diff --git a/docs/content/observability/access-logs.md b/docs/content/observability/access-logs.md index c1ad21d94..1f9ba9903 100644 --- a/docs/content/observability/access-logs.md +++ b/docs/content/observability/access-logs.md @@ -247,7 +247,7 @@ version: "3.7" services: traefik: - image: traefik:v2.2 + image: traefik:v2.5 environment: - TZ=US/Alaska command: From 566b205758b333dc8bdad048ed6c6fc2428df9c8 Mon Sep 17 00:00:00 2001 From: Romain Date: Tue, 26 Oct 2021 10:30:13 +0200 Subject: [PATCH 08/20] Clarify usage for cross provider references in Kubernetes ingress annotations --- docs/content/routing/providers/kubernetes-ingress.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/content/routing/providers/kubernetes-ingress.md b/docs/content/routing/providers/kubernetes-ingress.md index d2448f3e0..0f24b0317 100644 --- a/docs/content/routing/providers/kubernetes-ingress.md +++ b/docs/content/routing/providers/kubernetes-ingress.md @@ -209,6 +209,11 @@ which in turn will create the resulting routers, services, handlers, etc. ## Annotations +!!! warning "Referencing resources in annotations" + + In an annotation, when referencing a resource defined by another provider, + the [provider namespace syntax](../../providers/overview.md#provider-namespace) must be used. + #### On Ingress ??? info "`traefik.ingress.kubernetes.io/router.entrypoints`" @@ -224,7 +229,7 @@ which in turn will create the resulting routers, services, handlers, etc. See [middlewares](../routers/index.md#middlewares) and [middlewares overview](../../middlewares/overview.md) for more information. ```yaml - traefik.ingress.kubernetes.io/router.middlewares: auth@file,prefix@kubernetescrd,cb@file + traefik.ingress.kubernetes.io/router.middlewares: auth@file,default-prefix@kubernetescrd ``` ??? info "`traefik.ingress.kubernetes.io/router.priority`" @@ -283,7 +288,7 @@ which in turn will create the resulting routers, services, handlers, etc. See [options](../routers/index.md#options) for more information. ```yaml - traefik.ingress.kubernetes.io/router.tls.options: foobar + traefik.ingress.kubernetes.io/router.tls.options: foobar@file ``` #### On Service From d3ff0c2cd4221d7be8104620a59b0331b2051577 Mon Sep 17 00:00:00 2001 From: Kevin Pollet Date: Tue, 26 Oct 2021 10:54:11 +0200 Subject: [PATCH 09/20] fix: do not require a TLS client cert when InsecureSkipVerify is false Co-authored-by: Tom Moulard --- .../traefik.containo.us_middlewares.yaml | 4 +- integration/fixtures/k8s/01-traefik-crd.yml | 4 +- pkg/anonymize/anonymize_config_test.go | 6 +- pkg/config/dynamic/middlewares.go | 105 ++------------- pkg/config/dynamic/zz_generated.deepcopy.go | 30 +---- pkg/config/label/label_test.go | 13 +- pkg/middlewares/auth/forward.go | 4 +- .../passtlsclientcert/pass_tls_client_cert.go | 2 +- .../pass_tls_client_cert_test.go | 20 +-- pkg/provider/docker/docker.go | 2 +- pkg/provider/http/http.go | 2 +- pkg/provider/kubernetes/crd/kubernetes.go | 3 +- .../kubernetes/crd/kubernetes_test.go | 15 ++- pkg/provider/kv/kv.go | 2 +- pkg/provider/kv/kv_test.go | 6 +- pkg/provider/marathon/marathon.go | 2 +- pkg/types/fixtures/cert.pem | 13 ++ pkg/types/fixtures/key.pem | 16 +++ pkg/types/tls.go | 69 ++++++---- pkg/types/tls_test.go | 123 ++++++++++++++++++ pkg/types/zz_generated.deepcopy.go | 16 +++ 21 files changed, 273 insertions(+), 184 deletions(-) create mode 100644 pkg/types/fixtures/cert.pem create mode 100644 pkg/types/fixtures/key.pem create mode 100644 pkg/types/tls_test.go diff --git a/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml b/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml index 5129a5342..c94412d37 100644 --- a/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml @@ -398,7 +398,7 @@ spec: info configuration. properties: issuer: - description: TLSCLientCertificateDNInfo holds the client TLS + description: TLSClientCertificateDNInfo holds the client TLS certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739 properties: commonName: @@ -425,7 +425,7 @@ spec: serialNumber: type: boolean subject: - description: TLSCLientCertificateDNInfo holds the client TLS + description: TLSClientCertificateDNInfo holds the client TLS certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739 properties: commonName: diff --git a/integration/fixtures/k8s/01-traefik-crd.yml b/integration/fixtures/k8s/01-traefik-crd.yml index 44212b9f0..824cdb24d 100644 --- a/integration/fixtures/k8s/01-traefik-crd.yml +++ b/integration/fixtures/k8s/01-traefik-crd.yml @@ -840,7 +840,7 @@ spec: info configuration. properties: issuer: - description: TLSCLientCertificateDNInfo holds the client TLS + description: TLSClientCertificateDNInfo holds the client TLS certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739 properties: commonName: @@ -867,7 +867,7 @@ spec: serialNumber: type: boolean subject: - description: TLSCLientCertificateDNInfo holds the client TLS + description: TLSClientCertificateDNInfo holds the client TLS certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739 properties: commonName: diff --git a/pkg/anonymize/anonymize_config_test.go b/pkg/anonymize/anonymize_config_test.go index 941fef1b6..020d08a80 100644 --- a/pkg/anonymize/anonymize_config_test.go +++ b/pkg/anonymize/anonymize_config_test.go @@ -273,7 +273,7 @@ func TestDo_dynamicConfiguration(t *testing.T) { }, ForwardAuth: &dynamic.ForwardAuth{ Address: "127.0.0.1", - TLS: &dynamic.ClientTLS{ + TLS: &types.ClientTLS{ CA: "ca.pem", CAOptional: true, Cert: "cert.pem", @@ -315,7 +315,7 @@ func TestDo_dynamicConfiguration(t *testing.T) { NotAfter: true, NotBefore: true, Sans: true, - Subject: &dynamic.TLSCLientCertificateDNInfo{ + Subject: &dynamic.TLSClientCertificateDNInfo{ Country: true, Province: true, Locality: true, @@ -324,7 +324,7 @@ func TestDo_dynamicConfiguration(t *testing.T) { SerialNumber: true, DomainComponent: true, }, - Issuer: &dynamic.TLSCLientCertificateDNInfo{ + Issuer: &dynamic.TLSClientCertificateDNInfo{ Country: true, Province: true, Locality: true, diff --git a/pkg/config/dynamic/middlewares.go b/pkg/config/dynamic/middlewares.go index 0b3452f0a..d47ef8b22 100644 --- a/pkg/config/dynamic/middlewares.go +++ b/pkg/config/dynamic/middlewares.go @@ -1,14 +1,11 @@ package dynamic import ( - "crypto/tls" - "crypto/x509" - "fmt" - "os" "time" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v2/pkg/ip" + "github.com/traefik/traefik/v2/pkg/types" ) // +k8s:deepcopy-gen=true @@ -130,12 +127,12 @@ type ErrorPage struct { // ForwardAuth holds the http forward authentication configuration. type ForwardAuth struct { - Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"` - TLS *ClientTLS `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` - TrustForwardHeader bool `json:"trustForwardHeader,omitempty" toml:"trustForwardHeader,omitempty" yaml:"trustForwardHeader,omitempty" export:"true"` - AuthResponseHeaders []string `json:"authResponseHeaders,omitempty" toml:"authResponseHeaders,omitempty" yaml:"authResponseHeaders,omitempty" export:"true"` - AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty" toml:"authResponseHeadersRegex,omitempty" yaml:"authResponseHeadersRegex,omitempty" export:"true"` - AuthRequestHeaders []string `json:"authRequestHeaders,omitempty" toml:"authRequestHeaders,omitempty" yaml:"authRequestHeaders,omitempty" export:"true"` + Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"` + TLS *types.ClientTLS `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` + TrustForwardHeader bool `json:"trustForwardHeader,omitempty" toml:"trustForwardHeader,omitempty" yaml:"trustForwardHeader,omitempty" export:"true"` + AuthResponseHeaders []string `json:"authResponseHeaders,omitempty" toml:"authResponseHeaders,omitempty" yaml:"authResponseHeaders,omitempty" export:"true"` + AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty" toml:"authResponseHeadersRegex,omitempty" yaml:"authResponseHeadersRegex,omitempty" export:"true"` + AuthRequestHeaders []string `json:"authRequestHeaders,omitempty" toml:"authRequestHeaders,omitempty" yaml:"authRequestHeaders,omitempty" export:"true"` } // +k8s:deepcopy-gen=true @@ -402,16 +399,16 @@ type TLSClientCertificateInfo struct { NotAfter bool `json:"notAfter,omitempty" toml:"notAfter,omitempty" yaml:"notAfter,omitempty" export:"true"` NotBefore bool `json:"notBefore,omitempty" toml:"notBefore,omitempty" yaml:"notBefore,omitempty" export:"true"` Sans bool `json:"sans,omitempty" toml:"sans,omitempty" yaml:"sans,omitempty" export:"true"` - Subject *TLSCLientCertificateDNInfo `json:"subject,omitempty" toml:"subject,omitempty" yaml:"subject,omitempty" export:"true"` - Issuer *TLSCLientCertificateDNInfo `json:"issuer,omitempty" toml:"issuer,omitempty" yaml:"issuer,omitempty" export:"true"` + Subject *TLSClientCertificateDNInfo `json:"subject,omitempty" toml:"subject,omitempty" yaml:"subject,omitempty" export:"true"` + Issuer *TLSClientCertificateDNInfo `json:"issuer,omitempty" toml:"issuer,omitempty" yaml:"issuer,omitempty" export:"true"` SerialNumber bool `json:"serialNumber,omitempty" toml:"serialNumber,omitempty" yaml:"serialNumber,omitempty" export:"true"` } // +k8s:deepcopy-gen=true -// TLSCLientCertificateDNInfo holds the client TLS certificate distinguished name info configuration. +// TLSClientCertificateDNInfo holds the client TLS certificate distinguished name info configuration. // cf https://tools.ietf.org/html/rfc3739 -type TLSCLientCertificateDNInfo struct { +type TLSClientCertificateDNInfo struct { Country bool `json:"country,omitempty" toml:"country,omitempty" yaml:"country,omitempty" export:"true"` Province bool `json:"province,omitempty" toml:"province,omitempty" yaml:"province,omitempty" export:"true"` Locality bool `json:"locality,omitempty" toml:"locality,omitempty" yaml:"locality,omitempty" export:"true"` @@ -425,83 +422,3 @@ type TLSCLientCertificateDNInfo struct { // Users holds a list of users. type Users []string - -// +k8s:deepcopy-gen=true - -// ClientTLS holds the TLS specific configurations as client -// CA, Cert and Key can be either path or file contents. -type ClientTLS struct { - CA string `json:"ca,omitempty" toml:"ca,omitempty" yaml:"ca,omitempty"` - CAOptional bool `json:"caOptional,omitempty" toml:"caOptional,omitempty" yaml:"caOptional,omitempty" export:"true"` - Cert string `json:"cert,omitempty" toml:"cert,omitempty" yaml:"cert,omitempty"` - Key string `json:"key,omitempty" toml:"key,omitempty" yaml:"key,omitempty"` - InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"` -} - -// CreateTLSConfig creates a TLS config from ClientTLS structures. -func (c *ClientTLS) CreateTLSConfig() (*tls.Config, error) { - if c == nil { - return nil, nil - } - - var err error - caPool := x509.NewCertPool() - clientAuth := tls.NoClientCert - if c.CA != "" { - var ca []byte - if _, errCA := os.Stat(c.CA); errCA == nil { - ca, err = os.ReadFile(c.CA) - if err != nil { - return nil, fmt.Errorf("failed to read CA. %w", err) - } - } else { - ca = []byte(c.CA) - } - - if !caPool.AppendCertsFromPEM(ca) { - return nil, fmt.Errorf("failed to parse CA") - } - - if c.CAOptional { - clientAuth = tls.VerifyClientCertIfGiven - } else { - clientAuth = tls.RequireAndVerifyClientCert - } - } - - cert := tls.Certificate{} - _, errKeyIsFile := os.Stat(c.Key) - - if !c.InsecureSkipVerify && (len(c.Cert) == 0 || len(c.Key) == 0) { - return nil, fmt.Errorf("TLS Certificate or Key file must be set when TLS configuration is created") - } - - if len(c.Cert) > 0 && len(c.Key) > 0 { - if _, errCertIsFile := os.Stat(c.Cert); errCertIsFile == nil { - if errKeyIsFile == nil { - cert, err = tls.LoadX509KeyPair(c.Cert, c.Key) - if err != nil { - return nil, fmt.Errorf("failed to load TLS keypair: %w", err) - } - } else { - return nil, fmt.Errorf("tls cert is a file, but tls key is not") - } - } else { - if errKeyIsFile != nil { - cert, err = tls.X509KeyPair([]byte(c.Cert), []byte(c.Key)) - if err != nil { - return nil, fmt.Errorf("failed to load TLS keypair: %w", err) - } - } else { - return nil, fmt.Errorf("TLS key is a file, but tls cert is not") - } - } - } - - return &tls.Config{ - Certificates: []tls.Certificate{cert}, - RootCAs: caPool, - InsecureSkipVerify: c.InsecureSkipVerify, - ClientAuth: clientAuth, - }, nil -} diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go index fb21edc0f..1f39e6a1f 100644 --- a/pkg/config/dynamic/zz_generated.deepcopy.go +++ b/pkg/config/dynamic/zz_generated.deepcopy.go @@ -124,22 +124,6 @@ func (in *CircuitBreaker) DeepCopy() *CircuitBreaker { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClientTLS) DeepCopyInto(out *ClientTLS) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientTLS. -func (in *ClientTLS) DeepCopy() *ClientTLS { - if in == nil { - return nil - } - out := new(ClientTLS) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Compress) DeepCopyInto(out *Compress) { *out = *in @@ -306,7 +290,7 @@ func (in *ForwardAuth) DeepCopyInto(out *ForwardAuth) { *out = *in if in.TLS != nil { in, out := &in.TLS, &out.TLS - *out = new(ClientTLS) + *out = new(types.ClientTLS) **out = **in } if in.AuthResponseHeaders != nil { @@ -1536,17 +1520,17 @@ func (in *TCPWeightedRoundRobin) DeepCopy() *TCPWeightedRoundRobin { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TLSCLientCertificateDNInfo) DeepCopyInto(out *TLSCLientCertificateDNInfo) { +func (in *TLSClientCertificateDNInfo) DeepCopyInto(out *TLSClientCertificateDNInfo) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSCLientCertificateDNInfo. -func (in *TLSCLientCertificateDNInfo) DeepCopy() *TLSCLientCertificateDNInfo { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSClientCertificateDNInfo. +func (in *TLSClientCertificateDNInfo) DeepCopy() *TLSClientCertificateDNInfo { if in == nil { return nil } - out := new(TLSCLientCertificateDNInfo) + out := new(TLSClientCertificateDNInfo) in.DeepCopyInto(out) return out } @@ -1556,12 +1540,12 @@ func (in *TLSClientCertificateInfo) DeepCopyInto(out *TLSClientCertificateInfo) *out = *in if in.Subject != nil { in, out := &in.Subject, &out.Subject - *out = new(TLSCLientCertificateDNInfo) + *out = new(TLSClientCertificateDNInfo) **out = **in } if in.Issuer != nil { in, out := &in.Issuer, &out.Issuer - *out = new(TLSCLientCertificateDNInfo) + *out = new(TLSClientCertificateDNInfo) **out = **in } return diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go index 3abbd57ea..1a7f459e7 100644 --- a/pkg/config/label/label_test.go +++ b/pkg/config/label/label_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v2/pkg/config/dynamic" + "github.com/traefik/traefik/v2/pkg/types" ) func TestDecodeConfiguration(t *testing.T) { @@ -366,7 +367,7 @@ func TestDecodeConfiguration(t *testing.T) { NotAfter: true, NotBefore: true, SerialNumber: true, - Subject: &dynamic.TLSCLientCertificateDNInfo{ + Subject: &dynamic.TLSClientCertificateDNInfo{ Country: true, Province: true, Locality: true, @@ -375,7 +376,7 @@ func TestDecodeConfiguration(t *testing.T) { SerialNumber: true, DomainComponent: true, }, - Issuer: &dynamic.TLSCLientCertificateDNInfo{ + Issuer: &dynamic.TLSClientCertificateDNInfo{ Country: true, Province: true, Locality: true, @@ -501,7 +502,7 @@ func TestDecodeConfiguration(t *testing.T) { "Middleware7": { ForwardAuth: &dynamic.ForwardAuth{ Address: "foobar", - TLS: &dynamic.ClientTLS{ + TLS: &types.ClientTLS{ CA: "foobar", CAOptional: true, Cert: "foobar", @@ -844,7 +845,7 @@ func TestEncodeConfiguration(t *testing.T) { NotAfter: true, NotBefore: true, SerialNumber: true, - Subject: &dynamic.TLSCLientCertificateDNInfo{ + Subject: &dynamic.TLSClientCertificateDNInfo{ Country: true, Province: true, Locality: true, @@ -853,7 +854,7 @@ func TestEncodeConfiguration(t *testing.T) { SerialNumber: true, DomainComponent: true, }, - Issuer: &dynamic.TLSCLientCertificateDNInfo{ + Issuer: &dynamic.TLSClientCertificateDNInfo{ Country: true, Province: true, Locality: true, @@ -986,7 +987,7 @@ func TestEncodeConfiguration(t *testing.T) { "Middleware7": { ForwardAuth: &dynamic.ForwardAuth{ Address: "foobar", - TLS: &dynamic.ClientTLS{ + TLS: &types.ClientTLS{ CA: "foobar", CAOptional: true, Cert: "foobar", diff --git a/pkg/middlewares/auth/forward.go b/pkg/middlewares/auth/forward.go index df1895f09..a0cee86ce 100644 --- a/pkg/middlewares/auth/forward.go +++ b/pkg/middlewares/auth/forward.go @@ -72,9 +72,9 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu } if config.TLS != nil { - tlsConfig, err := config.TLS.CreateTLSConfig() + tlsConfig, err := config.TLS.CreateTLSConfig(ctx) if err != nil { - return nil, err + return nil, fmt.Errorf("unable to create client TLS configuration: %w", err) } tr := http.DefaultTransport.(*http.Transport).Clone() diff --git a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go index 682836d56..c5f0ad4fd 100644 --- a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go +++ b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go @@ -46,7 +46,7 @@ type DistinguishedNameOptions struct { StateOrProvinceName bool } -func newDistinguishedNameOptions(info *dynamic.TLSCLientCertificateDNInfo) *DistinguishedNameOptions { +func newDistinguishedNameOptions(info *dynamic.TLSClientCertificateDNInfo) *DistinguishedNameOptions { if info == nil { return nil } diff --git a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert_test.go b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert_test.go index 934dbff1a..e85bf81df 100644 --- a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert_test.go +++ b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert_test.go @@ -376,7 +376,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) { desc: "No TLS, with subject info", config: dynamic.PassTLSClientCert{ Info: &dynamic.TLSClientCertificateInfo{ - Subject: &dynamic.TLSCLientCertificateDNInfo{ + Subject: &dynamic.TLSClientCertificateDNInfo{ CommonName: true, Organization: true, Locality: true, @@ -392,7 +392,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) { config: dynamic.PassTLSClientCert{ PEM: false, Info: &dynamic.TLSClientCertificateInfo{ - Subject: &dynamic.TLSCLientCertificateDNInfo{}, + Subject: &dynamic.TLSClientCertificateDNInfo{}, }, }, }, @@ -405,7 +405,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) { NotBefore: true, Sans: true, SerialNumber: true, - Subject: &dynamic.TLSCLientCertificateDNInfo{ + Subject: &dynamic.TLSClientCertificateDNInfo{ CommonName: true, Country: true, DomainComponent: true, @@ -414,7 +414,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) { Province: true, SerialNumber: true, }, - Issuer: &dynamic.TLSCLientCertificateDNInfo{ + Issuer: &dynamic.TLSClientCertificateDNInfo{ CommonName: true, Country: true, DomainComponent: true, @@ -434,10 +434,10 @@ func TestPassTLSClientCert_certInfo(t *testing.T) { Info: &dynamic.TLSClientCertificateInfo{ NotAfter: true, Sans: true, - Subject: &dynamic.TLSCLientCertificateDNInfo{ + Subject: &dynamic.TLSClientCertificateDNInfo{ Organization: true, }, - Issuer: &dynamic.TLSCLientCertificateDNInfo{ + Issuer: &dynamic.TLSClientCertificateDNInfo{ Country: true, }, }, @@ -453,7 +453,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) { NotBefore: true, Sans: true, SerialNumber: true, - Subject: &dynamic.TLSCLientCertificateDNInfo{ + Subject: &dynamic.TLSClientCertificateDNInfo{ Country: true, Province: true, Locality: true, @@ -462,7 +462,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) { SerialNumber: true, DomainComponent: true, }, - Issuer: &dynamic.TLSCLientCertificateDNInfo{ + Issuer: &dynamic.TLSClientCertificateDNInfo{ Country: true, Province: true, Locality: true, @@ -484,7 +484,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) { NotBefore: true, Sans: true, SerialNumber: true, - Subject: &dynamic.TLSCLientCertificateDNInfo{ + Subject: &dynamic.TLSClientCertificateDNInfo{ Country: true, Province: true, Locality: true, @@ -493,7 +493,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) { SerialNumber: true, DomainComponent: true, }, - Issuer: &dynamic.TLSCLientCertificateDNInfo{ + Issuer: &dynamic.TLSClientCertificateDNInfo{ Country: true, Province: true, Locality: true, diff --git a/pkg/provider/docker/docker.go b/pkg/provider/docker/docker.go index 10ba1709c..6f87cfcd8 100644 --- a/pkg/provider/docker/docker.go +++ b/pkg/provider/docker/docker.go @@ -165,7 +165,7 @@ func (p *Provider) getClientOpts() ([]client.Opt, error) { conf, err := p.TLS.CreateTLSConfig(ctx) if err != nil { - return nil, err + return nil, fmt.Errorf("unable to create client TLS configuration: %w", err) } hostURL, err := client.ParseHostURL(p.Endpoint) diff --git a/pkg/provider/http/http.go b/pkg/provider/http/http.go index 8d1ce329e..c7caf7fdb 100644 --- a/pkg/provider/http/http.go +++ b/pkg/provider/http/http.go @@ -55,7 +55,7 @@ func (p *Provider) Init() error { if p.TLS != nil { tlsConfig, err := p.TLS.CreateTLSConfig(context.Background()) if err != nil { - return fmt.Errorf("unable to create TLS configuration: %w", err) + return fmt.Errorf("unable to create client TLS configuration: %w", err) } p.httpClient.Transport = &http.Transport{ diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index 5671e586b..37c56952e 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -23,6 +23,7 @@ import ( "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1" "github.com/traefik/traefik/v2/pkg/safe" "github.com/traefik/traefik/v2/pkg/tls" + "github.com/traefik/traefik/v2/pkg/types" corev1 "k8s.io/api/core/v1" apiextensionv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/labels" @@ -481,7 +482,7 @@ func createForwardAuthMiddleware(k8sClient Client, namespace string, auth *v1alp return forwardAuth, nil } - forwardAuth.TLS = &dynamic.ClientTLS{ + forwardAuth.TLS = &types.ClientTLS{ CAOptional: auth.TLS.CAOptional, InsecureSkipVerify: auth.TLS.InsecureSkipVerify, } diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index 310ae48bd..a84e2f540 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -9,13 +9,14 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/traefik/paerser/types" + ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v2/pkg/config/dynamic" "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/types" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" @@ -3243,7 +3244,7 @@ func TestLoadIngressRoutes(t *testing.T) { "default-forwardauth": { ForwardAuth: &dynamic.ForwardAuth{ Address: "test.com", - TLS: &dynamic.ClientTLS{ + TLS: &types.ClientTLS{ CA: "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----", Cert: "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----", Key: "-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----", @@ -3612,17 +3613,17 @@ func TestLoadIngressRoutes(t *testing.T) { MaxIdleConnsPerHost: 42, DisableHTTP2: true, ForwardingTimeouts: &dynamic.ForwardingTimeouts{ - DialTimeout: types.Duration(42 * time.Second), - ResponseHeaderTimeout: types.Duration(42 * time.Second), - IdleConnTimeout: types.Duration(42 * time.Millisecond), + DialTimeout: ptypes.Duration(42 * time.Second), + ResponseHeaderTimeout: ptypes.Duration(42 * time.Second), + IdleConnTimeout: ptypes.Duration(42 * time.Millisecond), }, PeerCertURI: "foo://bar", }, "default-test": { ServerName: "test", ForwardingTimeouts: &dynamic.ForwardingTimeouts{ - DialTimeout: types.Duration(30 * time.Second), - IdleConnTimeout: types.Duration(90 * time.Second), + DialTimeout: ptypes.Duration(30 * time.Second), + IdleConnTimeout: ptypes.Duration(90 * time.Second), }, }, }, diff --git a/pkg/provider/kv/kv.go b/pkg/provider/kv/kv.go index d62b142c5..4eee4ab63 100644 --- a/pkg/provider/kv/kv.go +++ b/pkg/provider/kv/kv.go @@ -170,7 +170,7 @@ func (p *Provider) createKVClient(ctx context.Context) (store.Store, error) { var err error storeConfig.TLS, err = p.TLS.CreateTLSConfig(ctx) if err != nil { - return nil, err + return nil, fmt.Errorf("unable to create client TLS configuration: %w", err) } } diff --git a/pkg/provider/kv/kv_test.go b/pkg/provider/kv/kv_test.go index ef6fde11c..f1a50ef06 100644 --- a/pkg/provider/kv/kv_test.go +++ b/pkg/provider/kv/kv_test.go @@ -399,7 +399,7 @@ func Test_buildConfiguration(t *testing.T) { "Middleware08": { ForwardAuth: &dynamic.ForwardAuth{ Address: "foobar", - TLS: &dynamic.ClientTLS{ + TLS: &types.ClientTLS{ CA: "foobar", CAOptional: true, Cert: "foobar", @@ -478,7 +478,7 @@ func Test_buildConfiguration(t *testing.T) { NotAfter: true, NotBefore: true, Sans: true, - Subject: &dynamic.TLSCLientCertificateDNInfo{ + Subject: &dynamic.TLSClientCertificateDNInfo{ Country: true, Province: true, Locality: true, @@ -487,7 +487,7 @@ func Test_buildConfiguration(t *testing.T) { SerialNumber: true, DomainComponent: true, }, - Issuer: &dynamic.TLSCLientCertificateDNInfo{ + Issuer: &dynamic.TLSClientCertificateDNInfo{ Country: true, Province: true, Locality: true, diff --git a/pkg/provider/marathon/marathon.go b/pkg/provider/marathon/marathon.go index d0405b6db..c78413ad7 100644 --- a/pkg/provider/marathon/marathon.go +++ b/pkg/provider/marathon/marathon.go @@ -134,7 +134,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe. } TLSConfig, err := p.TLS.CreateTLSConfig(ctx) if err != nil { - return err + return fmt.Errorf("unable to create client TLS configuration: %w", err) } confg.HTTPClient = &http.Client{ Transport: &http.Transport{ diff --git a/pkg/types/fixtures/cert.pem b/pkg/types/fixtures/cert.pem new file mode 100644 index 000000000..b0261ccd6 --- /dev/null +++ b/pkg/types/fixtures/cert.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB9jCCAV+gAwIBAgIQI3edJckNbicw4WIHs5Ws9TANBgkqhkiG9w0BAQsFADAS +MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw +MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQCb8oWyME1QRBoMLFei3M8TVKwfZfW74cVjtcugCBMTTOTCouEIgjjmiMv6 +FdMio2uBcgeD9R3dOtjjnA7N+xjwZ4vIPqDlJRE3YbfpV9igVX3sXU7ssHTSH0vs +R0TuYJwGReIFUnu5QIjGwVorodF+CQ8dTnyXVLeQVU9kvjohHwIDAQABo0swSTAO +BgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIw +ADAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADgYEADqylUQ/4 +lrxh4h8UUQ2wKATQ2kG2YvMGlaIhr2vPZo2QDBlmL2xzai7YXX3+JZyM15TNCamn +WtFR7WQIOHzKA1GkR9WkaXKmFbJjhGMSZVCG6ghhTjzB+stBYZXhBsdjCJbkZWBu +OeI73oivo0MdI+4iCYCo7TnoY4PZGObwcgI= +-----END CERTIFICATE----- diff --git a/pkg/types/fixtures/key.pem b/pkg/types/fixtures/key.pem new file mode 100644 index 000000000..fd1d7e728 --- /dev/null +++ b/pkg/types/fixtures/key.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJvyhbIwTVBEGgws +V6LczxNUrB9l9bvhxWO1y6AIExNM5MKi4QiCOOaIy/oV0yKja4FyB4P1Hd062OOc +Ds37GPBni8g+oOUlETdht+lX2KBVfexdTuywdNIfS+xHRO5gnAZF4gVSe7lAiMbB +Wiuh0X4JDx1OfJdUt5BVT2S+OiEfAgMBAAECgYA9+PbghQl0aFvhko2RDybLi86K ++73X2DTVFx3AjvTlqp0OLCQ5eWabVqmYzKuHDGJgoqwR6Irhq80dRpsriCm0YNui +mMV35bbimOKz9FoCTKx0ZB6xsqrVoFhjVmX3DOD9Txe41H42ZxmccOKZndR/QaXz +VV+1W/Wbz2VawnkyYQJBAMvF6w2eOJRRoN8e7GM7b7uqkupJPp9axgFREoJZb16W +mqXUZnH4Cydzc5keG4yknQRHdgz6RrQxnvR7GyKHLfUCQQDD6qG9D5BX0+mNW6TG +PRwW/L2qWgnmg9lxtSSQat9ZOnBhw2OLPi0zTu4p70oSmU67/YJr50HEoJpRccZJ +mnJDAkBdBTtY2xpe8qhqUjZ80hweYi5wzwDMQ+bRoQ2+/U6usjdkbgJaEm4dE0H4 +6tqOqHKZCnokUHfIOEKkvjHT4DulAkBAgiJNSTGi6aDOLa28pGR6YS/mRo1Z/HH9 +kcJ/VuFB1Q8p8Zb2QzvI2CVtY2AFbbtSBPALrXKnVqZZSNgcZiFXAkEAvcLKaEXE +haGMGwq2BLADPHqAR3hdCJL3ikMJwWUsTkTjm973iEIEZfF5j57EzRI4bASm4Zq5 +Zt3BcblLODQ//w== +-----END PRIVATE KEY----- diff --git a/pkg/types/tls.go b/pkg/types/tls.go index 8760a7217..3867d8097 100644 --- a/pkg/types/tls.go +++ b/pkg/types/tls.go @@ -4,12 +4,15 @@ import ( "context" "crypto/tls" "crypto/x509" + "errors" "fmt" "os" "github.com/traefik/traefik/v2/pkg/log" ) +// +k8s:deepcopy-gen=true + // ClientTLS holds TLS specific configurations as client // CA, Cert and Key can be either path or file contents. type ClientTLS struct { @@ -42,7 +45,7 @@ func (clientTLS *ClientTLS) CreateTLSConfig(ctx context.Context) (*tls.Config, e } if !caPool.AppendCertsFromPEM(ca) { - return nil, fmt.Errorf("failed to parse CA") + return nil, errors.New("failed to parse CA") } if clientTLS.CAOptional { @@ -52,34 +55,24 @@ func (clientTLS *ClientTLS) CreateTLSConfig(ctx context.Context) (*tls.Config, e } } - if !clientTLS.InsecureSkipVerify && (len(clientTLS.Cert) == 0 || len(clientTLS.Key) == 0) { - return nil, fmt.Errorf("TLS Certificate or Key file must be set when TLS configuration is created") + hasCert := len(clientTLS.Cert) > 0 + hasKey := len(clientTLS.Key) > 0 + + if hasCert != hasKey { + return nil, errors.New("both TLS cert and key must be defined") } - cert := tls.Certificate{} - _, errKeyIsFile := os.Stat(clientTLS.Key) + if !hasCert || !hasKey { + return &tls.Config{ + RootCAs: caPool, + InsecureSkipVerify: clientTLS.InsecureSkipVerify, + ClientAuth: clientAuth, + }, nil + } - if len(clientTLS.Cert) > 0 && len(clientTLS.Key) > 0 { - var err error - if _, errCertIsFile := os.Stat(clientTLS.Cert); errCertIsFile == nil { - if errKeyIsFile == nil { - cert, err = tls.LoadX509KeyPair(clientTLS.Cert, clientTLS.Key) - if err != nil { - return nil, fmt.Errorf("failed to load TLS keypair: %w", err) - } - } else { - return nil, fmt.Errorf("TLS cert is a file, but tls key is not") - } - } else { - if errKeyIsFile != nil { - cert, err = tls.X509KeyPair([]byte(clientTLS.Cert), []byte(clientTLS.Key)) - if err != nil { - return nil, fmt.Errorf("failed to load TLS keypair: %w", err) - } - } else { - return nil, fmt.Errorf("TLS key is a file, but tls cert is not") - } - } + cert, err := loadKeyPair(clientTLS.Cert, clientTLS.Key) + if err != nil { + return nil, err } return &tls.Config{ @@ -89,3 +82,27 @@ func (clientTLS *ClientTLS) CreateTLSConfig(ctx context.Context) (*tls.Config, e ClientAuth: clientAuth, }, nil } + +func loadKeyPair(cert, key string) (tls.Certificate, error) { + keyPair, err := tls.X509KeyPair([]byte(cert), []byte(key)) + if err == nil { + return keyPair, nil + } + + _, err = os.Stat(cert) + if err != nil { + return tls.Certificate{}, errors.New("cert file does not exist") + } + + _, err = os.Stat(key) + if err != nil { + return tls.Certificate{}, errors.New("key file does not exist") + } + + keyPair, err = tls.LoadX509KeyPair(cert, key) + if err != nil { + return tls.Certificate{}, err + } + + return keyPair, nil +} diff --git a/pkg/types/tls_test.go b/pkg/types/tls_test.go new file mode 100644 index 000000000..dbc1e6334 --- /dev/null +++ b/pkg/types/tls_test.go @@ -0,0 +1,123 @@ +package types + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// go run $GOROOT/src/crypto/tls/generate_cert.go --rsa-bits 1024 --host localhost --start-date "Jan 1 00:00:00 1970" --duration=1000000h +var cert = `-----BEGIN CERTIFICATE----- +MIIB9jCCAV+gAwIBAgIQI3edJckNbicw4WIHs5Ws9TANBgkqhkiG9w0BAQsFADAS +MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw +MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQCb8oWyME1QRBoMLFei3M8TVKwfZfW74cVjtcugCBMTTOTCouEIgjjmiMv6 +FdMio2uBcgeD9R3dOtjjnA7N+xjwZ4vIPqDlJRE3YbfpV9igVX3sXU7ssHTSH0vs +R0TuYJwGReIFUnu5QIjGwVorodF+CQ8dTnyXVLeQVU9kvjohHwIDAQABo0swSTAO +BgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIw +ADAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADgYEADqylUQ/4 +lrxh4h8UUQ2wKATQ2kG2YvMGlaIhr2vPZo2QDBlmL2xzai7YXX3+JZyM15TNCamn +WtFR7WQIOHzKA1GkR9WkaXKmFbJjhGMSZVCG6ghhTjzB+stBYZXhBsdjCJbkZWBu +OeI73oivo0MdI+4iCYCo7TnoY4PZGObwcgI= +-----END CERTIFICATE-----` + +var key = `-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJvyhbIwTVBEGgws +V6LczxNUrB9l9bvhxWO1y6AIExNM5MKi4QiCOOaIy/oV0yKja4FyB4P1Hd062OOc +Ds37GPBni8g+oOUlETdht+lX2KBVfexdTuywdNIfS+xHRO5gnAZF4gVSe7lAiMbB +Wiuh0X4JDx1OfJdUt5BVT2S+OiEfAgMBAAECgYA9+PbghQl0aFvhko2RDybLi86K ++73X2DTVFx3AjvTlqp0OLCQ5eWabVqmYzKuHDGJgoqwR6Irhq80dRpsriCm0YNui +mMV35bbimOKz9FoCTKx0ZB6xsqrVoFhjVmX3DOD9Txe41H42ZxmccOKZndR/QaXz +VV+1W/Wbz2VawnkyYQJBAMvF6w2eOJRRoN8e7GM7b7uqkupJPp9axgFREoJZb16W +mqXUZnH4Cydzc5keG4yknQRHdgz6RrQxnvR7GyKHLfUCQQDD6qG9D5BX0+mNW6TG +PRwW/L2qWgnmg9lxtSSQat9ZOnBhw2OLPi0zTu4p70oSmU67/YJr50HEoJpRccZJ +mnJDAkBdBTtY2xpe8qhqUjZ80hweYi5wzwDMQ+bRoQ2+/U6usjdkbgJaEm4dE0H4 +6tqOqHKZCnokUHfIOEKkvjHT4DulAkBAgiJNSTGi6aDOLa28pGR6YS/mRo1Z/HH9 +kcJ/VuFB1Q8p8Zb2QzvI2CVtY2AFbbtSBPALrXKnVqZZSNgcZiFXAkEAvcLKaEXE +haGMGwq2BLADPHqAR3hdCJL3ikMJwWUsTkTjm973iEIEZfF5j57EzRI4bASm4Zq5 +Zt3BcblLODQ//w== +-----END PRIVATE KEY-----` + +func TestClientTLS_CreateTLSConfig(t *testing.T) { + tests := []struct { + desc string + clientTLS ClientTLS + wantCertLen int + wantCALen int + wantErr bool + }{ + { + desc: "Configure CA", + clientTLS: ClientTLS{CA: cert}, + wantCALen: 1, + wantErr: false, + }, + { + desc: "Configure the client keyPair from strings", + clientTLS: ClientTLS{Cert: cert, Key: key}, + wantCertLen: 1, + wantErr: false, + }, + { + desc: "Configure the client keyPair from files", + clientTLS: ClientTLS{Cert: "fixtures/cert.pem", Key: "fixtures/key.pem"}, + wantCertLen: 1, + wantErr: false, + }, + { + desc: "Configure InsecureSkipVerify", + clientTLS: ClientTLS{InsecureSkipVerify: true}, + wantErr: false, + }, + { + desc: "Return an error if only the client cert is provided", + clientTLS: ClientTLS{Cert: cert}, + wantErr: true, + }, + { + desc: "Return an error if only the client key is provided", + clientTLS: ClientTLS{Key: key}, + wantErr: true, + }, + { + desc: "Return an error if only the client cert is of type file", + clientTLS: ClientTLS{Cert: "fixtures/cert.pem", Key: key}, + wantErr: true, + }, + { + desc: "Return an error if only the client key is of type file", + clientTLS: ClientTLS{Cert: cert, Key: "fixtures/key.pem"}, + wantErr: true, + }, + { + desc: "Return an error if the client cert does not exist", + clientTLS: ClientTLS{Cert: "fixtures/cert2.pem", Key: "fixtures/key.pem"}, + wantErr: true, + }, + { + desc: "Return an error if the client key does not exist", + clientTLS: ClientTLS{Cert: "fixtures/cert.pem", Key: "fixtures/key2.pem"}, + wantErr: true, + }, + } + + for _, test := range tests { + test := test + + t.Run(test.desc, func(t *testing.T) { + tlsConfig, err := test.clientTLS.CreateTLSConfig(context.Background()) + if test.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + + assert.Len(t, tlsConfig.RootCAs.Subjects(), test.wantCALen) + assert.Len(t, tlsConfig.Certificates, test.wantCertLen) + assert.Equal(t, test.clientTLS.InsecureSkipVerify, tlsConfig.InsecureSkipVerify) + }) + } +} diff --git a/pkg/types/zz_generated.deepcopy.go b/pkg/types/zz_generated.deepcopy.go index 06daf6fa7..91c7716b3 100644 --- a/pkg/types/zz_generated.deepcopy.go +++ b/pkg/types/zz_generated.deepcopy.go @@ -29,6 +29,22 @@ THE SOFTWARE. package types +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClientTLS) DeepCopyInto(out *ClientTLS) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientTLS. +func (in *ClientTLS) DeepCopy() *ClientTLS { + if in == nil { + return nil + } + out := new(ClientTLS) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Domain) DeepCopyInto(out *Domain) { *out = *in From 56ed45ae70fe5a38395a86a4ced64b942d561973 Mon Sep 17 00:00:00 2001 From: Kevin Pollet Date: Tue, 26 Oct 2021 12:08:12 +0200 Subject: [PATCH 10/20] docs: remove non-working kind config in IngressRouteTCP/UDP examples --- docs/content/routing/providers/kubernetes-crd.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index 56b93dfc4..239aec169 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -131,7 +131,6 @@ The Kubernetes Ingress Controller, The Custom Resource Way. - tcpep routes: - match: HostSNI(`bar`) - kind: Rule services: - name: whoamitcp port: 8080 @@ -147,8 +146,7 @@ The Kubernetes Ingress Controller, The Custom Resource Way. entryPoints: - udpep routes: - - kind: Rule - services: + - services: - name: whoamiudp port: 8080 ``` @@ -1224,7 +1222,6 @@ Register the `IngressRouteTCP` [kind](../../reference/dynamic-configuration/kube routes: - match: HostSNI(`*`) - kind: Rule services: - name: external-svc port: 80 @@ -1254,7 +1251,6 @@ Register the `IngressRouteTCP` [kind](../../reference/dynamic-configuration/kube routes: - match: HostSNI(`*`) - kind: Rule services: - name: external-svc port: 80 From e03335522509e83fef2e2d039d7971c2df655417 Mon Sep 17 00:00:00 2001 From: Tom Moulard Date: Wed, 27 Oct 2021 10:34:05 +0200 Subject: [PATCH 11/20] fix: do not validate shell script in node-modules folder --- script/validate-shell-script.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/validate-shell-script.sh b/script/validate-shell-script.sh index befaebe59..5f785128d 100755 --- a/script/validate-shell-script.sh +++ b/script/validate-shell-script.sh @@ -12,10 +12,10 @@ then # The shellcheck command are run in background, to have an overview of the linter (instead of a fail at first issue) shellcheck "${script_to_check}" & done < <( # Search all the repository for sh and bash shebangs, excluding .js and .md files - # the folders ".git" and "vendor" are also ignored + # the folders ".git", "vendor" and "node_modules" are also ignored grep -rI '#!/' "${script_dir}"/.. \ | grep 'sh' | grep -v '.js' | grep -v '.md' \ - | grep -v '.git/' | grep -v 'vendor/' \ + | grep -v '.git/' | grep -v 'vendor/' | grep -v 'node_modules/' \ | cut -d':' -f1 ) wait # Wait for all background command to be completed From 20dfb91948c38c2c8d8ec17cdbf8f41a8ea2bbb4 Mon Sep 17 00:00:00 2001 From: Marc Bihlmaier <30314040+rdxmb@users.noreply.github.com> Date: Thu, 28 Oct 2021 18:14:14 +0200 Subject: [PATCH 12/20] docs: remove quotes in certificatesresolvers CLI examples --- docs/content/https/acme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/https/acme.md b/docs/content/https/acme.md index 0f4bf8472..38b25746f 100644 --- a/docs/content/https/acme.md +++ b/docs/content/https/acme.md @@ -560,7 +560,7 @@ certificatesResolvers: ```bash tab="CLI" # ... ---certificatesresolvers.myresolver.acme.preferredChain="ISRG Root X1" +--certificatesresolvers.myresolver.acme.preferredChain=ISRG Root X1 # ... ``` @@ -588,7 +588,7 @@ certificatesResolvers: ```bash tab="CLI" # ... ---certificatesresolvers.myresolver.acme.keyType="RSA4096" +--certificatesresolvers.myresolver.acme.keyType=RSA4096 # ... ``` From b39d226fb87becc42c4006a5fd4f9b3b0e6dcb39 Mon Sep 17 00:00:00 2001 From: Kevin Pollet Date: Wed, 3 Nov 2021 17:38:07 +0100 Subject: [PATCH 13/20] fix: use host's root CA set if ClientTLS ca is not defined Co-authored-by: Tom Moulard --- docs/content/middlewares/http/forwardauth.md | 3 ++- docs/content/providers/consul-catalog.md | 3 ++- docs/content/providers/consul.md | 3 ++- docs/content/providers/docker.md | 3 ++- docs/content/providers/etcd.md | 3 ++- docs/content/providers/http.md | 3 ++- docs/content/providers/kubernetes-crd.md | 2 +- docs/content/providers/kubernetes-ingress.md | 2 +- docs/content/providers/marathon.md | 3 ++- docs/content/providers/redis.md | 3 ++- docs/content/providers/zookeeper.md | 3 ++- pkg/types/tls.go | 5 ++++- pkg/types/tls_test.go | 8 +++++++- 13 files changed, 31 insertions(+), 13 deletions(-) diff --git a/docs/content/middlewares/http/forwardauth.md b/docs/content/middlewares/http/forwardauth.md index f56fd320f..9b21e0279 100644 --- a/docs/content/middlewares/http/forwardauth.md +++ b/docs/content/middlewares/http/forwardauth.md @@ -353,7 +353,8 @@ The `tls` option is the TLS configuration from Traefik to the authentication ser #### `tls.ca` -Certificate Authority used for the secured connection to the authentication server. +Certificate Authority used for the secured connection to the authentication server, +defaults to the system bundle. ```yaml tab="Docker" labels: diff --git a/docs/content/providers/consul-catalog.md b/docs/content/providers/consul-catalog.md index bfadd47a5..1e4a1bedb 100644 --- a/docs/content/providers/consul-catalog.md +++ b/docs/content/providers/consul-catalog.md @@ -368,7 +368,8 @@ Defines TLS options for Consul server endpoint. _Optional_ -`ca` is the path to the CA certificate used for Consul communication, defaults to the system bundle if not specified. +Certificate Authority used for the secure connection to Consul, +defaults to the system bundle. ```yaml tab="File (YAML)" providers: diff --git a/docs/content/providers/consul.md b/docs/content/providers/consul.md index ab488ef3f..d101c7e69 100644 --- a/docs/content/providers/consul.md +++ b/docs/content/providers/consul.md @@ -106,7 +106,8 @@ _Optional_ #### `tls.ca` -Certificate Authority used for the secure connection to Consul. +Certificate Authority used for the secure connection to Consul, +defaults to the system bundle. ```yaml tab="File (YAML)" providers: diff --git a/docs/content/providers/docker.md b/docs/content/providers/docker.md index e354599e1..f69401e4d 100644 --- a/docs/content/providers/docker.md +++ b/docs/content/providers/docker.md @@ -615,7 +615,8 @@ _Optional_ #### `tls.ca` -Certificate Authority used for the secure connection to Docker. +Certificate Authority used for the secure connection to Docker, +defaults to the system bundle. ```yaml tab="File (YAML)" providers: diff --git a/docs/content/providers/etcd.md b/docs/content/providers/etcd.md index 46e61f4fc..8df549ca3 100644 --- a/docs/content/providers/etcd.md +++ b/docs/content/providers/etcd.md @@ -106,7 +106,8 @@ _Optional_ #### `tls.ca` -Certificate Authority used for the secure connection to etcd. +Certificate Authority used for the secure connection to etcd, +defaults to the system bundle. ```yaml tab="File (YAML)" providers: diff --git a/docs/content/providers/http.md b/docs/content/providers/http.md index 5c611cef9..f70c2d059 100644 --- a/docs/content/providers/http.md +++ b/docs/content/providers/http.md @@ -78,7 +78,8 @@ _Optional_ #### `tls.ca` -Certificate Authority used for the secure connection to the configured endpoint. +Certificate Authority used for the secure connection to the configured endpoint, +defaults to the system bundle. ```yaml tab="File (YAML)" providers: diff --git a/docs/content/providers/kubernetes-crd.md b/docs/content/providers/kubernetes-crd.md index 7276e391d..89dd02e6b 100644 --- a/docs/content/providers/kubernetes-crd.md +++ b/docs/content/providers/kubernetes-crd.md @@ -62,7 +62,7 @@ Previous versions of Traefik used a [KV store](https://doc.traefik.io/traefik/v1 If you need Let's Encrypt with HA in a Kubernetes environment, we recommend using [Traefik Enterprise](https://traefik.io/traefik-enterprise/), which includes distributed Let's Encrypt as a supported feature. -If you want to keep using Traefik Proxy, high availability for Let's Encrypt can be achieved by using a Certificate Controller such as [Cert-Manager](https://docs.cert-manager.io/en/latest/index.html). +If you want to keep using Traefik Proxy, high availability for Let's Encrypt can be achieved by using a Certificate Controller such as [Cert-Manager](https://cert-manager.io/docs/). When using Cert-Manager to manage certificates, it creates secrets in your namespaces that can be referenced as TLS secrets in your [ingress objects](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls). When using the Traefik Kubernetes CRD Provider, unfortunately Cert-Manager cannot yet interface directly with the CRDs. A workaround is to enable the [Kubernetes Ingress provider](./kubernetes-ingress.md) to allow Cert-Manager to create ingress objects to complete the challenges. diff --git a/docs/content/providers/kubernetes-ingress.md b/docs/content/providers/kubernetes-ingress.md index bfbf0cff6..659f6a3d8 100644 --- a/docs/content/providers/kubernetes-ingress.md +++ b/docs/content/providers/kubernetes-ingress.md @@ -104,7 +104,7 @@ If you need Let's Encrypt with high availability in a Kubernetes environment, we recommend using [Traefik Enterprise](https://traefik.io/traefik-enterprise/) which includes distributed Let's Encrypt as a supported feature. If you want to keep using Traefik Proxy, -LetsEncrypt HA can be achieved by using a Certificate Controller such as [Cert-Manager](https://docs.cert-manager.io/en/latest/index.html). +LetsEncrypt HA can be achieved by using a Certificate Controller such as [Cert-Manager](https://cert-manager.io/docs/). When using Cert-Manager to manage certificates, it creates secrets in your namespaces that can be referenced as TLS secrets in your [ingress objects](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls). diff --git a/docs/content/providers/marathon.md b/docs/content/providers/marathon.md index 769d2ee3c..290aa500c 100644 --- a/docs/content/providers/marathon.md +++ b/docs/content/providers/marathon.md @@ -406,7 +406,8 @@ _Optional_ #### `tls.ca` -Certificate Authority used for the secure connection to Marathon. +Certificate Authority used for the secure connection to Marathon, +defaults to the system bundle. ```yaml tab="File (YAML)" providers: diff --git a/docs/content/providers/redis.md b/docs/content/providers/redis.md index 61fa801d0..70607d2ae 100644 --- a/docs/content/providers/redis.md +++ b/docs/content/providers/redis.md @@ -106,7 +106,8 @@ _Optional_ #### `tls.ca` -Certificate Authority used for the secure connection to Redis. +Certificate Authority used for the secure connection to Redis, +defaults to the system bundle. ```yaml tab="File (YAML)" providers: diff --git a/docs/content/providers/zookeeper.md b/docs/content/providers/zookeeper.md index ffb343b81..b89daa6bf 100644 --- a/docs/content/providers/zookeeper.md +++ b/docs/content/providers/zookeeper.md @@ -106,7 +106,8 @@ _Optional_ #### `tls.ca` -Certificate Authority used for the secure connection to ZooKeeper. +Certificate Authority used for the secure connection to ZooKeeper, +defaults to the system bundle. ```yaml tab="File (YAML)" providers: diff --git a/pkg/types/tls.go b/pkg/types/tls.go index 3867d8097..c701b6b7e 100644 --- a/pkg/types/tls.go +++ b/pkg/types/tls.go @@ -30,7 +30,9 @@ func (clientTLS *ClientTLS) CreateTLSConfig(ctx context.Context) (*tls.Config, e return nil, nil } - caPool := x509.NewCertPool() + // Not initialized, to rely on system bundle. + var caPool *x509.CertPool + clientAuth := tls.NoClientCert if clientTLS.CA != "" { var ca []byte @@ -44,6 +46,7 @@ func (clientTLS *ClientTLS) CreateTLSConfig(ctx context.Context) (*tls.Config, e ca = []byte(clientTLS.CA) } + caPool = x509.NewCertPool() if !caPool.AppendCertsFromPEM(ca) { return nil, errors.New("failed to parse CA") } diff --git a/pkg/types/tls_test.go b/pkg/types/tls_test.go index dbc1e6334..d3e93f408 100644 --- a/pkg/types/tls_test.go +++ b/pkg/types/tls_test.go @@ -115,9 +115,15 @@ func TestClientTLS_CreateTLSConfig(t *testing.T) { require.NoError(t, err) - assert.Len(t, tlsConfig.RootCAs.Subjects(), test.wantCALen) assert.Len(t, tlsConfig.Certificates, test.wantCertLen) assert.Equal(t, test.clientTLS.InsecureSkipVerify, tlsConfig.InsecureSkipVerify) + + if test.wantCALen > 0 { + assert.Len(t, tlsConfig.RootCAs.Subjects(), test.wantCALen) + return + } + + assert.Nil(t, tlsConfig.RootCAs) }) } } From 596f04eae88488cf444118880993d74fd049ae18 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Thu, 4 Nov 2021 09:50:11 +0100 Subject: [PATCH 14/20] chore: update linter --- .github/workflows/validate.yaml | 2 +- .golangci.toml | 4 ++++ .semaphore/semaphore.yml | 2 +- build.Dockerfile | 2 +- pkg/middlewares/accesslog/logger_formatters_test.go | 3 +-- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index da4009c91..900c1d936 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.42.1 + GOLANGCI_LINT_VERSION: v1.43.0 MISSSPELL_VERSION: v0.3.4 PRE_TARGET: "" diff --git a/.golangci.toml b/.golangci.toml index 9caed7360..904ed4446 100644 --- a/.golangci.toml +++ b/.golangci.toml @@ -96,6 +96,10 @@ "godox", # Too strict "forcetypeassert", # Too strict "tagliatelle", # Not compatible with current tags. + "varnamelen", # not relevant + "nilnil", # not relevant + "ireturn", # not relevant + "contextcheck", # too many false-positive ] [issues] diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 30d7cd1f5..8e10e8da9 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.42.1 + - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOPATH}/bin" v1.43.0 - 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 30cc0056f..1ca6789ee 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.42.1 +RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.43.0 # 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/pkg/middlewares/accesslog/logger_formatters_test.go b/pkg/middlewares/accesslog/logger_formatters_test.go index 1a7f69b5a..74a86b2a4 100644 --- a/pkg/middlewares/accesslog/logger_formatters_test.go +++ b/pkg/middlewares/accesslog/logger_formatters_test.go @@ -2,7 +2,6 @@ package accesslog import ( "net/http" - "os" "testing" "time" @@ -84,7 +83,7 @@ func TestCommonLogFormatter_Format(t *testing.T) { } // Set timezone to Etc/GMT+9 to have a constant behavior - os.Setenv("TZ", "Etc/GMT+9") + t.Setenv("TZ", "Etc/GMT+9") for _, test := range testCases { test := test From bda7e025a2cc84806fb0921453a1055acd855974 Mon Sep 17 00:00:00 2001 From: CrispyBaguette Date: Fri, 5 Nov 2021 17:28:06 +0100 Subject: [PATCH 15/20] docs: remove link to microbadger.com --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 120806bc8..c51d744ee 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ _(But if you'd rather configure some of your routes manually, Traefik supports t - Keeps access logs (JSON, CLF) - Fast - Exposes a Rest API -- Packaged as a single binary file (made with :heart: with go) and available as a [tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image +- Packaged as a single binary file (made with :heart: with go) and available as an [official](https://hub.docker.com/r/_/traefik/) docker image ## Supported Backends From 69d504c9058b2942f127667b8409af6e02e96cde Mon Sep 17 00:00:00 2001 From: Tom Moulard Date: Fri, 5 Nov 2021 18:02:05 +0100 Subject: [PATCH 16/20] fix: git ignore webui/static/ --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5d3608c1f..3a911837a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ .DS_Store /dist /webui/.tmp/ +/webui/static/ /site/ /docs/site/ /static/ From a13b03ef3dbccbed25cd1d657e52cbdcd52283ac Mon Sep 17 00:00:00 2001 From: kerrsmith <12290040+kerrsmith@users.noreply.github.com> Date: Mon, 8 Nov 2021 09:06:05 +0000 Subject: [PATCH 17/20] docs: add named groups details to Regexp Syntax section --- docs/content/routing/routers/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/content/routing/routers/index.md b/docs/content/routing/routers/index.md index ef221ad0a..c0508c16c 100644 --- a/docs/content/routing/routers/index.md +++ b/docs/content/routing/routers/index.md @@ -251,6 +251,7 @@ The table below lists all the available matchers: `HostRegexp` and `Path` accept an expression with zero or more groups enclosed by curly braces. Named groups can be like `{name:pattern}` that matches the given regexp pattern or like `{name}` that matches anything until the next dot. + The group name (`name` is the above examples) is an arbitrary value. Any pattern supported by [Go's regexp package](https://golang.org/pkg/regexp/) may be used (example: `{subdomain:[a-z]+}.{domain}.com`). !!! info "Combining Matchers Using Operators and Parenthesis" From ffdfc1346121646a83eb78a95a59591f0181f187 Mon Sep 17 00:00:00 2001 From: Kevin Pollet Date: Mon, 8 Nov 2021 13:32:10 +0100 Subject: [PATCH 18/20] docs: fix typo in addRoutersLabels option title --- docs/content/observability/metrics/datadog.md | 2 +- docs/content/observability/metrics/influxdb.md | 2 +- docs/content/observability/metrics/prometheus.md | 2 +- docs/content/observability/metrics/statsd.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/content/observability/metrics/datadog.md b/docs/content/observability/metrics/datadog.md index 5e133e1df..4d0ffc706 100644 --- a/docs/content/observability/metrics/datadog.md +++ b/docs/content/observability/metrics/datadog.md @@ -59,7 +59,7 @@ metrics: ```bash tab="CLI" --metrics.datadog.addEntryPointsLabels=true ``` -#### `AddRoutersLabels` +#### `addRoutersLabels` _Optional, Default=false_ diff --git a/docs/content/observability/metrics/influxdb.md b/docs/content/observability/metrics/influxdb.md index b7ffee00b..b05ec2e65 100644 --- a/docs/content/observability/metrics/influxdb.md +++ b/docs/content/observability/metrics/influxdb.md @@ -170,7 +170,7 @@ metrics: --metrics.influxdb.addEntryPointsLabels=true ``` -#### `AddRoutersLabels` +#### `addRoutersLabels` _Optional, Default=false_ diff --git a/docs/content/observability/metrics/prometheus.md b/docs/content/observability/metrics/prometheus.md index d751ebb56..d4ac5e54a 100644 --- a/docs/content/observability/metrics/prometheus.md +++ b/docs/content/observability/metrics/prometheus.md @@ -64,7 +64,7 @@ metrics: --metrics.prometheus.addEntryPointsLabels=true ``` -#### `AddRoutersLabels` +#### `addRoutersLabels` _Optional, Default=false_ diff --git a/docs/content/observability/metrics/statsd.md b/docs/content/observability/metrics/statsd.md index 7a57ea91f..4d310969a 100644 --- a/docs/content/observability/metrics/statsd.md +++ b/docs/content/observability/metrics/statsd.md @@ -60,7 +60,7 @@ metrics: --metrics.statsd.addEntryPointsLabels=true ``` -#### `AddRoutersLabels` +#### `addRoutersLabels` _Optional, Default=false_ From d91eefa74f7d0102bf687e3919f259dab3544be5 Mon Sep 17 00:00:00 2001 From: Tom Moulard Date: Mon, 8 Nov 2021 17:58:12 +0100 Subject: [PATCH 19/20] fix: TCP/UDP wrr when all servers have a weight set to 0 Co-authored-by: Kevin Pollet --- pkg/tcp/wrr_load_balancer.go | 29 ++++++++++++------------ pkg/tcp/wrr_load_balancer_test.go | 37 ++++++++++++++++++++++--------- pkg/udp/wrr_load_balancer.go | 29 ++++++++++++------------ 3 files changed, 56 insertions(+), 39 deletions(-) diff --git a/pkg/tcp/wrr_load_balancer.go b/pkg/tcp/wrr_load_balancer.go index 8f26251ef..236fd8007 100644 --- a/pkg/tcp/wrr_load_balancer.go +++ b/pkg/tcp/wrr_load_balancer.go @@ -15,7 +15,7 @@ type server struct { // WRRLoadBalancer is a naive RoundRobin load balancer for TCP services. type WRRLoadBalancer struct { servers []server - lock sync.RWMutex + lock sync.Mutex currentWeight int index int } @@ -29,16 +29,16 @@ func NewWRRLoadBalancer() *WRRLoadBalancer { // ServeTCP forwards the connection to the right service. func (b *WRRLoadBalancer) ServeTCP(conn WriteCloser) { - if len(b.servers) == 0 { - log.WithoutContext().Error("no available server") - return - } - + b.lock.Lock() next, err := b.next() + b.lock.Unlock() + if err != nil { log.WithoutContext().Errorf("Error during load balancing: %v", err) conn.Close() + return } + next.ServeTCP(conn) } @@ -50,6 +50,9 @@ func (b *WRRLoadBalancer) AddServer(serverHandler Handler) { // AddWeightServer appends a server to the existing list with a weight. func (b *WRRLoadBalancer) AddWeightServer(serverHandler Handler, weight *int) { + b.lock.Lock() + defer b.lock.Unlock() + w := 1 if weight != nil { w = *weight @@ -87,9 +90,6 @@ func gcd(a, b int) int { } func (b *WRRLoadBalancer) next() (Handler, error) { - b.lock.Lock() - defer b.lock.Unlock() - if len(b.servers) == 0 { return nil, fmt.Errorf("no servers in the pool") } @@ -98,10 +98,14 @@ func (b *WRRLoadBalancer) next() (Handler, error) { // it calculates the GCD and subtracts it on every iteration, what interleaves servers // and allows us not to build an iterator every time we readjust weights - // GCD across all enabled servers - gcd := b.weightGcd() // Maximum weight across all enabled servers max := b.maxWeight() + if max == 0 { + return nil, fmt.Errorf("all servers have 0 weight") + } + + // GCD across all enabled servers + gcd := b.weightGcd() for { b.index = (b.index + 1) % len(b.servers) @@ -109,9 +113,6 @@ func (b *WRRLoadBalancer) next() (Handler, error) { b.currentWeight -= gcd if b.currentWeight <= 0 { b.currentWeight = max - if b.currentWeight == 0 { - return nil, fmt.Errorf("all servers have 0 weight") - } } } srv := b.servers[b.index] diff --git a/pkg/tcp/wrr_load_balancer_test.go b/pkg/tcp/wrr_load_balancer_test.go index 933f48976..a07f51762 100644 --- a/pkg/tcp/wrr_load_balancer_test.go +++ b/pkg/tcp/wrr_load_balancer_test.go @@ -10,7 +10,8 @@ import ( ) type fakeConn struct { - call map[string]int + writeCall map[string]int + closeCall int } func (f *fakeConn) Read(b []byte) (n int, err error) { @@ -18,12 +19,13 @@ func (f *fakeConn) Read(b []byte) (n int, err error) { } func (f *fakeConn) Write(b []byte) (n int, err error) { - f.call[string(b)]++ + f.writeCall[string(b)]++ return len(b), nil } func (f *fakeConn) Close() error { - panic("implement me") + f.closeCall++ + return nil } func (f *fakeConn) LocalAddr() net.Addr { @@ -55,7 +57,8 @@ func TestLoadBalancing(t *testing.T) { desc string serversWeight map[string]int totalCall int - expected map[string]int + expectedWrite map[string]int + expectedClose int }{ { desc: "RoundRobin", @@ -64,7 +67,7 @@ func TestLoadBalancing(t *testing.T) { "h2": 1, }, totalCall: 4, - expected: map[string]int{ + expectedWrite: map[string]int{ "h1": 2, "h2": 2, }, @@ -76,7 +79,7 @@ func TestLoadBalancing(t *testing.T) { "h2": 1, }, totalCall: 4, - expected: map[string]int{ + expectedWrite: map[string]int{ "h1": 3, "h2": 1, }, @@ -88,22 +91,33 @@ func TestLoadBalancing(t *testing.T) { "h2": 1, }, totalCall: 16, - expected: map[string]int{ + expectedWrite: map[string]int{ "h1": 12, "h2": 4, }, }, { - desc: "WeighedRoundRobin with 0 weight server", + desc: "WeighedRoundRobin with one 0 weight server", serversWeight: map[string]int{ "h1": 3, "h2": 0, }, totalCall: 16, - expected: map[string]int{ + expectedWrite: map[string]int{ "h1": 16, }, }, + { + desc: "WeighedRoundRobin with all servers with 0 weight", + serversWeight: map[string]int{ + "h1": 0, + "h2": 0, + "h3": 0, + }, + totalCall: 10, + expectedWrite: map[string]int{}, + expectedClose: 10, + }, } for _, test := range testCases { @@ -120,12 +134,13 @@ func TestLoadBalancing(t *testing.T) { }), &weight) } - conn := &fakeConn{call: make(map[string]int)} + conn := &fakeConn{writeCall: make(map[string]int)} for i := 0; i < test.totalCall; i++ { balancer.ServeTCP(conn) } - assert.Equal(t, test.expected, conn.call) + assert.Equal(t, test.expectedWrite, conn.writeCall) + assert.Equal(t, test.expectedClose, conn.closeCall) }) } } diff --git a/pkg/udp/wrr_load_balancer.go b/pkg/udp/wrr_load_balancer.go index 384b699fc..7dd2a9c07 100644 --- a/pkg/udp/wrr_load_balancer.go +++ b/pkg/udp/wrr_load_balancer.go @@ -15,7 +15,7 @@ type server struct { // WRRLoadBalancer is a naive RoundRobin load balancer for UDP services. type WRRLoadBalancer struct { servers []server - lock sync.RWMutex + lock sync.Mutex currentWeight int index int } @@ -29,16 +29,16 @@ func NewWRRLoadBalancer() *WRRLoadBalancer { // ServeUDP forwards the connection to the right service. func (b *WRRLoadBalancer) ServeUDP(conn *Conn) { - if len(b.servers) == 0 { - log.WithoutContext().Error("no available server") - return - } - + b.lock.Lock() next, err := b.next() + b.lock.Unlock() + if err != nil { log.WithoutContext().Errorf("Error during load balancing: %v", err) conn.Close() + return } + next.ServeUDP(conn) } @@ -50,6 +50,9 @@ func (b *WRRLoadBalancer) AddServer(serverHandler Handler) { // AddWeightedServer appends a handler to the existing list with a weight. func (b *WRRLoadBalancer) AddWeightedServer(serverHandler Handler, weight *int) { + b.lock.Lock() + defer b.lock.Unlock() + w := 1 if weight != nil { w = *weight @@ -87,9 +90,6 @@ func gcd(a, b int) int { } func (b *WRRLoadBalancer) next() (Handler, error) { - b.lock.Lock() - defer b.lock.Unlock() - if len(b.servers) == 0 { return nil, fmt.Errorf("no servers in the pool") } @@ -98,10 +98,14 @@ func (b *WRRLoadBalancer) next() (Handler, error) { // but is actually very simple it calculates the GCD and subtracts it on every iteration, // what interleaves servers and allows us not to build an iterator every time we readjust weights. - // GCD across all enabled servers - gcd := b.weightGcd() // Maximum weight across all enabled servers max := b.maxWeight() + if max == 0 { + return nil, fmt.Errorf("all servers have 0 weight") + } + + // GCD across all enabled servers + gcd := b.weightGcd() for { b.index = (b.index + 1) % len(b.servers) @@ -109,9 +113,6 @@ func (b *WRRLoadBalancer) next() (Handler, error) { b.currentWeight -= gcd if b.currentWeight <= 0 { b.currentWeight = max - if b.currentWeight == 0 { - return nil, fmt.Errorf("all servers have 0 weight") - } } } srv := b.servers[b.index] From 95dc43ce4a0b9a3cdfa1ce33e606af5ef0b09f5b Mon Sep 17 00:00:00 2001 From: Romain Date: Mon, 8 Nov 2021 18:36:13 +0100 Subject: [PATCH 20/20] Prepare release v2.5.4 --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ script/gcg/traefik-bugfix.toml | 6 +++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5a696986..26dd81c2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ +## [v2.5.4](https://github.com/traefik/traefik/tree/v2.5.4) (2021-11-08) +[All Commits](https://github.com/traefik/traefik/compare/v2.5.3...v2.5.4) + +**Bug fixes:** +- **[acme]** Update go-acme/lego to v4.5.0 ([#8481](https://github.com/traefik/traefik/pull/8481) by [ldez](https://github.com/ldez)) +- **[k8s/crd,k8s]** fix: add missing RequireAnyClientCert value to TLSOption CRD ([#8464](https://github.com/traefik/traefik/pull/8464) by [kevinpollet](https://github.com/kevinpollet)) +- **[k8s/crd,k8s]** fix: normalize middleware names in ingress route config ([#8484](https://github.com/traefik/traefik/pull/8484) by [aaronraff](https://github.com/aaronraff)) +- **[middleware,provider,tls]** fix: do not require a TLS client cert when InsecureSkipVerify is false ([#8525](https://github.com/traefik/traefik/pull/8525) by [kevinpollet](https://github.com/kevinpollet)) +- **[middleware,tls]** fix: use host's root CA set if ClientTLS ca is not defined ([#8545](https://github.com/traefik/traefik/pull/8545) by [kevinpollet](https://github.com/kevinpollet)) +- **[middleware]** fix: forward request Host to errors middleware service ([#8460](https://github.com/traefik/traefik/pull/8460) by [kevinpollet](https://github.com/kevinpollet)) +- **[middleware]** fix: use EscapedPath as header value when RawPath is empty ([#8251](https://github.com/traefik/traefik/pull/8251) by [dtomcej](https://github.com/dtomcej)) +- **[tcp,udp]** fix: TCP/UDP wrr when all servers have a weight set to 0 ([#8553](https://github.com/traefik/traefik/pull/8553) by [tomMoulard](https://github.com/tomMoulard)) +- **[webui]** fix: bug parsing weighted service provider name ([#8522](https://github.com/traefik/traefik/pull/8522) by [cocoanton](https://github.com/cocoanton)) + +**Documentation:** +- **[acme]** docs: remove quotes in certificatesresolvers CLI examples ([#8544](https://github.com/traefik/traefik/pull/8544) by [rdxmb](https://github.com/rdxmb)) +- **[k8s/ingress,k8s]** docs: clarify usage for cross provider references in Kubernetes ingress annotations ([#8536](https://github.com/traefik/traefik/pull/8536) by [rtribotte](https://github.com/rtribotte)) +- **[k8s/ingress]** docs: networking.k8s.io/v1beta1 to networking.k8s.io/v1 ([#8523](https://github.com/traefik/traefik/pull/8523) by [pmareke](https://github.com/pmareke)) +- **[k8s]** docs: replace links to French translation of k8s docs with English ones ([#8457](https://github.com/traefik/traefik/pull/8457) by [FoseFx](https://github.com/FoseFx)) +- **[k8s]** docs: remove non-working kind config in IngressRouteTCP/UDP examples ([#8538](https://github.com/traefik/traefik/pull/8538) by [kevinpollet](https://github.com/kevinpollet)) +- **[kv]** docs: fix typo in KV providers documentation ([#8477](https://github.com/traefik/traefik/pull/8477) by [rondoe](https://github.com/rondoe)) +- **[metrics]** docs: fix typo in addRoutersLabels option title ([#8561](https://github.com/traefik/traefik/pull/8561) by [kevinpollet](https://github.com/kevinpollet)) +- **[middleware]** fix: sourceCriterion documentation for InFlightReq and RateLimit middlewares ([#8524](https://github.com/traefik/traefik/pull/8524) by [pmareke](https://github.com/pmareke)) +- **[middleware]** Mention escaping escape characters in YAML for regex usage ([#8496](https://github.com/traefik/traefik/pull/8496) by [JackMorganNZ](https://github.com/JackMorganNZ)) +- **[rules]** docs: add named groups details to Regexp Syntax section ([#8559](https://github.com/traefik/traefik/pull/8559) by [kerrsmith](https://github.com/kerrsmith)) +- **[tracing]** docs: reword tracing config descriptions to be consistent ([#8473](https://github.com/traefik/traefik/pull/8473) by [kevinpollet](https://github.com/kevinpollet)) +- docs: remove link to microbadger.com ([#8555](https://github.com/traefik/traefik/pull/8555) by [CrispyBaguette](https://github.com/CrispyBaguette)) +- docs: remove http scheme urls in documentation ([#8507](https://github.com/traefik/traefik/pull/8507) by [tomMoulard](https://github.com/tomMoulard)) +- docs: update traefik image version ([#8533](https://github.com/traefik/traefik/pull/8533) by [tomMoulard](https://github.com/tomMoulard)) + ## [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) diff --git a/script/gcg/traefik-bugfix.toml b/script/gcg/traefik-bugfix.toml index d9678b286..78db43815 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.3 +# example new bugfix v2.5.4 CurrentRef = "v2.5" -PreviousRef = "v2.5.2" +PreviousRef = "v2.5.3" BaseBranch = "v2.5" -FutureCurrentRefName = "v2.5.3" +FutureCurrentRefName = "v2.5.4" ThresholdPreviousRef = 10 ThresholdCurrentRef = 10