From e78374aa29c2c61c1f6ce21623578ce07e45ed40 Mon Sep 17 00:00:00 2001 From: Oliver Dvorski Date: Wed, 10 Jan 2024 15:12:07 +0100 Subject: [PATCH 1/9] docs: slightly rewords the documentation --- docs/content/getting-started/concepts.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/getting-started/concepts.md b/docs/content/getting-started/concepts.md index 10e1787e4..21e1817f7 100644 --- a/docs/content/getting-started/concepts.md +++ b/docs/content/getting-started/concepts.md @@ -25,7 +25,7 @@ The main features include dynamic configuration, automatic service discovery, an ## Edge Router -Traefik is an *Edge Router*, it means that it's the door to your platform, and that it intercepts and routes every incoming request: +Traefik is an *Edge Router*; this means that it's the door to your platform, and that it intercepts and routes every incoming request: it knows all the logic and every [rule](../routing/routers/index.md#rule "Link to docs about routing rules") that determine which services handle which requests (based on the *path*, the *host*, *headers*, etc.). ![The Door to Your Infrastructure](../assets/img/traefik-concepts-1.png "Picture explaining the infrastructure") @@ -38,7 +38,7 @@ Deploying your services, you attach information that tells Traefik the character ![Decentralized Configuration](../assets/img/traefik-concepts-2.png "Picture about Decentralized Configuration") -It means that when a service is deployed, Traefik detects it immediately and updates the routing rules in real time. +This means that when a service is deployed, Traefik detects it immediately and updates the routing rules in real time. Similarly, when a service is removed from the infrastructure, the corresponding route is deleted accordingly. You no longer need to create and synchronize configuration files cluttered with IP addresses or other rules. From 3a461d2f237375a20db8053a8ee251983c1c908f Mon Sep 17 00:00:00 2001 From: James Rasell Date: Fri, 12 Jan 2024 14:22:05 +0100 Subject: [PATCH 2/9] deps: update the Nomad API dependency to v1.7.2 --- go.mod | 4 ++-- go.sum | 13 ++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 81c2de9a8..d5a59d96f 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/hashicorp/go-hclog v1.5.0 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-version v1.6.0 - github.com/hashicorp/nomad/api v0.0.0-20220506174431-b5665129cd1f + github.com/hashicorp/nomad/api v0.0.0-20231213195942-64e3dca9274b github.com/influxdata/influxdb-client-go/v2 v2.7.0 github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d github.com/instana/go-sensor v1.38.3 @@ -193,7 +193,7 @@ require ( github.com/gophercloud/gophercloud v1.0.0 // indirect github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect - github.com/hashicorp/cronexpr v1.1.1 // indirect + github.com/hashicorp/cronexpr v1.1.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect diff --git a/go.sum b/go.sum index dc194e29d..dfa028007 100644 --- a/go.sum +++ b/go.sum @@ -385,6 +385,8 @@ github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -626,8 +628,8 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.15.0 h1:2qK9nDrr4tiJKRoxPGhm6B7xJjLVIQqkjiab2M4aKjU= github.com/hashicorp/consul/sdk v0.15.0/go.mod h1:r/OmRRPbHOe0yxNahLw7G9x5WG17E1BIECMtCjcPSNo= -github.com/hashicorp/cronexpr v1.1.1 h1:NJZDd87hGXjoZBdvyCF9mX4DCq5Wy7+A/w+A7q0wn6c= -github.com/hashicorp/cronexpr v1.1.1/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= +github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= +github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -679,8 +681,8 @@ github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= -github.com/hashicorp/nomad/api v0.0.0-20220506174431-b5665129cd1f h1:jSBbBJcPca465gK6XfwdXRQnFCd63e0oJmqllZTsawI= -github.com/hashicorp/nomad/api v0.0.0-20220506174431-b5665129cd1f/go.mod h1:b/AoT79m3PEpb6tKCFKva/M+q1rKJNUk5mdu1S8DymM= +github.com/hashicorp/nomad/api v0.0.0-20231213195942-64e3dca9274b h1:R1UDhkwGltpSPY9bCBBxIMQd+NY9BkN0vFHnJo/8o8w= +github.com/hashicorp/nomad/api v0.0.0-20231213195942-64e3dca9274b/go.mod h1:ijDwa6o1uG1jFSq6kERiX2PamKGpZzTmo0XOFNeFZgw= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= @@ -1150,8 +1152,9 @@ github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9 github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= -github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk= +github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= From 81ce45271d5bb1173b5182d9b35a2aba28ec0d7b Mon Sep 17 00:00:00 2001 From: Thomas Gunsch Date: Mon, 15 Jan 2024 16:14:05 +0100 Subject: [PATCH 3/9] Add forwardAuth.addAuthCookiesToResponse --- docs/content/middlewares/http/forwardauth.md | 49 +++++++++++++++++++ .../dynamic-configuration/docker-labels.yml | 1 + .../reference/dynamic-configuration/file.toml | 1 + .../reference/dynamic-configuration/file.yaml | 3 ++ .../kubernetes-crd-definition-v1.yml | 6 +++ .../reference/dynamic-configuration/kv-ref.md | 2 + .../traefik.io_middlewares.yaml | 6 +++ integration/fixtures/k8s/01-traefik-crd.yml | 6 +++ pkg/config/dynamic/middlewares.go | 2 + pkg/config/dynamic/zz_generated.deepcopy.go | 5 ++ pkg/middlewares/auth/forward.go | 49 ++++++++++++++++--- pkg/middlewares/auth/forward_test.go | 8 +++ pkg/middlewares/headers/header.go | 3 +- pkg/middlewares/headers/secure.go | 3 +- ...responsewriter.go => response_modifier.go} | 22 +++++---- pkg/provider/kubernetes/crd/kubernetes.go | 1 + .../crd/traefikio/v1alpha1/middleware.go | 2 + .../v1alpha1/zz_generated.deepcopy.go | 5 ++ 18 files changed, 155 insertions(+), 19 deletions(-) rename pkg/middlewares/{headers/responsewriter.go => response_modifier.go} (76%) diff --git a/docs/content/middlewares/http/forwardauth.md b/docs/content/middlewares/http/forwardauth.md index 6b69c620b..25b58480c 100644 --- a/docs/content/middlewares/http/forwardauth.md +++ b/docs/content/middlewares/http/forwardauth.md @@ -285,6 +285,55 @@ http: authRequestHeaders = "Accept,X-CustomHeader" ``` +### `addAuthCookiesToResponse` + +The `addAuthCookiesToResponse` option is the list of cookies to copy from the authentication server to the response, +replacing any existing conflicting cookie from the forwarded response. + +!!! info + + Please note that all backend cookies matching the configured list will not be added to the response. + +```yaml tab="Docker" +labels: + - "traefik.http.middlewares.test-auth.forwardauth.addAuthCookiesToResponse=Session-Cookie,State-Cookie" +``` + +```yaml tab="Kubernetes" +apiVersion: traefik.containo.us/v1alpha1 +kind: Middleware +metadata: + name: test-auth +spec: + forwardAuth: + address: https://example.com/auth + addAuthCookiesToResponse: + - Session-Cookie + - State-Cookie +``` + +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.forwardauth.addAuthCookiesToResponse=Session-Cookie,State-Cookie" +``` + +```toml tab="File (TOML)" +[http.middlewares] + [http.middlewares.test-auth.forwardAuth] + address = "https://example.com/auth" + addAuthCookiesToResponse = ["Session-Cookie", "State-Cookie"] +``` + +```yaml tab="File (YAML)" +http: + middlewares: + test-auth: + forwardAuth: + address: "https://example.com/auth" + addAuthCookiesToResponse: + - "Session-Cookie" + - "State-Cookie" +``` + ### `tls` _Optional_ diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml index 214af5fa1..8f47b6897 100644 --- a/docs/content/reference/dynamic-configuration/docker-labels.yml +++ b/docs/content/reference/dynamic-configuration/docker-labels.yml @@ -30,6 +30,7 @@ - "traefik.http.middlewares.middleware09.forwardauth.authresponseheaders=foobar, foobar" - "traefik.http.middlewares.middleware09.forwardauth.authresponseheadersregex=foobar" - "traefik.http.middlewares.middleware09.forwardauth.authrequestheaders=foobar, foobar" +- "traefik.http.middlewares.middleware09.forwardauth.addauthcookiestoresponse=foobar, foobar" - "traefik.http.middlewares.middleware09.forwardauth.tls.ca=foobar" - "traefik.http.middlewares.middleware09.forwardauth.tls.cert=foobar" - "traefik.http.middlewares.middleware09.forwardauth.tls.insecureskipverify=true" diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index 49cf27e95..a0115b8c6 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -156,6 +156,7 @@ authResponseHeaders = ["foobar", "foobar"] authResponseHeadersRegex = "foobar" authRequestHeaders = ["foobar", "foobar"] + addAuthCookiesToResponse = ["foobar", "foobar"] [http.middlewares.Middleware09.forwardAuth.tls] ca = "foobar" cert = "foobar" diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index a1bd21658..2fd350550 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -174,6 +174,9 @@ http: authRequestHeaders: - foobar - foobar + addAuthCookiesToResponse: + - foobar + - foobar Middleware10: headers: customRequestHeaders: diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml index e388639a9..ed7b19817 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml @@ -912,6 +912,12 @@ spec: This middleware delegates the request authentication to a Service. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/' properties: + addAuthCookiesToResponse: + description: AddAuthCookiesToResponse defines the list of cookies + to copy from the authentication server response to the response. + items: + type: string + type: array address: description: Address defines the authentication server address. type: string diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md index 13ed064b2..3d4ae73cd 100644 --- a/docs/content/reference/dynamic-configuration/kv-ref.md +++ b/docs/content/reference/dynamic-configuration/kv-ref.md @@ -30,6 +30,8 @@ | `traefik/http/middlewares/Middleware08/errors/service` | `foobar` | | `traefik/http/middlewares/Middleware08/errors/status/0` | `foobar` | | `traefik/http/middlewares/Middleware08/errors/status/1` | `foobar` | +| `traefik/http/middlewares/Middleware09/forwardAuth/addAuthCookiesToResponse/0` | `foobar` | +| `traefik/http/middlewares/Middleware09/forwardAuth/addAuthCookiesToResponse/1` | `foobar` | | `traefik/http/middlewares/Middleware09/forwardAuth/address` | `foobar` | | `traefik/http/middlewares/Middleware09/forwardAuth/authRequestHeaders/0` | `foobar` | | `traefik/http/middlewares/Middleware09/forwardAuth/authRequestHeaders/1` | `foobar` | diff --git a/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml b/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml index 6eb4089c4..54301dbae 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml @@ -337,6 +337,12 @@ spec: This middleware delegates the request authentication to a Service. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/' properties: + addAuthCookiesToResponse: + description: AddAuthCookiesToResponse defines the list of cookies + to copy from the authentication server response to the response. + items: + type: string + type: array address: description: Address defines the authentication server address. type: string diff --git a/integration/fixtures/k8s/01-traefik-crd.yml b/integration/fixtures/k8s/01-traefik-crd.yml index e388639a9..ed7b19817 100644 --- a/integration/fixtures/k8s/01-traefik-crd.yml +++ b/integration/fixtures/k8s/01-traefik-crd.yml @@ -912,6 +912,12 @@ spec: This middleware delegates the request authentication to a Service. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/' properties: + addAuthCookiesToResponse: + description: AddAuthCookiesToResponse defines the list of cookies + to copy from the authentication server response to the response. + items: + type: string + type: array address: description: Address defines the authentication server address. type: string diff --git a/pkg/config/dynamic/middlewares.go b/pkg/config/dynamic/middlewares.go index dcb884337..b6482d8ef 100644 --- a/pkg/config/dynamic/middlewares.go +++ b/pkg/config/dynamic/middlewares.go @@ -223,6 +223,8 @@ type ForwardAuth struct { // AuthRequestHeaders defines the list of the headers to copy from the request to the authentication server. // If not set or empty then all request headers are passed. AuthRequestHeaders []string `json:"authRequestHeaders,omitempty" toml:"authRequestHeaders,omitempty" yaml:"authRequestHeaders,omitempty" export:"true"` + // AddAuthCookiesToResponse defines the list of cookies to copy from the authentication server response to the response. + AddAuthCookiesToResponse []string `json:"addAuthCookiesToResponse,omitempty" toml:"addAuthCookiesToResponse,omitempty" yaml:"addAuthCookiesToResponse,omitempty" export:"true"` } // +k8s:deepcopy-gen=true diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go index 0a4e70d45..84755c5cb 100644 --- a/pkg/config/dynamic/zz_generated.deepcopy.go +++ b/pkg/config/dynamic/zz_generated.deepcopy.go @@ -324,6 +324,11 @@ func (in *ForwardAuth) DeepCopyInto(out *ForwardAuth) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.AddAuthCookiesToResponse != nil { + in, out := &in.AddAuthCookiesToResponse, &out.AddAuthCookiesToResponse + *out = make([]string, len(*in)) + copy(*out, *in) + } return } diff --git a/pkg/middlewares/auth/forward.go b/pkg/middlewares/auth/forward.go index f93137469..6e3b3ac84 100644 --- a/pkg/middlewares/auth/forward.go +++ b/pkg/middlewares/auth/forward.go @@ -48,19 +48,26 @@ type forwardAuth struct { client http.Client trustForwardHeader bool authRequestHeaders []string + addAuthCookiesToResponse map[string]struct{} } // NewForward creates a forward auth middleware. func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAuth, name string) (http.Handler, error) { middlewares.GetLogger(ctx, name, typeNameForward).Debug().Msg("Creating middleware") + addAuthCookiesToResponse := make(map[string]struct{}) + for _, cookieName := range config.AddAuthCookiesToResponse { + addAuthCookiesToResponse[cookieName] = struct{}{} + } + fa := &forwardAuth{ - address: config.Address, - authResponseHeaders: config.AuthResponseHeaders, - next: next, - name: name, - trustForwardHeader: config.TrustForwardHeader, - authRequestHeaders: config.AuthRequestHeaders, + address: config.Address, + authResponseHeaders: config.AuthResponseHeaders, + next: next, + name: name, + trustForwardHeader: config.TrustForwardHeader, + authRequestHeaders: config.AuthRequestHeaders, + addAuthCookiesToResponse: addAuthCookiesToResponse, } // Ensure our request client does not follow redirects @@ -211,7 +218,35 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { tracing.LogResponseCode(forwardSpan, forwardResponse.StatusCode, trace.SpanKindClient) req.RequestURI = req.URL.RequestURI() - fa.next.ServeHTTP(rw, req) + + authCookies := forwardResponse.Cookies() + if len(authCookies) == 0 { + fa.next.ServeHTTP(rw, req) + return + } + + fa.next.ServeHTTP(middlewares.NewResponseModifier(rw, req, fa.buildModifier(authCookies)), req) +} + +func (fa *forwardAuth) buildModifier(authCookies []*http.Cookie) func(res *http.Response) error { + return func(res *http.Response) error { + cookies := res.Cookies() + res.Header.Del("Set-Cookie") + + for _, cookie := range cookies { + if _, found := fa.addAuthCookiesToResponse[cookie.Name]; !found { + res.Header.Add("Set-Cookie", cookie.String()) + } + } + + for _, cookie := range authCookies { + if _, found := fa.addAuthCookiesToResponse[cookie.Name]; found { + res.Header.Add("Set-Cookie", cookie.String()) + } + } + + return nil + } } func writeHeader(req, forwardReq *http.Request, trustForwardHeader bool, allowedHeaders []string) { diff --git a/pkg/middlewares/auth/forward_test.go b/pkg/middlewares/auth/forward_test.go index e3afd6882..461d9b329 100644 --- a/pkg/middlewares/auth/forward_test.go +++ b/pkg/middlewares/auth/forward_test.go @@ -66,6 +66,8 @@ func TestForwardAuthSuccess(t *testing.T) { w.Header().Add("X-Auth-Group", "group1") w.Header().Add("X-Auth-Group", "group2") w.Header().Add("Foo-Bar", "auth-value") + w.Header().Add("Set-Cookie", "authCookie=Auth") + w.Header().Add("Set-Cookie", "authCookieNotAdded=Auth") fmt.Fprintln(w, "Success") })) t.Cleanup(server.Close) @@ -76,6 +78,9 @@ func TestForwardAuthSuccess(t *testing.T) { assert.Equal(t, []string{"group1", "group2"}, r.Header["X-Auth-Group"]) assert.Equal(t, "auth-value", r.Header.Get("Foo-Bar")) assert.Empty(t, r.Header.Get("Foo-Baz")) + w.Header().Add("Set-Cookie", "authCookie=Backend") + w.Header().Add("Set-Cookie", "backendCookie=Backend") + w.Header().Add("Other-Header", "BackendHeaderValue") fmt.Fprintln(w, "traefik") }) @@ -83,6 +88,7 @@ func TestForwardAuthSuccess(t *testing.T) { Address: server.URL, AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Group"}, AuthResponseHeadersRegex: "^Foo-", + AddAuthCookiesToResponse: []string{"authCookie"}, } middleware, err := NewForward(context.Background(), next, auth, "authTest") require.NoError(t, err) @@ -97,6 +103,8 @@ func TestForwardAuthSuccess(t *testing.T) { res, err := http.DefaultClient.Do(req) require.NoError(t, err) assert.Equal(t, http.StatusOK, res.StatusCode) + assert.Equal(t, []string{"backendCookie=Backend", "authCookie=Auth"}, res.Header["Set-Cookie"]) + assert.Equal(t, []string{"BackendHeaderValue"}, res.Header["Other-Header"]) body, err := io.ReadAll(res.Body) require.NoError(t, err) diff --git a/pkg/middlewares/headers/header.go b/pkg/middlewares/headers/header.go index 739358170..000cf4977 100644 --- a/pkg/middlewares/headers/header.go +++ b/pkg/middlewares/headers/header.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/vulcand/oxy/v2/forward" ) @@ -58,7 +59,7 @@ func (s *Header) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // If there is a next, call it. if s.next != nil { - s.next.ServeHTTP(newResponseModifier(rw, req, s.PostRequestModifyResponseHeaders), req) + s.next.ServeHTTP(middlewares.NewResponseModifier(rw, req, s.PostRequestModifyResponseHeaders), req) } } diff --git a/pkg/middlewares/headers/secure.go b/pkg/middlewares/headers/secure.go index 99b03a489..1766e6356 100644 --- a/pkg/middlewares/headers/secure.go +++ b/pkg/middlewares/headers/secure.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/unrolled/secure" ) @@ -45,6 +46,6 @@ func newSecure(next http.Handler, cfg dynamic.Headers, contextKey string) *secur func (s secureHeader) ServeHTTP(rw http.ResponseWriter, req *http.Request) { s.secure.HandlerFuncWithNextForRequestOnly(rw, req, func(writer http.ResponseWriter, request *http.Request) { - s.next.ServeHTTP(newResponseModifier(writer, request, s.secure.ModifyResponseHeaders), request) + s.next.ServeHTTP(middlewares.NewResponseModifier(writer, request, s.secure.ModifyResponseHeaders), request) }) } diff --git a/pkg/middlewares/headers/responsewriter.go b/pkg/middlewares/response_modifier.go similarity index 76% rename from pkg/middlewares/headers/responsewriter.go rename to pkg/middlewares/response_modifier.go index 121389e59..77b76c740 100644 --- a/pkg/middlewares/headers/responsewriter.go +++ b/pkg/middlewares/response_modifier.go @@ -1,4 +1,4 @@ -package headers +package middlewares import ( "bufio" @@ -9,7 +9,8 @@ import ( "github.com/rs/zerolog/log" ) -type responseModifier struct { +// ResponseModifier is a ResponseWriter to modify the response headers before sending them. +type ResponseModifier struct { req *http.Request rw http.ResponseWriter @@ -21,9 +22,10 @@ type responseModifier struct { modifierErr error // returned by modifier call } -// modifier can be nil. -func newResponseModifier(w http.ResponseWriter, r *http.Request, modifier func(*http.Response) error) http.ResponseWriter { - return &responseModifier{ +// NewResponseModifier returns a new ResponseModifier instance. +// The given modifier can be nil. +func NewResponseModifier(w http.ResponseWriter, r *http.Request, modifier func(*http.Response) error) http.ResponseWriter { + return &ResponseModifier{ req: r, rw: w, modifier: modifier, @@ -33,7 +35,7 @@ func newResponseModifier(w http.ResponseWriter, r *http.Request, modifier func(* // WriteHeader is, in the specific case of 1xx status codes, a direct call to the wrapped ResponseWriter, without marking headers as sent, // allowing so further calls. -func (r *responseModifier) WriteHeader(code int) { +func (r *ResponseModifier) WriteHeader(code int) { if r.headersSent { return } @@ -73,11 +75,11 @@ func (r *responseModifier) WriteHeader(code int) { r.rw.WriteHeader(code) } -func (r *responseModifier) Header() http.Header { +func (r *ResponseModifier) Header() http.Header { return r.rw.Header() } -func (r *responseModifier) Write(b []byte) (int, error) { +func (r *ResponseModifier) Write(b []byte) (int, error) { r.WriteHeader(r.code) if r.modifierErr != nil { return 0, r.modifierErr @@ -87,7 +89,7 @@ func (r *responseModifier) Write(b []byte) (int, error) { } // Hijack hijacks the connection. -func (r *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) { +func (r *ResponseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) { if h, ok := r.rw.(http.Hijacker); ok { return h.Hijack() } @@ -96,7 +98,7 @@ func (r *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) { } // Flush sends any buffered data to the client. -func (r *responseModifier) Flush() { +func (r *ResponseModifier) Flush() { if flusher, ok := r.rw.(http.Flusher); ok { flusher.Flush() } diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index 1f7ec671f..45b6634af 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -728,6 +728,7 @@ func createForwardAuthMiddleware(k8sClient Client, namespace string, auth *traef AuthResponseHeaders: auth.AuthResponseHeaders, AuthResponseHeadersRegex: auth.AuthResponseHeadersRegex, AuthRequestHeaders: auth.AuthRequestHeaders, + AddAuthCookiesToResponse: auth.AddAuthCookiesToResponse, } if auth.TLS == nil { diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middleware.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middleware.go index c86d51d3a..1378dc85d 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middleware.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middleware.go @@ -157,6 +157,8 @@ type ForwardAuth struct { AuthRequestHeaders []string `json:"authRequestHeaders,omitempty"` // TLS defines the configuration used to secure the connection to the authentication server. TLS *ClientTLS `json:"tls,omitempty"` + // AddAuthCookiesToResponse defines the list of cookies to copy from the authentication server response to the response. + AddAuthCookiesToResponse []string `json:"addAuthCookiesToResponse,omitempty"` } // ClientTLS holds the client TLS configuration. diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go index b08d2f7c4..40c9b979d 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go @@ -215,6 +215,11 @@ func (in *ForwardAuth) DeepCopyInto(out *ForwardAuth) { *out = new(ClientTLS) **out = **in } + if in.AddAuthCookiesToResponse != nil { + in, out := &in.AddAuthCookiesToResponse, &out.AddAuthCookiesToResponse + *out = make([]string, len(*in)) + copy(*out, *in) + } return } From 34d2a816c229f2861d1a5c6f8160a533d155ea11 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 16 Jan 2024 10:32:05 +0100 Subject: [PATCH 4/9] Enhance gendoc for Generating Static and Dynamic Reference Configuration Files --- .../dynamic-configuration/docker-labels.yml | 321 ++++++++------- .../reference/dynamic-configuration/file.toml | 201 ++++----- .../reference/dynamic-configuration/file.yaml | 110 ++--- .../reference/dynamic-configuration/kv-ref.md | 385 +++++++++--------- .../marathon-labels.json | 321 ++++++++------- .../reference/static-configuration/file.toml | 38 +- .../reference/static-configuration/file.yaml | 118 +++--- internal/gendoc.go | 221 ++++++++++ internal/parser.go | 66 +++ pkg/collector/collector_test.go | 3 +- .../hydration.go} | 11 +- pkg/config/dynamic/config.go | 2 +- 12 files changed, 1061 insertions(+), 736 deletions(-) create mode 100644 internal/parser.go rename pkg/collector/{hydration_test.go => hydratation/hydration.go} (94%) diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml index 723d4b049..300c4febe 100644 --- a/docs/content/reference/dynamic-configuration/docker-labels.yml +++ b/docs/content/reference/dynamic-configuration/docker-labels.yml @@ -1,129 +1,136 @@ -- "traefik.http.middlewares.middleware00.addprefix.prefix=foobar" -- "traefik.http.middlewares.middleware01.basicauth.headerfield=foobar" -- "traefik.http.middlewares.middleware01.basicauth.realm=foobar" -- "traefik.http.middlewares.middleware01.basicauth.removeheader=true" -- "traefik.http.middlewares.middleware01.basicauth.users=foobar, foobar" -- "traefik.http.middlewares.middleware01.basicauth.usersfile=foobar" -- "traefik.http.middlewares.middleware02.buffering.maxrequestbodybytes=42" -- "traefik.http.middlewares.middleware02.buffering.maxresponsebodybytes=42" -- "traefik.http.middlewares.middleware02.buffering.memrequestbodybytes=42" -- "traefik.http.middlewares.middleware02.buffering.memresponsebodybytes=42" -- "traefik.http.middlewares.middleware02.buffering.retryexpression=foobar" -- "traefik.http.middlewares.middleware03.chain.middlewares=foobar, foobar" -- "traefik.http.middlewares.middleware04.circuitbreaker.expression=foobar" -- "traefik.http.middlewares.middleware04.circuitbreaker.checkperiod=42s" -- "traefik.http.middlewares.middleware04.circuitbreaker.fallbackduration=42s" -- "traefik.http.middlewares.middleware04.circuitbreaker.recoveryduration=42s" -- "traefik.http.middlewares.middleware05.compress=true" -- "traefik.http.middlewares.middleware05.compress.excludedcontenttypes=foobar, foobar" -- "traefik.http.middlewares.middleware05.compress.minresponsebodybytes=42" -- "traefik.http.middlewares.middleware06.contenttype.autodetect=true" -- "traefik.http.middlewares.middleware07.digestauth.headerfield=foobar" -- "traefik.http.middlewares.middleware07.digestauth.realm=foobar" -- "traefik.http.middlewares.middleware07.digestauth.removeheader=true" -- "traefik.http.middlewares.middleware07.digestauth.users=foobar, foobar" -- "traefik.http.middlewares.middleware07.digestauth.usersfile=foobar" -- "traefik.http.middlewares.middleware08.errors.query=foobar" -- "traefik.http.middlewares.middleware08.errors.service=foobar" -- "traefik.http.middlewares.middleware08.errors.status=foobar, foobar" -- "traefik.http.middlewares.middleware09.forwardauth.address=foobar" -- "traefik.http.middlewares.middleware09.forwardauth.authresponseheaders=foobar, foobar" -- "traefik.http.middlewares.middleware09.forwardauth.authresponseheadersregex=foobar" -- "traefik.http.middlewares.middleware09.forwardauth.authrequestheaders=foobar, foobar" -- "traefik.http.middlewares.middleware09.forwardauth.tls.ca=foobar" -- "traefik.http.middlewares.middleware09.forwardauth.tls.caoptional=true" -- "traefik.http.middlewares.middleware09.forwardauth.tls.cert=foobar" -- "traefik.http.middlewares.middleware09.forwardauth.tls.insecureskipverify=true" -- "traefik.http.middlewares.middleware09.forwardauth.tls.key=foobar" -- "traefik.http.middlewares.middleware09.forwardauth.trustforwardheader=true" -- "traefik.http.middlewares.middleware10.headers.accesscontrolallowcredentials=true" -- "traefik.http.middlewares.middleware10.headers.accesscontrolallowheaders=foobar, foobar" -- "traefik.http.middlewares.middleware10.headers.accesscontrolallowmethods=foobar, foobar" -- "traefik.http.middlewares.middleware10.headers.accesscontrolalloworiginlist=foobar, foobar" -- "traefik.http.middlewares.middleware10.headers.accesscontrolalloworiginlistregex=foobar, foobar" -- "traefik.http.middlewares.middleware10.headers.accesscontrolexposeheaders=foobar, foobar" -- "traefik.http.middlewares.middleware10.headers.accesscontrolmaxage=42" -- "traefik.http.middlewares.middleware10.headers.addvaryheader=true" -- "traefik.http.middlewares.middleware10.headers.allowedhosts=foobar, foobar" -- "traefik.http.middlewares.middleware10.headers.browserxssfilter=true" -- "traefik.http.middlewares.middleware10.headers.contentsecuritypolicy=foobar" -- "traefik.http.middlewares.middleware10.headers.contenttypenosniff=true" -- "traefik.http.middlewares.middleware10.headers.custombrowserxssvalue=foobar" -- "traefik.http.middlewares.middleware10.headers.customframeoptionsvalue=foobar" -- "traefik.http.middlewares.middleware10.headers.customrequestheaders.name0=foobar" -- "traefik.http.middlewares.middleware10.headers.customrequestheaders.name1=foobar" -- "traefik.http.middlewares.middleware10.headers.customresponseheaders.name0=foobar" -- "traefik.http.middlewares.middleware10.headers.customresponseheaders.name1=foobar" -- "traefik.http.middlewares.middleware10.headers.featurepolicy=foobar" -- "traefik.http.middlewares.middleware10.headers.forcestsheader=true" -- "traefik.http.middlewares.middleware10.headers.framedeny=true" -- "traefik.http.middlewares.middleware10.headers.hostsproxyheaders=foobar, foobar" -- "traefik.http.middlewares.middleware10.headers.isdevelopment=true" -- "traefik.http.middlewares.middleware10.headers.permissionspolicy=foobar" -- "traefik.http.middlewares.middleware10.headers.publickey=foobar" -- "traefik.http.middlewares.middleware10.headers.referrerpolicy=foobar" -- "traefik.http.middlewares.middleware10.headers.sslforcehost=true" -- "traefik.http.middlewares.middleware10.headers.sslhost=foobar" -- "traefik.http.middlewares.middleware10.headers.sslproxyheaders.name0=foobar" -- "traefik.http.middlewares.middleware10.headers.sslproxyheaders.name1=foobar" -- "traefik.http.middlewares.middleware10.headers.sslredirect=true" -- "traefik.http.middlewares.middleware10.headers.ssltemporaryredirect=true" -- "traefik.http.middlewares.middleware10.headers.stsincludesubdomains=true" -- "traefik.http.middlewares.middleware10.headers.stspreload=true" -- "traefik.http.middlewares.middleware10.headers.stsseconds=42" -- "traefik.http.middlewares.middleware11.ipwhitelist.ipstrategy.depth=42" -- "traefik.http.middlewares.middleware11.ipwhitelist.ipstrategy.excludedips=foobar, foobar" -- "traefik.http.middlewares.middleware11.ipwhitelist.sourcerange=foobar, foobar" -- "traefik.http.middlewares.middleware12.inflightreq.amount=42" -- "traefik.http.middlewares.middleware12.inflightreq.sourcecriterion.ipstrategy.depth=42" -- "traefik.http.middlewares.middleware12.inflightreq.sourcecriterion.ipstrategy.excludedips=foobar, foobar" -- "traefik.http.middlewares.middleware12.inflightreq.sourcecriterion.requestheadername=foobar" -- "traefik.http.middlewares.middleware12.inflightreq.sourcecriterion.requesthost=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.issuer.commonname=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.issuer.country=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.issuer.domaincomponent=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.issuer.locality=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.issuer.organization=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.issuer.province=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.issuer.serialnumber=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.notafter=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.notbefore=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.sans=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.serialnumber=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.commonname=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.country=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.domaincomponent=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.locality=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.organization=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.organizationalunit=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.province=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.serialnumber=true" -- "traefik.http.middlewares.middleware13.passtlsclientcert.pem=true" -- "traefik.http.middlewares.middleware14.plugin.foobar.foo=bar" -- "traefik.http.middlewares.middleware15.ratelimit.average=42" -- "traefik.http.middlewares.middleware15.ratelimit.burst=42" -- "traefik.http.middlewares.middleware15.ratelimit.period=42" -- "traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.ipstrategy.depth=42" -- "traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.ipstrategy.excludedips=foobar, foobar" -- "traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.requestheadername=foobar" -- "traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.requesthost=true" -- "traefik.http.middlewares.middleware16.redirectregex.permanent=true" -- "traefik.http.middlewares.middleware16.redirectregex.regex=foobar" -- "traefik.http.middlewares.middleware16.redirectregex.replacement=foobar" -- "traefik.http.middlewares.middleware17.redirectscheme.permanent=true" -- "traefik.http.middlewares.middleware17.redirectscheme.port=foobar" -- "traefik.http.middlewares.middleware17.redirectscheme.scheme=foobar" -- "traefik.http.middlewares.middleware18.replacepath.path=foobar" -- "traefik.http.middlewares.middleware19.replacepathregex.regex=foobar" -- "traefik.http.middlewares.middleware19.replacepathregex.replacement=foobar" -- "traefik.http.middlewares.middleware20.retry.attempts=42" -- "traefik.http.middlewares.middleware20.retry.initialinterval=42" -- "traefik.http.middlewares.middleware21.stripprefix.forceslash=true" -- "traefik.http.middlewares.middleware21.stripprefix.prefixes=foobar, foobar" -- "traefik.http.middlewares.middleware22.stripprefixregex.regex=foobar, foobar" -- "traefik.http.middlewares.middleware23.ipallowlist.ipstrategy.depth=42" -- "traefik.http.middlewares.middleware23.ipallowlist.ipstrategy.excludedips=foobar, foobar" -- "traefik.http.middlewares.middleware23.ipallowlist.sourcerange=foobar, foobar" +## CODE GENERATED AUTOMATICALLY +## THIS FILE MUST NOT BE EDITED BY HAND +- "traefik.http.middlewares.middleware01.addprefix.prefix=foobar" +- "traefik.http.middlewares.middleware02.basicauth.headerfield=foobar" +- "traefik.http.middlewares.middleware02.basicauth.realm=foobar" +- "traefik.http.middlewares.middleware02.basicauth.removeheader=true" +- "traefik.http.middlewares.middleware02.basicauth.users=foobar, foobar" +- "traefik.http.middlewares.middleware02.basicauth.usersfile=foobar" +- "traefik.http.middlewares.middleware03.buffering.maxrequestbodybytes=42" +- "traefik.http.middlewares.middleware03.buffering.maxresponsebodybytes=42" +- "traefik.http.middlewares.middleware03.buffering.memrequestbodybytes=42" +- "traefik.http.middlewares.middleware03.buffering.memresponsebodybytes=42" +- "traefik.http.middlewares.middleware03.buffering.retryexpression=foobar" +- "traefik.http.middlewares.middleware04.chain.middlewares=foobar, foobar" +- "traefik.http.middlewares.middleware05.circuitbreaker.checkperiod=42s" +- "traefik.http.middlewares.middleware05.circuitbreaker.expression=foobar" +- "traefik.http.middlewares.middleware05.circuitbreaker.fallbackduration=42s" +- "traefik.http.middlewares.middleware05.circuitbreaker.recoveryduration=42s" +- "traefik.http.middlewares.middleware06.compress=true" +- "traefik.http.middlewares.middleware06.compress.excludedcontenttypes=foobar, foobar" +- "traefik.http.middlewares.middleware06.compress.minresponsebodybytes=42" +- "traefik.http.middlewares.middleware07.contenttype.autodetect=true" +- "traefik.http.middlewares.middleware08.digestauth.headerfield=foobar" +- "traefik.http.middlewares.middleware08.digestauth.realm=foobar" +- "traefik.http.middlewares.middleware08.digestauth.removeheader=true" +- "traefik.http.middlewares.middleware08.digestauth.users=foobar, foobar" +- "traefik.http.middlewares.middleware08.digestauth.usersfile=foobar" +- "traefik.http.middlewares.middleware09.errors.query=foobar" +- "traefik.http.middlewares.middleware09.errors.service=foobar" +- "traefik.http.middlewares.middleware09.errors.status=foobar, foobar" +- "traefik.http.middlewares.middleware10.forwardauth.address=foobar" +- "traefik.http.middlewares.middleware10.forwardauth.authrequestheaders=foobar, foobar" +- "traefik.http.middlewares.middleware10.forwardauth.authresponseheaders=foobar, foobar" +- "traefik.http.middlewares.middleware10.forwardauth.authresponseheadersregex=foobar" +- "traefik.http.middlewares.middleware10.forwardauth.tls.ca=foobar" +- "traefik.http.middlewares.middleware10.forwardauth.tls.caoptional=true" +- "traefik.http.middlewares.middleware10.forwardauth.tls.cert=foobar" +- "traefik.http.middlewares.middleware10.forwardauth.tls.insecureskipverify=true" +- "traefik.http.middlewares.middleware10.forwardauth.tls.key=foobar" +- "traefik.http.middlewares.middleware10.forwardauth.trustforwardheader=true" +- "traefik.http.middlewares.middleware11.headers.accesscontrolallowcredentials=true" +- "traefik.http.middlewares.middleware11.headers.accesscontrolallowheaders=foobar, foobar" +- "traefik.http.middlewares.middleware11.headers.accesscontrolallowmethods=foobar, foobar" +- "traefik.http.middlewares.middleware11.headers.accesscontrolalloworiginlist=foobar, foobar" +- "traefik.http.middlewares.middleware11.headers.accesscontrolalloworiginlistregex=foobar, foobar" +- "traefik.http.middlewares.middleware11.headers.accesscontrolexposeheaders=foobar, foobar" +- "traefik.http.middlewares.middleware11.headers.accesscontrolmaxage=42" +- "traefik.http.middlewares.middleware11.headers.addvaryheader=true" +- "traefik.http.middlewares.middleware11.headers.allowedhosts=foobar, foobar" +- "traefik.http.middlewares.middleware11.headers.browserxssfilter=true" +- "traefik.http.middlewares.middleware11.headers.contentsecuritypolicy=foobar" +- "traefik.http.middlewares.middleware11.headers.contenttypenosniff=true" +- "traefik.http.middlewares.middleware11.headers.custombrowserxssvalue=foobar" +- "traefik.http.middlewares.middleware11.headers.customframeoptionsvalue=foobar" +- "traefik.http.middlewares.middleware11.headers.customrequestheaders.name0=foobar" +- "traefik.http.middlewares.middleware11.headers.customrequestheaders.name1=foobar" +- "traefik.http.middlewares.middleware11.headers.customresponseheaders.name0=foobar" +- "traefik.http.middlewares.middleware11.headers.customresponseheaders.name1=foobar" +- "traefik.http.middlewares.middleware11.headers.featurepolicy=foobar" +- "traefik.http.middlewares.middleware11.headers.forcestsheader=true" +- "traefik.http.middlewares.middleware11.headers.framedeny=true" +- "traefik.http.middlewares.middleware11.headers.hostsproxyheaders=foobar, foobar" +- "traefik.http.middlewares.middleware11.headers.isdevelopment=true" +- "traefik.http.middlewares.middleware11.headers.permissionspolicy=foobar" +- "traefik.http.middlewares.middleware11.headers.publickey=foobar" +- "traefik.http.middlewares.middleware11.headers.referrerpolicy=foobar" +- "traefik.http.middlewares.middleware11.headers.sslforcehost=true" +- "traefik.http.middlewares.middleware11.headers.sslhost=foobar" +- "traefik.http.middlewares.middleware11.headers.sslproxyheaders.name0=foobar" +- "traefik.http.middlewares.middleware11.headers.sslproxyheaders.name1=foobar" +- "traefik.http.middlewares.middleware11.headers.sslredirect=true" +- "traefik.http.middlewares.middleware11.headers.ssltemporaryredirect=true" +- "traefik.http.middlewares.middleware11.headers.stsincludesubdomains=true" +- "traefik.http.middlewares.middleware11.headers.stspreload=true" +- "traefik.http.middlewares.middleware11.headers.stsseconds=42" +- "traefik.http.middlewares.middleware12.ipallowlist.ipstrategy=true" +- "traefik.http.middlewares.middleware12.ipallowlist.ipstrategy.depth=42" +- "traefik.http.middlewares.middleware12.ipallowlist.ipstrategy.excludedips=foobar, foobar" +- "traefik.http.middlewares.middleware12.ipallowlist.sourcerange=foobar, foobar" +- "traefik.http.middlewares.middleware13.ipwhitelist.ipstrategy=true" +- "traefik.http.middlewares.middleware13.ipwhitelist.ipstrategy.depth=42" +- "traefik.http.middlewares.middleware13.ipwhitelist.ipstrategy.excludedips=foobar, foobar" +- "traefik.http.middlewares.middleware13.ipwhitelist.sourcerange=foobar, foobar" +- "traefik.http.middlewares.middleware14.inflightreq.amount=42" +- "traefik.http.middlewares.middleware14.inflightreq.sourcecriterion.ipstrategy.depth=42" +- "traefik.http.middlewares.middleware14.inflightreq.sourcecriterion.ipstrategy.excludedips=foobar, foobar" +- "traefik.http.middlewares.middleware14.inflightreq.sourcecriterion.requestheadername=foobar" +- "traefik.http.middlewares.middleware14.inflightreq.sourcecriterion.requesthost=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.issuer.commonname=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.issuer.country=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.issuer.domaincomponent=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.issuer.locality=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.issuer.organization=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.issuer.province=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.issuer.serialnumber=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.notafter=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.notbefore=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.sans=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.serialnumber=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.commonname=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.country=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.domaincomponent=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.locality=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.organization=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.organizationalunit=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.province=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.serialnumber=true" +- "traefik.http.middlewares.middleware15.passtlsclientcert.pem=true" +- "traefik.http.middlewares.middleware16.plugin.pluginconf0.name0=foobar" +- "traefik.http.middlewares.middleware16.plugin.pluginconf0.name1=foobar" +- "traefik.http.middlewares.middleware16.plugin.pluginconf1.name0=foobar" +- "traefik.http.middlewares.middleware16.plugin.pluginconf1.name1=foobar" +- "traefik.http.middlewares.middleware17.ratelimit.average=42" +- "traefik.http.middlewares.middleware17.ratelimit.burst=42" +- "traefik.http.middlewares.middleware17.ratelimit.period=42s" +- "traefik.http.middlewares.middleware17.ratelimit.sourcecriterion.ipstrategy.depth=42" +- "traefik.http.middlewares.middleware17.ratelimit.sourcecriterion.ipstrategy.excludedips=foobar, foobar" +- "traefik.http.middlewares.middleware17.ratelimit.sourcecriterion.requestheadername=foobar" +- "traefik.http.middlewares.middleware17.ratelimit.sourcecriterion.requesthost=true" +- "traefik.http.middlewares.middleware18.redirectregex.permanent=true" +- "traefik.http.middlewares.middleware18.redirectregex.regex=foobar" +- "traefik.http.middlewares.middleware18.redirectregex.replacement=foobar" +- "traefik.http.middlewares.middleware19.redirectscheme.permanent=true" +- "traefik.http.middlewares.middleware19.redirectscheme.port=foobar" +- "traefik.http.middlewares.middleware19.redirectscheme.scheme=foobar" +- "traefik.http.middlewares.middleware20.replacepath.path=foobar" +- "traefik.http.middlewares.middleware21.replacepathregex.regex=foobar" +- "traefik.http.middlewares.middleware21.replacepathregex.replacement=foobar" +- "traefik.http.middlewares.middleware22.retry.attempts=42" +- "traefik.http.middlewares.middleware22.retry.initialinterval=42s" +- "traefik.http.middlewares.middleware23.stripprefix.forceslash=true" +- "traefik.http.middlewares.middleware23.stripprefix.prefixes=foobar, foobar" +- "traefik.http.middlewares.middleware24.stripprefixregex.regex=foobar, foobar" - "traefik.http.routers.router0.entrypoints=foobar, foobar" - "traefik.http.routers.router0.middlewares=foobar, foobar" - "traefik.http.routers.router0.priority=42" @@ -148,33 +155,34 @@ - "traefik.http.routers.router1.tls.domains[1].main=foobar" - "traefik.http.routers.router1.tls.domains[1].sans=foobar, foobar" - "traefik.http.routers.router1.tls.options=foobar" -- "traefik.http.services.service01.loadbalancer.healthcheck.followredirects=true" -- "traefik.http.services.service01.loadbalancer.healthcheck.headers.name0=foobar" -- "traefik.http.services.service01.loadbalancer.healthcheck.headers.name1=foobar" -- "traefik.http.services.service01.loadbalancer.healthcheck.hostname=foobar" -- "traefik.http.services.service01.loadbalancer.healthcheck.interval=foobar" -- "traefik.http.services.service01.loadbalancer.healthcheck.path=foobar" -- "traefik.http.services.service01.loadbalancer.healthcheck.method=foobar" -- "traefik.http.services.service01.loadbalancer.healthcheck.port=42" -- "traefik.http.services.service01.loadbalancer.healthcheck.scheme=foobar" -- "traefik.http.services.service01.loadbalancer.healthcheck.timeout=foobar" -- "traefik.http.services.service01.loadbalancer.passhostheader=true" -- "traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval=foobar" -- "traefik.http.services.service01.loadbalancer.serverstransport=foobar" -- "traefik.http.services.service01.loadbalancer.sticky.cookie=true" -- "traefik.http.services.service01.loadbalancer.sticky.cookie.httponly=true" -- "traefik.http.services.service01.loadbalancer.sticky.cookie.name=foobar" -- "traefik.http.services.service01.loadbalancer.sticky.cookie.samesite=foobar" -- "traefik.http.services.service01.loadbalancer.sticky.cookie.secure=true" -- "traefik.http.services.service01.loadbalancer.server.port=foobar" -- "traefik.http.services.service01.loadbalancer.server.scheme=foobar" -- "traefik.tcp.middlewares.tcpmiddleware00.ipwhitelist.sourcerange=foobar, foobar" -- "traefik.tcp.middlewares.tcpmiddleware01.inflightconn.amount=42" -- "traefik.tcp.middlewares.tcpmiddleware02.ipallowlist.sourcerange=foobar, foobar" +- "traefik.http.services.service02.loadbalancer.healthcheck.followredirects=true" +- "traefik.http.services.service02.loadbalancer.healthcheck.headers.name0=foobar" +- "traefik.http.services.service02.loadbalancer.healthcheck.headers.name1=foobar" +- "traefik.http.services.service02.loadbalancer.healthcheck.hostname=foobar" +- "traefik.http.services.service02.loadbalancer.healthcheck.interval=foobar" +- "traefik.http.services.service02.loadbalancer.healthcheck.method=foobar" +- "traefik.http.services.service02.loadbalancer.healthcheck.path=foobar" +- "traefik.http.services.service02.loadbalancer.healthcheck.port=42" +- "traefik.http.services.service02.loadbalancer.healthcheck.scheme=foobar" +- "traefik.http.services.service02.loadbalancer.healthcheck.timeout=foobar" +- "traefik.http.services.service02.loadbalancer.passhostheader=true" +- "traefik.http.services.service02.loadbalancer.responseforwarding.flushinterval=foobar" +- "traefik.http.services.service02.loadbalancer.serverstransport=foobar" +- "traefik.http.services.service02.loadbalancer.sticky=true" +- "traefik.http.services.service02.loadbalancer.sticky.cookie=true" +- "traefik.http.services.service02.loadbalancer.sticky.cookie.httponly=true" +- "traefik.http.services.service02.loadbalancer.sticky.cookie.name=foobar" +- "traefik.http.services.service02.loadbalancer.sticky.cookie.samesite=foobar" +- "traefik.http.services.service02.loadbalancer.sticky.cookie.secure=true" +- "traefik.http.services.service02.loadbalancer.server.port=foobar" +- "traefik.http.services.service02.loadbalancer.server.scheme=foobar" +- "traefik.tcp.middlewares.tcpmiddleware01.ipallowlist.sourcerange=foobar, foobar" +- "traefik.tcp.middlewares.tcpmiddleware02.ipwhitelist.sourcerange=foobar, foobar" +- "traefik.tcp.middlewares.tcpmiddleware03.inflightconn.amount=42" - "traefik.tcp.routers.tcprouter0.entrypoints=foobar, foobar" - "traefik.tcp.routers.tcprouter0.middlewares=foobar, foobar" -- "traefik.tcp.routers.tcprouter0.rule=foobar" - "traefik.tcp.routers.tcprouter0.priority=42" +- "traefik.tcp.routers.tcprouter0.rule=foobar" - "traefik.tcp.routers.tcprouter0.service=foobar" - "traefik.tcp.routers.tcprouter0.tls=true" - "traefik.tcp.routers.tcprouter0.tls.certresolver=foobar" @@ -186,8 +194,8 @@ - "traefik.tcp.routers.tcprouter0.tls.passthrough=true" - "traefik.tcp.routers.tcprouter1.entrypoints=foobar, foobar" - "traefik.tcp.routers.tcprouter1.middlewares=foobar, foobar" -- "traefik.tcp.routers.tcprouter1.rule=foobar" - "traefik.tcp.routers.tcprouter1.priority=42" +- "traefik.tcp.routers.tcprouter1.rule=foobar" - "traefik.tcp.routers.tcprouter1.service=foobar" - "traefik.tcp.routers.tcprouter1.tls=true" - "traefik.tcp.routers.tcprouter1.tls.certresolver=foobar" @@ -197,6 +205,7 @@ - "traefik.tcp.routers.tcprouter1.tls.domains[1].sans=foobar, foobar" - "traefik.tcp.routers.tcprouter1.tls.options=foobar" - "traefik.tcp.routers.tcprouter1.tls.passthrough=true" +- "traefik.tcp.services.tcpservice01.loadbalancer.proxyprotocol=true" - "traefik.tcp.services.tcpservice01.loadbalancer.proxyprotocol.version=42" - "traefik.tcp.services.tcpservice01.loadbalancer.terminationdelay=42" - "traefik.tcp.services.tcpservice01.loadbalancer.server.port=foobar" @@ -205,13 +214,3 @@ - "traefik.udp.routers.udprouter1.entrypoints=foobar, foobar" - "traefik.udp.routers.udprouter1.service=foobar" - "traefik.udp.services.udpservice01.loadbalancer.server.port=foobar" -- "traefik.tls.stores.Store0.defaultcertificate.certfile=foobar" -- "traefik.tls.stores.Store0.defaultcertificate.keyfile=foobar" -- "traefik.tls.stores.Store0.defaultgeneratedcert.domain.main=foobar" -- "traefik.tls.stores.Store0.defaultgeneratedcert.domain.sans=foobar, foobar" -- "traefik.tls.stores.Store0.defaultgeneratedcert.resolver=foobar" -- "traefik.tls.stores.Store1.defaultcertificate.certfile=foobar" -- "traefik.tls.stores.Store1.defaultcertificate.keyfile=foobar" -- "traefik.tls.stores.Store1.defaultgeneratedcert.domain.main=foobar" -- "traefik.tls.stores.Store1.defaultgeneratedcert.domain.sans=foobar, foobar" -- "traefik.tls.stores.Store1.defaultgeneratedcert.resolver=foobar" diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index 7b00cffa2..fc04a29c6 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -1,3 +1,5 @@ +## CODE GENERATED AUTOMATICALLY +## THIS FILE MUST NOT BE EDITED BY HAND [http] [http.routers] [http.routers.Router0] @@ -36,22 +38,27 @@ sans = ["foobar", "foobar"] [http.services] [http.services.Service01] - [http.services.Service01.loadBalancer] + [http.services.Service01.failover] + service = "foobar" + fallback = "foobar" + [http.services.Service01.failover.healthCheck] + [http.services.Service02] + [http.services.Service02.loadBalancer] passHostHeader = true serversTransport = "foobar" - [http.services.Service01.loadBalancer.sticky] - [http.services.Service01.loadBalancer.sticky.cookie] + [http.services.Service02.loadBalancer.sticky] + [http.services.Service02.loadBalancer.sticky.cookie] name = "foobar" secure = true httpOnly = true sameSite = "foobar" - [[http.services.Service01.loadBalancer.servers]] + [[http.services.Service02.loadBalancer.servers]] url = "foobar" - [[http.services.Service01.loadBalancer.servers]] + [[http.services.Service02.loadBalancer.servers]] url = "foobar" - [http.services.Service01.loadBalancer.healthCheck] + [http.services.Service02.loadBalancer.healthCheck] scheme = "foobar" path = "foobar" method = "foobar" @@ -60,109 +67,102 @@ timeout = "foobar" hostname = "foobar" followRedirects = true - [http.services.Service01.loadBalancer.healthCheck.headers] + [http.services.Service02.loadBalancer.healthCheck.headers] name0 = "foobar" name1 = "foobar" - [http.services.Service01.loadBalancer.responseForwarding] + [http.services.Service02.loadBalancer.responseForwarding] flushInterval = "foobar" - [http.services.Service02] - [http.services.Service02.mirroring] + [http.services.Service03] + [http.services.Service03.mirroring] service = "foobar" maxBodySize = 42 - [http.services.Service02.mirroring.healthCheck] - - [[http.services.Service02.mirroring.mirrors]] + [[http.services.Service03.mirroring.mirrors]] name = "foobar" percent = 42 - [[http.services.Service02.mirroring.mirrors]] + [[http.services.Service03.mirroring.mirrors]] name = "foobar" percent = 42 - [http.services.Service03] - [http.services.Service03.weighted] - [http.services.Service03.weighted.healthCheck] + [http.services.Service03.mirroring.healthCheck] + [http.services.Service04] + [http.services.Service04.weighted] - [[http.services.Service03.weighted.services]] + [[http.services.Service04.weighted.services]] name = "foobar" weight = 42 - [[http.services.Service03.weighted.services]] + [[http.services.Service04.weighted.services]] name = "foobar" weight = 42 - [http.services.Service03.weighted.sticky] - [http.services.Service03.weighted.sticky.cookie] + [http.services.Service04.weighted.sticky] + [http.services.Service04.weighted.sticky.cookie] name = "foobar" secure = true httpOnly = true sameSite = "foobar" - [http.services.Service04] - [http.services.Service04.failover] - service = "foobar" - fallback = "foobar" - - [http.services.Service04.failover.healthCheck] + [http.services.Service04.weighted.healthCheck] [http.middlewares] - [http.middlewares.Middleware00] - [http.middlewares.Middleware00.addPrefix] - prefix = "foobar" [http.middlewares.Middleware01] - [http.middlewares.Middleware01.basicAuth] + [http.middlewares.Middleware01.addPrefix] + prefix = "foobar" + [http.middlewares.Middleware02] + [http.middlewares.Middleware02.basicAuth] users = ["foobar", "foobar"] usersFile = "foobar" realm = "foobar" removeHeader = true headerField = "foobar" - [http.middlewares.Middleware02] - [http.middlewares.Middleware02.buffering] + [http.middlewares.Middleware03] + [http.middlewares.Middleware03.buffering] maxRequestBodyBytes = 42 memRequestBodyBytes = 42 maxResponseBodyBytes = 42 memResponseBodyBytes = 42 retryExpression = "foobar" - [http.middlewares.Middleware03] - [http.middlewares.Middleware03.chain] - middlewares = ["foobar", "foobar"] [http.middlewares.Middleware04] - [http.middlewares.Middleware04.circuitBreaker] + [http.middlewares.Middleware04.chain] + middlewares = ["foobar", "foobar"] + [http.middlewares.Middleware05] + [http.middlewares.Middleware05.circuitBreaker] expression = "foobar" checkPeriod = "42s" fallbackDuration = "42s" recoveryDuration = "42s" - [http.middlewares.Middleware05] - [http.middlewares.Middleware05.compress] + [http.middlewares.Middleware06] + [http.middlewares.Middleware06.compress] excludedContentTypes = ["foobar", "foobar"] minResponseBodyBytes = 42 - [http.middlewares.Middleware06] - [http.middlewares.Middleware06.contentType] - autoDetect = true [http.middlewares.Middleware07] - [http.middlewares.Middleware07.digestAuth] + [http.middlewares.Middleware07.contentType] + autoDetect = true + [http.middlewares.Middleware08] + [http.middlewares.Middleware08.digestAuth] users = ["foobar", "foobar"] usersFile = "foobar" removeHeader = true realm = "foobar" headerField = "foobar" - [http.middlewares.Middleware08] - [http.middlewares.Middleware08.errors] + [http.middlewares.Middleware09] + [http.middlewares.Middleware09.errors] status = ["foobar", "foobar"] service = "foobar" query = "foobar" - [http.middlewares.Middleware09] - [http.middlewares.Middleware09.forwardAuth] + [http.middlewares.Middleware10] + [http.middlewares.Middleware10.forwardAuth] address = "foobar" trustForwardHeader = true authResponseHeaders = ["foobar", "foobar"] authResponseHeadersRegex = "foobar" authRequestHeaders = ["foobar", "foobar"] - [http.middlewares.Middleware09.forwardAuth.tls] + [http.middlewares.Middleware10.forwardAuth.tls] ca = "foobar" caOptional = true cert = "foobar" key = "foobar" insecureSkipVerify = true - [http.middlewares.Middleware10] - [http.middlewares.Middleware10.headers] + [http.middlewares.Middleware11] + [http.middlewares.Middleware11.headers] accessControlAllowCredentials = true accessControlAllowHeaders = ["foobar", "foobar"] accessControlAllowMethods = ["foobar", "foobar"] @@ -192,39 +192,45 @@ featurePolicy = "foobar" permissionsPolicy = "foobar" isDevelopment = true - [http.middlewares.Middleware10.headers.customRequestHeaders] + [http.middlewares.Middleware11.headers.customRequestHeaders] name0 = "foobar" name1 = "foobar" - [http.middlewares.Middleware10.headers.customResponseHeaders] + [http.middlewares.Middleware11.headers.customResponseHeaders] name0 = "foobar" name1 = "foobar" - [http.middlewares.Middleware10.headers.sslProxyHeaders] + [http.middlewares.Middleware11.headers.sslProxyHeaders] name0 = "foobar" name1 = "foobar" - [http.middlewares.Middleware11] - [http.middlewares.Middleware11.ipWhiteList] + [http.middlewares.Middleware12] + [http.middlewares.Middleware12.ipAllowList] sourceRange = ["foobar", "foobar"] - [http.middlewares.Middleware11.ipWhiteList.ipStrategy] + [http.middlewares.Middleware12.ipAllowList.ipStrategy] depth = 42 excludedIPs = ["foobar", "foobar"] - [http.middlewares.Middleware12] - [http.middlewares.Middleware12.inFlightReq] + [http.middlewares.Middleware13] + [http.middlewares.Middleware13.ipWhiteList] + sourceRange = ["foobar", "foobar"] + [http.middlewares.Middleware13.ipWhiteList.ipStrategy] + depth = 42 + excludedIPs = ["foobar", "foobar"] + [http.middlewares.Middleware14] + [http.middlewares.Middleware14.inFlightReq] amount = 42 - [http.middlewares.Middleware12.inFlightReq.sourceCriterion] + [http.middlewares.Middleware14.inFlightReq.sourceCriterion] requestHeaderName = "foobar" requestHost = true - [http.middlewares.Middleware12.inFlightReq.sourceCriterion.ipStrategy] + [http.middlewares.Middleware14.inFlightReq.sourceCriterion.ipStrategy] depth = 42 excludedIPs = ["foobar", "foobar"] - [http.middlewares.Middleware13] - [http.middlewares.Middleware13.passTLSClientCert] + [http.middlewares.Middleware15] + [http.middlewares.Middleware15.passTLSClientCert] pem = true - [http.middlewares.Middleware13.passTLSClientCert.info] + [http.middlewares.Middleware15.passTLSClientCert.info] notAfter = true notBefore = true sans = true serialNumber = true - [http.middlewares.Middleware13.passTLSClientCert.info.subject] + [http.middlewares.Middleware15.passTLSClientCert.info.subject] country = true province = true locality = true @@ -233,7 +239,7 @@ commonName = true serialNumber = true domainComponent = true - [http.middlewares.Middleware13.passTLSClientCert.info.issuer] + [http.middlewares.Middleware15.passTLSClientCert.info.issuer] country = true province = true locality = true @@ -241,55 +247,53 @@ commonName = true serialNumber = true domainComponent = true - [http.middlewares.Middleware14] - [http.middlewares.Middleware14.plugin] - [http.middlewares.Middleware14.plugin.PluginConf] - foo = "bar" - [http.middlewares.Middleware15] - [http.middlewares.Middleware15.rateLimit] + [http.middlewares.Middleware16] + [http.middlewares.Middleware16.plugin] + [http.middlewares.Middleware16.plugin.PluginConf0] + name0 = "foobar" + name1 = "foobar" + [http.middlewares.Middleware16.plugin.PluginConf1] + name0 = "foobar" + name1 = "foobar" + [http.middlewares.Middleware17] + [http.middlewares.Middleware17.rateLimit] average = 42 period = "42s" burst = 42 - [http.middlewares.Middleware15.rateLimit.sourceCriterion] + [http.middlewares.Middleware17.rateLimit.sourceCriterion] requestHeaderName = "foobar" requestHost = true - [http.middlewares.Middleware15.rateLimit.sourceCriterion.ipStrategy] + [http.middlewares.Middleware17.rateLimit.sourceCriterion.ipStrategy] depth = 42 excludedIPs = ["foobar", "foobar"] - [http.middlewares.Middleware16] - [http.middlewares.Middleware16.redirectRegex] + [http.middlewares.Middleware18] + [http.middlewares.Middleware18.redirectRegex] regex = "foobar" replacement = "foobar" permanent = true - [http.middlewares.Middleware17] - [http.middlewares.Middleware17.redirectScheme] + [http.middlewares.Middleware19] + [http.middlewares.Middleware19.redirectScheme] scheme = "foobar" port = "foobar" permanent = true - [http.middlewares.Middleware18] - [http.middlewares.Middleware18.replacePath] + [http.middlewares.Middleware20] + [http.middlewares.Middleware20.replacePath] path = "foobar" - [http.middlewares.Middleware19] - [http.middlewares.Middleware19.replacePathRegex] + [http.middlewares.Middleware21] + [http.middlewares.Middleware21.replacePathRegex] regex = "foobar" replacement = "foobar" - [http.middlewares.Middleware20] - [http.middlewares.Middleware20.retry] + [http.middlewares.Middleware22] + [http.middlewares.Middleware22.retry] attempts = 42 initialInterval = "42s" - [http.middlewares.Middleware21] - [http.middlewares.Middleware21.stripPrefix] + [http.middlewares.Middleware23] + [http.middlewares.Middleware23.stripPrefix] prefixes = ["foobar", "foobar"] forceSlash = true - [http.middlewares.Middleware22] - [http.middlewares.Middleware22.stripPrefixRegex] + [http.middlewares.Middleware24] + [http.middlewares.Middleware24.stripPrefixRegex] regex = ["foobar", "foobar"] - [http.middlewares.Middleware23] - [http.middlewares.Middleware23.ipAllowList] - sourceRange = ["foobar", "foobar"] - [http.middlewares.Middleware23.ipAllowList.ipStrategy] - depth = 42 - excludedIPs = ["foobar", "foobar"] [http.serversTransports] [http.serversTransports.ServersTransport0] serverName = "foobar" @@ -395,11 +399,14 @@ name = "foobar" weight = 42 [tcp.middlewares] - [tcp.middlewares.TCPMiddleware00] - [tcp.middlewares.TCPMiddleware00.ipWhiteList] - sourceRange = ["foobar", "foobar"] [tcp.middlewares.TCPMiddleware01] - [tcp.middlewares.TCPMiddleware01.inFlightConn] + [tcp.middlewares.TCPMiddleware01.ipAllowList] + sourceRange = ["foobar", "foobar"] + [tcp.middlewares.TCPMiddleware02] + [tcp.middlewares.TCPMiddleware02.ipWhiteList] + sourceRange = ["foobar", "foobar"] + [tcp.middlewares.TCPMiddleware03] + [tcp.middlewares.TCPMiddleware03.inFlightConn] amount = 42 [udp] diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index a19859e0b..ec861d0a2 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -1,3 +1,5 @@ +## CODE GENERATED AUTOMATICALLY +## THIS FILE MUST NOT BE EDITED BY HAND http: routers: Router0: @@ -46,6 +48,11 @@ http: - foobar services: Service01: + failover: + service: foobar + fallback: foobar + healthCheck: {} + Service02: loadBalancer: sticky: cookie: @@ -72,19 +79,18 @@ http: responseForwarding: flushInterval: foobar serversTransport: foobar - Service02: + Service03: mirroring: service: foobar maxBodySize: 42 - healthCheck: {} mirrors: - name: foobar percent: 42 - name: foobar percent: 42 - Service03: - weighted: healthCheck: {} + Service04: + weighted: services: - name: foobar weight: 42 @@ -96,16 +102,12 @@ http: secure: true httpOnly: true sameSite: foobar - Service04: - failover: - service: foobar - fallback: foobar healthCheck: {} middlewares: - Middleware00: + Middleware01: addPrefix: prefix: foobar - Middleware01: + Middleware02: basicAuth: users: - foobar @@ -114,34 +116,34 @@ http: realm: foobar removeHeader: true headerField: foobar - Middleware02: + Middleware03: buffering: maxRequestBodyBytes: 42 memRequestBodyBytes: 42 maxResponseBodyBytes: 42 memResponseBodyBytes: 42 retryExpression: foobar - Middleware03: + Middleware04: chain: middlewares: - foobar - foobar - Middleware04: + Middleware05: circuitBreaker: expression: foobar checkPeriod: 42s fallbackDuration: 42s recoveryDuration: 42s - Middleware05: + Middleware06: compress: excludedContentTypes: - foobar - foobar minResponseBodyBytes: 42 - Middleware06: + Middleware07: contentType: autoDetect: true - Middleware07: + Middleware08: digestAuth: users: - foobar @@ -150,14 +152,14 @@ http: removeHeader: true realm: foobar headerField: foobar - Middleware08: + Middleware09: errors: status: - foobar - foobar service: foobar query: foobar - Middleware09: + Middleware10: forwardAuth: address: foobar tls: @@ -174,7 +176,7 @@ http: authRequestHeaders: - foobar - foobar - Middleware10: + Middleware11: headers: customRequestHeaders: name0: foobar @@ -228,7 +230,17 @@ http: featurePolicy: foobar permissionsPolicy: foobar isDevelopment: true - Middleware11: + Middleware12: + ipAllowList: + sourceRange: + - foobar + - foobar + ipStrategy: + depth: 42 + excludedIPs: + - foobar + - foobar + Middleware13: ipWhiteList: sourceRange: - foobar @@ -238,7 +250,7 @@ http: excludedIPs: - foobar - foobar - Middleware12: + Middleware14: inFlightReq: amount: 42 sourceCriterion: @@ -249,13 +261,14 @@ http: - foobar requestHeaderName: foobar requestHost: true - Middleware13: + Middleware15: passTLSClientCert: pem: true info: notAfter: true notBefore: true sans: true + serialNumber: true subject: country: true province: true @@ -273,12 +286,15 @@ http: commonName: true serialNumber: true domainComponent: true - serialNumber: true - Middleware14: + Middleware16: plugin: - PluginConf: - foo: bar - Middleware15: + PluginConf0: + name0: foobar + name1: foobar + PluginConf1: + name0: foobar + name1: foobar + Middleware17: rateLimit: average: 42 period: 42s @@ -291,48 +307,38 @@ http: - foobar requestHeaderName: foobar requestHost: true - Middleware16: + Middleware18: redirectRegex: regex: foobar replacement: foobar permanent: true - Middleware17: + Middleware19: redirectScheme: scheme: foobar port: foobar permanent: true - Middleware18: + Middleware20: replacePath: path: foobar - Middleware19: + Middleware21: replacePathRegex: regex: foobar replacement: foobar - Middleware20: + Middleware22: retry: attempts: 42 initialInterval: 42s - Middleware21: + Middleware23: stripPrefix: prefixes: - foobar - foobar forceSlash: true - Middleware22: + Middleware24: stripPrefixRegex: regex: - foobar - foobar - Middleware23: - ipAllowList: - sourceRange: - - foobar - - foobar - ipStrategy: - depth: 42 - excludedIPs: - - foobar - - foobar serversTransports: ServersTransport0: serverName: foobar @@ -439,19 +445,19 @@ tcp: - name: foobar weight: 42 middlewares: - TCPMiddleware00: - ipWhiteList: - sourceRange: - - foobar - - foobar TCPMiddleware01: - inFlightConn: - amount: 42 - TCPMiddleware02: ipAllowList: sourceRange: - foobar - foobar + TCPMiddleware02: + ipWhiteList: + sourceRange: + - foobar + - foobar + TCPMiddleware03: + inFlightConn: + amount: 42 udp: routers: UDPRouter0: diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md index 13029dd50..b39dc96ab 100644 --- a/docs/content/reference/dynamic-configuration/kv-ref.md +++ b/docs/content/reference/dynamic-configuration/kv-ref.md @@ -1,150 +1,157 @@ -| `traefik/http/middlewares/Middleware00/addPrefix/prefix` | `foobar` | -| `traefik/http/middlewares/Middleware01/basicAuth/headerField` | `foobar` | -| `traefik/http/middlewares/Middleware01/basicAuth/realm` | `foobar` | -| `traefik/http/middlewares/Middleware01/basicAuth/removeHeader` | `true` | -| `traefik/http/middlewares/Middleware01/basicAuth/users/0` | `foobar` | -| `traefik/http/middlewares/Middleware01/basicAuth/users/1` | `foobar` | -| `traefik/http/middlewares/Middleware01/basicAuth/usersFile` | `foobar` | -| `traefik/http/middlewares/Middleware02/buffering/maxRequestBodyBytes` | `42` | -| `traefik/http/middlewares/Middleware02/buffering/maxResponseBodyBytes` | `42` | -| `traefik/http/middlewares/Middleware02/buffering/memRequestBodyBytes` | `42` | -| `traefik/http/middlewares/Middleware02/buffering/memResponseBodyBytes` | `42` | -| `traefik/http/middlewares/Middleware02/buffering/retryExpression` | `foobar` | -| `traefik/http/middlewares/Middleware03/chain/middlewares/0` | `foobar` | -| `traefik/http/middlewares/Middleware03/chain/middlewares/1` | `foobar` | -| `traefik/http/middlewares/Middleware04/circuitBreaker/checkPeriod` | `42s` | -| `traefik/http/middlewares/Middleware04/circuitBreaker/expression` | `foobar` | -| `traefik/http/middlewares/Middleware04/circuitBreaker/fallbackDuration` | `42s` | -| `traefik/http/middlewares/Middleware04/circuitBreaker/recoveryDuration` | `42s` | -| `traefik/http/middlewares/Middleware05/compress/excludedContentTypes/0` | `foobar` | -| `traefik/http/middlewares/Middleware05/compress/excludedContentTypes/1` | `foobar` | -| `traefik/http/middlewares/Middleware05/compress/minResponseBodyBytes` | `42` | -| `traefik/http/middlewares/Middleware06/contentType/autoDetect` | `true` | -| `traefik/http/middlewares/Middleware07/digestAuth/headerField` | `foobar` | -| `traefik/http/middlewares/Middleware07/digestAuth/realm` | `foobar` | -| `traefik/http/middlewares/Middleware07/digestAuth/removeHeader` | `true` | -| `traefik/http/middlewares/Middleware07/digestAuth/users/0` | `foobar` | -| `traefik/http/middlewares/Middleware07/digestAuth/users/1` | `foobar` | -| `traefik/http/middlewares/Middleware07/digestAuth/usersFile` | `foobar` | -| `traefik/http/middlewares/Middleware08/errors/query` | `foobar` | -| `traefik/http/middlewares/Middleware08/errors/service` | `foobar` | -| `traefik/http/middlewares/Middleware08/errors/status/0` | `foobar` | -| `traefik/http/middlewares/Middleware08/errors/status/1` | `foobar` | -| `traefik/http/middlewares/Middleware09/forwardAuth/address` | `foobar` | -| `traefik/http/middlewares/Middleware09/forwardAuth/authRequestHeaders/0` | `foobar` | -| `traefik/http/middlewares/Middleware09/forwardAuth/authRequestHeaders/1` | `foobar` | -| `traefik/http/middlewares/Middleware09/forwardAuth/authResponseHeaders/0` | `foobar` | -| `traefik/http/middlewares/Middleware09/forwardAuth/authResponseHeaders/1` | `foobar` | -| `traefik/http/middlewares/Middleware09/forwardAuth/authResponseHeadersRegex` | `foobar` | -| `traefik/http/middlewares/Middleware09/forwardAuth/tls/ca` | `foobar` | -| `traefik/http/middlewares/Middleware09/forwardAuth/tls/caOptional` | `true` | -| `traefik/http/middlewares/Middleware09/forwardAuth/tls/cert` | `foobar` | -| `traefik/http/middlewares/Middleware09/forwardAuth/tls/insecureSkipVerify` | `true` | -| `traefik/http/middlewares/Middleware09/forwardAuth/tls/key` | `foobar` | -| `traefik/http/middlewares/Middleware09/forwardAuth/trustForwardHeader` | `true` | -| `traefik/http/middlewares/Middleware10/headers/accessControlAllowCredentials` | `true` | -| `traefik/http/middlewares/Middleware10/headers/accessControlAllowHeaders/0` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/accessControlAllowHeaders/1` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/accessControlAllowMethods/0` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/accessControlAllowMethods/1` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/accessControlAllowOriginList/0` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/accessControlAllowOriginList/1` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/accessControlAllowOriginListRegex/0` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/accessControlAllowOriginListRegex/1` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/accessControlExposeHeaders/0` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/accessControlExposeHeaders/1` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/accessControlMaxAge` | `42` | -| `traefik/http/middlewares/Middleware10/headers/addVaryHeader` | `true` | -| `traefik/http/middlewares/Middleware10/headers/allowedHosts/0` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/allowedHosts/1` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/browserXssFilter` | `true` | -| `traefik/http/middlewares/Middleware10/headers/contentSecurityPolicy` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/contentTypeNosniff` | `true` | -| `traefik/http/middlewares/Middleware10/headers/customBrowserXSSValue` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/customFrameOptionsValue` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/customRequestHeaders/name0` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/customRequestHeaders/name1` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/customResponseHeaders/name0` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/customResponseHeaders/name1` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/featurePolicy` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/forceSTSHeader` | `true` | -| `traefik/http/middlewares/Middleware10/headers/frameDeny` | `true` | -| `traefik/http/middlewares/Middleware10/headers/hostsProxyHeaders/0` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/hostsProxyHeaders/1` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/isDevelopment` | `true` | -| `traefik/http/middlewares/Middleware10/headers/permissionsPolicy` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/publicKey` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/referrerPolicy` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/sslForceHost` | `true` | -| `traefik/http/middlewares/Middleware10/headers/sslHost` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/sslProxyHeaders/name0` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/sslProxyHeaders/name1` | `foobar` | -| `traefik/http/middlewares/Middleware10/headers/sslRedirect` | `true` | -| `traefik/http/middlewares/Middleware10/headers/sslTemporaryRedirect` | `true` | -| `traefik/http/middlewares/Middleware10/headers/stsIncludeSubdomains` | `true` | -| `traefik/http/middlewares/Middleware10/headers/stsPreload` | `true` | -| `traefik/http/middlewares/Middleware10/headers/stsSeconds` | `42` | -| `traefik/http/middlewares/Middleware11/ipWhiteList/ipStrategy/depth` | `42` | -| `traefik/http/middlewares/Middleware11/ipWhiteList/ipStrategy/excludedIPs/0` | `foobar` | -| `traefik/http/middlewares/Middleware11/ipWhiteList/ipStrategy/excludedIPs/1` | `foobar` | -| `traefik/http/middlewares/Middleware11/ipWhiteList/sourceRange/0` | `foobar` | -| `traefik/http/middlewares/Middleware11/ipWhiteList/sourceRange/1` | `foobar` | -| `traefik/http/middlewares/Middleware12/inFlightReq/amount` | `42` | -| `traefik/http/middlewares/Middleware12/inFlightReq/sourceCriterion/ipStrategy/depth` | `42` | -| `traefik/http/middlewares/Middleware12/inFlightReq/sourceCriterion/ipStrategy/excludedIPs/0` | `foobar` | -| `traefik/http/middlewares/Middleware12/inFlightReq/sourceCriterion/ipStrategy/excludedIPs/1` | `foobar` | -| `traefik/http/middlewares/Middleware12/inFlightReq/sourceCriterion/requestHeaderName` | `foobar` | -| `traefik/http/middlewares/Middleware12/inFlightReq/sourceCriterion/requestHost` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/issuer/commonName` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/issuer/country` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/issuer/domainComponent` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/issuer/locality` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/issuer/organization` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/issuer/province` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/issuer/serialNumber` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/notAfter` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/notBefore` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/sans` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/serialNumber` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/commonName` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/country` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/domainComponent` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/locality` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/organization` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/organizationalUnit` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/province` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/serialNumber` | `true` | -| `traefik/http/middlewares/Middleware13/passTLSClientCert/pem` | `true` | -| `traefik/http/middlewares/Middleware14/plugin/PluginConf/foo` | `bar` | -| `traefik/http/middlewares/Middleware15/rateLimit/average` | `42` | -| `traefik/http/middlewares/Middleware15/rateLimit/burst` | `42` | -| `traefik/http/middlewares/Middleware15/rateLimit/period` | `42s` | -| `traefik/http/middlewares/Middleware15/rateLimit/sourceCriterion/ipStrategy/depth` | `42` | -| `traefik/http/middlewares/Middleware15/rateLimit/sourceCriterion/ipStrategy/excludedIPs/0` | `foobar` | -| `traefik/http/middlewares/Middleware15/rateLimit/sourceCriterion/ipStrategy/excludedIPs/1` | `foobar` | -| `traefik/http/middlewares/Middleware15/rateLimit/sourceCriterion/requestHeaderName` | `foobar` | -| `traefik/http/middlewares/Middleware15/rateLimit/sourceCriterion/requestHost` | `true` | -| `traefik/http/middlewares/Middleware16/redirectRegex/permanent` | `true` | -| `traefik/http/middlewares/Middleware16/redirectRegex/regex` | `foobar` | -| `traefik/http/middlewares/Middleware16/redirectRegex/replacement` | `foobar` | -| `traefik/http/middlewares/Middleware17/redirectScheme/permanent` | `true` | -| `traefik/http/middlewares/Middleware17/redirectScheme/port` | `foobar` | -| `traefik/http/middlewares/Middleware17/redirectScheme/scheme` | `foobar` | -| `traefik/http/middlewares/Middleware18/replacePath/path` | `foobar` | -| `traefik/http/middlewares/Middleware19/replacePathRegex/regex` | `foobar` | -| `traefik/http/middlewares/Middleware19/replacePathRegex/replacement` | `foobar` | -| `traefik/http/middlewares/Middleware20/retry/attempts` | `42` | -| `traefik/http/middlewares/Middleware20/retry/initialInterval` | `42s` | -| `traefik/http/middlewares/Middleware21/stripPrefix/forceSlash` | `true` | -| `traefik/http/middlewares/Middleware21/stripPrefix/prefixes/0` | `foobar` | -| `traefik/http/middlewares/Middleware21/stripPrefix/prefixes/1` | `foobar` | -| `traefik/http/middlewares/Middleware22/stripPrefixRegex/regex/0` | `foobar` | -| `traefik/http/middlewares/Middleware22/stripPrefixRegex/regex/1` | `foobar` | -| `traefik/http/middlewares/Middleware23/ipAllowList/ipStrategy/depth` | `42` | -| `traefik/http/middlewares/Middleware23/ipAllowList/ipStrategy/excludedIPs/0` | `foobar` | -| `traefik/http/middlewares/Middleware23/ipAllowList/ipStrategy/excludedIPs/1` | `foobar` | -| `traefik/http/middlewares/Middleware23/ipAllowList/sourceRange/0` | `foobar` | -| `traefik/http/middlewares/Middleware23/ipAllowList/sourceRange/1` | `foobar` | + +| `traefik/http/middlewares/Middleware01/addPrefix/prefix` | `foobar` | +| `traefik/http/middlewares/Middleware02/basicAuth/headerField` | `foobar` | +| `traefik/http/middlewares/Middleware02/basicAuth/realm` | `foobar` | +| `traefik/http/middlewares/Middleware02/basicAuth/removeHeader` | `true` | +| `traefik/http/middlewares/Middleware02/basicAuth/users/0` | `foobar` | +| `traefik/http/middlewares/Middleware02/basicAuth/users/1` | `foobar` | +| `traefik/http/middlewares/Middleware02/basicAuth/usersFile` | `foobar` | +| `traefik/http/middlewares/Middleware03/buffering/maxRequestBodyBytes` | `42` | +| `traefik/http/middlewares/Middleware03/buffering/maxResponseBodyBytes` | `42` | +| `traefik/http/middlewares/Middleware03/buffering/memRequestBodyBytes` | `42` | +| `traefik/http/middlewares/Middleware03/buffering/memResponseBodyBytes` | `42` | +| `traefik/http/middlewares/Middleware03/buffering/retryExpression` | `foobar` | +| `traefik/http/middlewares/Middleware04/chain/middlewares/0` | `foobar` | +| `traefik/http/middlewares/Middleware04/chain/middlewares/1` | `foobar` | +| `traefik/http/middlewares/Middleware05/circuitBreaker/checkPeriod` | `42s` | +| `traefik/http/middlewares/Middleware05/circuitBreaker/expression` | `foobar` | +| `traefik/http/middlewares/Middleware05/circuitBreaker/fallbackDuration` | `42s` | +| `traefik/http/middlewares/Middleware05/circuitBreaker/recoveryDuration` | `42s` | +| `traefik/http/middlewares/Middleware06/compress/excludedContentTypes/0` | `foobar` | +| `traefik/http/middlewares/Middleware06/compress/excludedContentTypes/1` | `foobar` | +| `traefik/http/middlewares/Middleware06/compress/minResponseBodyBytes` | `42` | +| `traefik/http/middlewares/Middleware07/contentType/autoDetect` | `true` | +| `traefik/http/middlewares/Middleware08/digestAuth/headerField` | `foobar` | +| `traefik/http/middlewares/Middleware08/digestAuth/realm` | `foobar` | +| `traefik/http/middlewares/Middleware08/digestAuth/removeHeader` | `true` | +| `traefik/http/middlewares/Middleware08/digestAuth/users/0` | `foobar` | +| `traefik/http/middlewares/Middleware08/digestAuth/users/1` | `foobar` | +| `traefik/http/middlewares/Middleware08/digestAuth/usersFile` | `foobar` | +| `traefik/http/middlewares/Middleware09/errors/query` | `foobar` | +| `traefik/http/middlewares/Middleware09/errors/service` | `foobar` | +| `traefik/http/middlewares/Middleware09/errors/status/0` | `foobar` | +| `traefik/http/middlewares/Middleware09/errors/status/1` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/address` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/authRequestHeaders/0` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/authRequestHeaders/1` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeaders/0` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeaders/1` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeadersRegex` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/tls/ca` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/tls/caOptional` | `true` | +| `traefik/http/middlewares/Middleware10/forwardAuth/tls/cert` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/tls/insecureSkipVerify` | `true` | +| `traefik/http/middlewares/Middleware10/forwardAuth/tls/key` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/trustForwardHeader` | `true` | +| `traefik/http/middlewares/Middleware11/headers/accessControlAllowCredentials` | `true` | +| `traefik/http/middlewares/Middleware11/headers/accessControlAllowHeaders/0` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/accessControlAllowHeaders/1` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/accessControlAllowMethods/0` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/accessControlAllowMethods/1` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/accessControlAllowOriginList/0` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/accessControlAllowOriginList/1` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/accessControlAllowOriginListRegex/0` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/accessControlAllowOriginListRegex/1` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/accessControlExposeHeaders/0` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/accessControlExposeHeaders/1` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/accessControlMaxAge` | `42` | +| `traefik/http/middlewares/Middleware11/headers/addVaryHeader` | `true` | +| `traefik/http/middlewares/Middleware11/headers/allowedHosts/0` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/allowedHosts/1` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/browserXssFilter` | `true` | +| `traefik/http/middlewares/Middleware11/headers/contentSecurityPolicy` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/contentTypeNosniff` | `true` | +| `traefik/http/middlewares/Middleware11/headers/customBrowserXSSValue` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/customFrameOptionsValue` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/customRequestHeaders/name0` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/customRequestHeaders/name1` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/customResponseHeaders/name0` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/customResponseHeaders/name1` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/featurePolicy` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/forceSTSHeader` | `true` | +| `traefik/http/middlewares/Middleware11/headers/frameDeny` | `true` | +| `traefik/http/middlewares/Middleware11/headers/hostsProxyHeaders/0` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/hostsProxyHeaders/1` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/isDevelopment` | `true` | +| `traefik/http/middlewares/Middleware11/headers/permissionsPolicy` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/publicKey` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/referrerPolicy` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/sslForceHost` | `true` | +| `traefik/http/middlewares/Middleware11/headers/sslHost` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/sslProxyHeaders/name0` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/sslProxyHeaders/name1` | `foobar` | +| `traefik/http/middlewares/Middleware11/headers/sslRedirect` | `true` | +| `traefik/http/middlewares/Middleware11/headers/sslTemporaryRedirect` | `true` | +| `traefik/http/middlewares/Middleware11/headers/stsIncludeSubdomains` | `true` | +| `traefik/http/middlewares/Middleware11/headers/stsPreload` | `true` | +| `traefik/http/middlewares/Middleware11/headers/stsSeconds` | `42` | +| `traefik/http/middlewares/Middleware12/ipAllowList/ipStrategy/depth` | `42` | +| `traefik/http/middlewares/Middleware12/ipAllowList/ipStrategy/excludedIPs/0` | `foobar` | +| `traefik/http/middlewares/Middleware12/ipAllowList/ipStrategy/excludedIPs/1` | `foobar` | +| `traefik/http/middlewares/Middleware12/ipAllowList/sourceRange/0` | `foobar` | +| `traefik/http/middlewares/Middleware12/ipAllowList/sourceRange/1` | `foobar` | +| `traefik/http/middlewares/Middleware13/ipWhiteList/ipStrategy/depth` | `42` | +| `traefik/http/middlewares/Middleware13/ipWhiteList/ipStrategy/excludedIPs/0` | `foobar` | +| `traefik/http/middlewares/Middleware13/ipWhiteList/ipStrategy/excludedIPs/1` | `foobar` | +| `traefik/http/middlewares/Middleware13/ipWhiteList/sourceRange/0` | `foobar` | +| `traefik/http/middlewares/Middleware13/ipWhiteList/sourceRange/1` | `foobar` | +| `traefik/http/middlewares/Middleware14/inFlightReq/amount` | `42` | +| `traefik/http/middlewares/Middleware14/inFlightReq/sourceCriterion/ipStrategy/depth` | `42` | +| `traefik/http/middlewares/Middleware14/inFlightReq/sourceCriterion/ipStrategy/excludedIPs/0` | `foobar` | +| `traefik/http/middlewares/Middleware14/inFlightReq/sourceCriterion/ipStrategy/excludedIPs/1` | `foobar` | +| `traefik/http/middlewares/Middleware14/inFlightReq/sourceCriterion/requestHeaderName` | `foobar` | +| `traefik/http/middlewares/Middleware14/inFlightReq/sourceCriterion/requestHost` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/issuer/commonName` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/issuer/country` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/issuer/domainComponent` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/issuer/locality` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/issuer/organization` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/issuer/province` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/issuer/serialNumber` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/notAfter` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/notBefore` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/sans` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/serialNumber` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/subject/commonName` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/subject/country` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/subject/domainComponent` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/subject/locality` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/subject/organization` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/subject/organizationalUnit` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/subject/province` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/info/subject/serialNumber` | `true` | +| `traefik/http/middlewares/Middleware15/passTLSClientCert/pem` | `true` | +| `traefik/http/middlewares/Middleware16/plugin/PluginConf0/name0` | `foobar` | +| `traefik/http/middlewares/Middleware16/plugin/PluginConf0/name1` | `foobar` | +| `traefik/http/middlewares/Middleware16/plugin/PluginConf1/name0` | `foobar` | +| `traefik/http/middlewares/Middleware16/plugin/PluginConf1/name1` | `foobar` | +| `traefik/http/middlewares/Middleware17/rateLimit/average` | `42` | +| `traefik/http/middlewares/Middleware17/rateLimit/burst` | `42` | +| `traefik/http/middlewares/Middleware17/rateLimit/period` | `42s` | +| `traefik/http/middlewares/Middleware17/rateLimit/sourceCriterion/ipStrategy/depth` | `42` | +| `traefik/http/middlewares/Middleware17/rateLimit/sourceCriterion/ipStrategy/excludedIPs/0` | `foobar` | +| `traefik/http/middlewares/Middleware17/rateLimit/sourceCriterion/ipStrategy/excludedIPs/1` | `foobar` | +| `traefik/http/middlewares/Middleware17/rateLimit/sourceCriterion/requestHeaderName` | `foobar` | +| `traefik/http/middlewares/Middleware17/rateLimit/sourceCriterion/requestHost` | `true` | +| `traefik/http/middlewares/Middleware18/redirectRegex/permanent` | `true` | +| `traefik/http/middlewares/Middleware18/redirectRegex/regex` | `foobar` | +| `traefik/http/middlewares/Middleware18/redirectRegex/replacement` | `foobar` | +| `traefik/http/middlewares/Middleware19/redirectScheme/permanent` | `true` | +| `traefik/http/middlewares/Middleware19/redirectScheme/port` | `foobar` | +| `traefik/http/middlewares/Middleware19/redirectScheme/scheme` | `foobar` | +| `traefik/http/middlewares/Middleware20/replacePath/path` | `foobar` | +| `traefik/http/middlewares/Middleware21/replacePathRegex/regex` | `foobar` | +| `traefik/http/middlewares/Middleware21/replacePathRegex/replacement` | `foobar` | +| `traefik/http/middlewares/Middleware22/retry/attempts` | `42` | +| `traefik/http/middlewares/Middleware22/retry/initialInterval` | `42s` | +| `traefik/http/middlewares/Middleware23/stripPrefix/forceSlash` | `true` | +| `traefik/http/middlewares/Middleware23/stripPrefix/prefixes/0` | `foobar` | +| `traefik/http/middlewares/Middleware23/stripPrefix/prefixes/1` | `foobar` | +| `traefik/http/middlewares/Middleware24/stripPrefixRegex/regex/0` | `foobar` | +| `traefik/http/middlewares/Middleware24/stripPrefixRegex/regex/1` | `foobar` | | `traefik/http/routers/Router0/entryPoints/0` | `foobar` | | `traefik/http/routers/Router0/entryPoints/1` | `foobar` | | `traefik/http/routers/Router0/middlewares/0` | `foobar` | @@ -207,47 +214,49 @@ | `traefik/http/serversTransports/ServersTransport1/rootCAs/0` | `foobar` | | `traefik/http/serversTransports/ServersTransport1/rootCAs/1` | `foobar` | | `traefik/http/serversTransports/ServersTransport1/serverName` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/healthCheck/followRedirects` | `true` | -| `traefik/http/services/Service01/loadBalancer/healthCheck/headers/name0` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/healthCheck/headers/name1` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/healthCheck/hostname` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/healthCheck/interval` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/healthCheck/method` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/healthCheck/path` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/healthCheck/port` | `42` | -| `traefik/http/services/Service01/loadBalancer/healthCheck/scheme` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/healthCheck/timeout` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/passHostHeader` | `true` | -| `traefik/http/services/Service01/loadBalancer/responseForwarding/flushInterval` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/servers/0/url` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/servers/1/url` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/serversTransport` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/sticky/cookie/httpOnly` | `true` | -| `traefik/http/services/Service01/loadBalancer/sticky/cookie/name` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/sticky/cookie/sameSite` | `foobar` | -| `traefik/http/services/Service01/loadBalancer/sticky/cookie/secure` | `true` | -| `traefik/http/services/Service02/mirroring/healthCheck` | `` | -| `traefik/http/services/Service02/mirroring/maxBodySize` | `42` | -| `traefik/http/services/Service02/mirroring/mirrors/0/name` | `foobar` | -| `traefik/http/services/Service02/mirroring/mirrors/0/percent` | `42` | -| `traefik/http/services/Service02/mirroring/mirrors/1/name` | `foobar` | -| `traefik/http/services/Service02/mirroring/mirrors/1/percent` | `42` | -| `traefik/http/services/Service02/mirroring/service` | `foobar` | -| `traefik/http/services/Service03/weighted/healthCheck` | `` | -| `traefik/http/services/Service03/weighted/services/0/name` | `foobar` | -| `traefik/http/services/Service03/weighted/services/0/weight` | `42` | -| `traefik/http/services/Service03/weighted/services/1/name` | `foobar` | -| `traefik/http/services/Service03/weighted/services/1/weight` | `42` | -| `traefik/http/services/Service03/weighted/sticky/cookie/httpOnly` | `true` | -| `traefik/http/services/Service03/weighted/sticky/cookie/name` | `foobar` | -| `traefik/http/services/Service03/weighted/sticky/cookie/sameSite` | `foobar` | -| `traefik/http/services/Service03/weighted/sticky/cookie/secure` | `true` | -| `traefik/http/services/Service04/failover/fallback` | `foobar` | -| `traefik/http/services/Service04/failover/healthCheck` | `` | -| `traefik/http/services/Service04/failover/service` | `foobar` | -| `traefik/tcp/middlewares/TCPMiddleware00/ipWhiteList/sourceRange/0` | `foobar` | -| `traefik/tcp/middlewares/TCPMiddleware00/ipWhiteList/sourceRange/1` | `foobar` | -| `traefik/tcp/middlewares/TCPMiddleware01/inFlightConn/amount` | `42` | +| `traefik/http/services/Service01/failover/fallback` | `foobar` | +| `traefik/http/services/Service01/failover/healthCheck` | `` | +| `traefik/http/services/Service01/failover/service` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/followRedirects` | `true` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/headers/name0` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/headers/name1` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/hostname` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/interval` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/method` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/path` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/port` | `42` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/scheme` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/timeout` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/passHostHeader` | `true` | +| `traefik/http/services/Service02/loadBalancer/responseForwarding/flushInterval` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/servers/0/url` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/servers/1/url` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/serversTransport` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/sticky/cookie/httpOnly` | `true` | +| `traefik/http/services/Service02/loadBalancer/sticky/cookie/name` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/sticky/cookie/sameSite` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/sticky/cookie/secure` | `true` | +| `traefik/http/services/Service03/mirroring/healthCheck` | `` | +| `traefik/http/services/Service03/mirroring/maxBodySize` | `42` | +| `traefik/http/services/Service03/mirroring/mirrors/0/name` | `foobar` | +| `traefik/http/services/Service03/mirroring/mirrors/0/percent` | `42` | +| `traefik/http/services/Service03/mirroring/mirrors/1/name` | `foobar` | +| `traefik/http/services/Service03/mirroring/mirrors/1/percent` | `42` | +| `traefik/http/services/Service03/mirroring/service` | `foobar` | +| `traefik/http/services/Service04/weighted/healthCheck` | `` | +| `traefik/http/services/Service04/weighted/services/0/name` | `foobar` | +| `traefik/http/services/Service04/weighted/services/0/weight` | `42` | +| `traefik/http/services/Service04/weighted/services/1/name` | `foobar` | +| `traefik/http/services/Service04/weighted/services/1/weight` | `42` | +| `traefik/http/services/Service04/weighted/sticky/cookie/httpOnly` | `true` | +| `traefik/http/services/Service04/weighted/sticky/cookie/name` | `foobar` | +| `traefik/http/services/Service04/weighted/sticky/cookie/sameSite` | `foobar` | +| `traefik/http/services/Service04/weighted/sticky/cookie/secure` | `true` | +| `traefik/tcp/middlewares/TCPMiddleware01/ipAllowList/sourceRange/0` | `foobar` | +| `traefik/tcp/middlewares/TCPMiddleware01/ipAllowList/sourceRange/1` | `foobar` | +| `traefik/tcp/middlewares/TCPMiddleware02/ipWhiteList/sourceRange/0` | `foobar` | +| `traefik/tcp/middlewares/TCPMiddleware02/ipWhiteList/sourceRange/1` | `foobar` | +| `traefik/tcp/middlewares/TCPMiddleware03/inFlightConn/amount` | `42` | | `traefik/tcp/routers/TCPRouter0/entryPoints/0` | `foobar` | | `traefik/tcp/routers/TCPRouter0/entryPoints/1` | `foobar` | | `traefik/tcp/routers/TCPRouter0/middlewares/0` | `foobar` | diff --git a/docs/content/reference/dynamic-configuration/marathon-labels.json b/docs/content/reference/dynamic-configuration/marathon-labels.json index 8d4768f8f..55ea30b35 100644 --- a/docs/content/reference/dynamic-configuration/marathon-labels.json +++ b/docs/content/reference/dynamic-configuration/marathon-labels.json @@ -1,129 +1,136 @@ -"traefik.http.middlewares.middleware00.addprefix.prefix": "foobar", -"traefik.http.middlewares.middleware01.basicauth.headerfield": "foobar", -"traefik.http.middlewares.middleware01.basicauth.realm": "foobar", -"traefik.http.middlewares.middleware01.basicauth.removeheader": "true", -"traefik.http.middlewares.middleware01.basicauth.users": "foobar, foobar", -"traefik.http.middlewares.middleware01.basicauth.usersfile": "foobar", -"traefik.http.middlewares.middleware02.buffering.maxrequestbodybytes": "42", -"traefik.http.middlewares.middleware02.buffering.maxresponsebodybytes": "42", -"traefik.http.middlewares.middleware02.buffering.memrequestbodybytes": "42", -"traefik.http.middlewares.middleware02.buffering.memresponsebodybytes": "42", -"traefik.http.middlewares.middleware02.buffering.retryexpression": "foobar", -"traefik.http.middlewares.middleware03.chain.middlewares": "foobar, foobar", -"traefik.http.middlewares.middleware04.circuitbreaker.expression": "foobar", -"traefik.http.middlewares.middleware04.circuitbreaker.checkperiod": "42s", -"traefik.http.middlewares.middleware04.circuitbreaker.fallbackduration": "42s", -"traefik.http.middlewares.middleware04.circuitbreaker.recoveryduration": "42s", -"traefik.http.middlewares.middleware05.compress": "true", -"traefik.http.middlewares.middleware05.compress.excludedcontenttypes": "foobar, foobar", -"traefik.http.middlewares.middleware05.compress.minresponsebodybytes": "42", -"traefik.http.middlewares.middleware06.contenttype.autodetect": "true", -"traefik.http.middlewares.middleware07.digestauth.headerfield": "foobar", -"traefik.http.middlewares.middleware07.digestauth.realm": "foobar", -"traefik.http.middlewares.middleware07.digestauth.removeheader": "true", -"traefik.http.middlewares.middleware07.digestauth.users": "foobar, foobar", -"traefik.http.middlewares.middleware07.digestauth.usersfile": "foobar", -"traefik.http.middlewares.middleware08.errors.query": "foobar", -"traefik.http.middlewares.middleware08.errors.service": "foobar", -"traefik.http.middlewares.middleware08.errors.status": "foobar, foobar", -"traefik.http.middlewares.middleware09.forwardauth.address": "foobar", -"traefik.http.middlewares.middleware09.forwardauth.authrequestheaders": "foobar, foobar", -"traefik.http.middlewares.middleware09.forwardauth.authresponseheaders": "foobar, foobar", -"traefik.http.middlewares.middleware09.forwardauth.authresponseheadersregex": "foobar", -"traefik.http.middlewares.middleware09.forwardauth.tls.ca": "foobar", -"traefik.http.middlewares.middleware09.forwardauth.tls.caoptional": "true", -"traefik.http.middlewares.middleware09.forwardauth.tls.cert": "foobar", -"traefik.http.middlewares.middleware09.forwardauth.tls.insecureskipverify": "true", -"traefik.http.middlewares.middleware09.forwardauth.tls.key": "foobar", -"traefik.http.middlewares.middleware09.forwardauth.trustforwardheader": "true", -"traefik.http.middlewares.middleware10.headers.accesscontrolallowcredentials": "true", -"traefik.http.middlewares.middleware10.headers.accesscontrolallowheaders": "foobar, foobar", -"traefik.http.middlewares.middleware10.headers.accesscontrolallowmethods": "foobar, foobar", -"traefik.http.middlewares.middleware10.headers.accesscontrolalloworiginlist": "foobar, foobar", -"traefik.http.middlewares.middleware10.headers.accesscontrolalloworiginlistregex": "foobar, foobar", -"traefik.http.middlewares.middleware10.headers.accesscontrolexposeheaders": "foobar, foobar", -"traefik.http.middlewares.middleware10.headers.accesscontrolmaxage": "42", -"traefik.http.middlewares.middleware10.headers.addvaryheader": "true", -"traefik.http.middlewares.middleware10.headers.allowedhosts": "foobar, foobar", -"traefik.http.middlewares.middleware10.headers.browserxssfilter": "true", -"traefik.http.middlewares.middleware10.headers.contentsecuritypolicy": "foobar", -"traefik.http.middlewares.middleware10.headers.contenttypenosniff": "true", -"traefik.http.middlewares.middleware10.headers.custombrowserxssvalue": "foobar", -"traefik.http.middlewares.middleware10.headers.customframeoptionsvalue": "foobar", -"traefik.http.middlewares.middleware10.headers.customrequestheaders.name0": "foobar", -"traefik.http.middlewares.middleware10.headers.customrequestheaders.name1": "foobar", -"traefik.http.middlewares.middleware10.headers.customresponseheaders.name0": "foobar", -"traefik.http.middlewares.middleware10.headers.customresponseheaders.name1": "foobar", -"traefik.http.middlewares.middleware10.headers.featurepolicy": "foobar", -"traefik.http.middlewares.middleware10.headers.forcestsheader": "true", -"traefik.http.middlewares.middleware10.headers.framedeny": "true", -"traefik.http.middlewares.middleware10.headers.hostsproxyheaders": "foobar, foobar", -"traefik.http.middlewares.middleware10.headers.isdevelopment": "true", -"traefik.http.middlewares.middleware10.headers.permissionspolicy": "foobar", -"traefik.http.middlewares.middleware10.headers.publickey": "foobar", -"traefik.http.middlewares.middleware10.headers.referrerpolicy": "foobar", -"traefik.http.middlewares.middleware10.headers.sslforcehost": "true", -"traefik.http.middlewares.middleware10.headers.sslhost": "foobar", -"traefik.http.middlewares.middleware10.headers.sslproxyheaders.name0": "foobar", -"traefik.http.middlewares.middleware10.headers.sslproxyheaders.name1": "foobar", -"traefik.http.middlewares.middleware10.headers.sslredirect": "true", -"traefik.http.middlewares.middleware10.headers.ssltemporaryredirect": "true", -"traefik.http.middlewares.middleware10.headers.stsincludesubdomains": "true", -"traefik.http.middlewares.middleware10.headers.stspreload": "true", -"traefik.http.middlewares.middleware10.headers.stsseconds": "42", -"traefik.http.middlewares.middleware11.ipwhitelist.ipstrategy.depth": "42", -"traefik.http.middlewares.middleware11.ipwhitelist.ipstrategy.excludedips": "foobar, foobar", -"traefik.http.middlewares.middleware11.ipwhitelist.sourcerange": "foobar, foobar", -"traefik.http.middlewares.middleware12.inflightreq.amount": "42", -"traefik.http.middlewares.middleware12.inflightreq.sourcecriterion.ipstrategy.depth": "42", -"traefik.http.middlewares.middleware12.inflightreq.sourcecriterion.ipstrategy.excludedips": "foobar, foobar", -"traefik.http.middlewares.middleware12.inflightreq.sourcecriterion.requestheadername": "foobar", -"traefik.http.middlewares.middleware12.inflightreq.sourcecriterion.requesthost": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.issuer.commonname": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.issuer.country": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.issuer.domaincomponent": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.issuer.locality": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.issuer.organization": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.issuer.province": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.issuer.serialnumber": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.notafter": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.notbefore": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.sans": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.serialnumber": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.commonname": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.country": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.domaincomponent": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.locality": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.organization": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.organizationalunit": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.province": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.serialnumber": "true", -"traefik.http.middlewares.middleware13.passtlsclientcert.pem": "true", -"traefik.http.middlewares.middleware14.plugin.foobar.foo": "bar", -"traefik.http.middlewares.middleware15.ratelimit.average": "42", -"traefik.http.middlewares.middleware15.ratelimit.burst": "42", -"traefik.http.middlewares.middleware15.ratelimit.period": "42", -"traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.ipstrategy.depth": "42", -"traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.ipstrategy.excludedips": "foobar, foobar", -"traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.requestheadername": "foobar", -"traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.requesthost": "true", -"traefik.http.middlewares.middleware16.redirectregex.permanent": "true", -"traefik.http.middlewares.middleware16.redirectregex.regex": "foobar", -"traefik.http.middlewares.middleware16.redirectregex.replacement": "foobar", -"traefik.http.middlewares.middleware17.redirectscheme.permanent": "true", -"traefik.http.middlewares.middleware17.redirectscheme.port": "foobar", -"traefik.http.middlewares.middleware17.redirectscheme.scheme": "foobar", -"traefik.http.middlewares.middleware18.replacepath.path": "foobar", -"traefik.http.middlewares.middleware19.replacepathregex.regex": "foobar", -"traefik.http.middlewares.middleware19.replacepathregex.replacement": "foobar", -"traefik.http.middlewares.middleware20.retry.attempts": "42", -"traefik.http.middlewares.middleware20.retry.initialinterval": "42", -"traefik.http.middlewares.middleware21.stripprefix.forceslash": "true", -"traefik.http.middlewares.middleware21.stripprefix.prefixes": "foobar, foobar", -"traefik.http.middlewares.middleware22.stripprefixregex.regex": "foobar, foobar", -"traefik.http.middlewares.middleware23.ipallowlist.ipstrategy.depth": "42", -"traefik.http.middlewares.middleware23.ipallowlist.ipstrategy.excludedips": "foobar, foobar", -"traefik.http.middlewares.middleware23.ipallowlist.sourcerange": "foobar, foobar", +// CODE GENERATED AUTOMATICALLY +// THIS FILE MUST NOT BE EDITED BY HAND +"traefik.http.middlewares.middleware01.addprefix.prefix": "foobar", +"traefik.http.middlewares.middleware02.basicauth.headerfield": "foobar", +"traefik.http.middlewares.middleware02.basicauth.realm": "foobar", +"traefik.http.middlewares.middleware02.basicauth.removeheader": "true", +"traefik.http.middlewares.middleware02.basicauth.users": "foobar, foobar", +"traefik.http.middlewares.middleware02.basicauth.usersfile": "foobar", +"traefik.http.middlewares.middleware03.buffering.maxrequestbodybytes": "42", +"traefik.http.middlewares.middleware03.buffering.maxresponsebodybytes": "42", +"traefik.http.middlewares.middleware03.buffering.memrequestbodybytes": "42", +"traefik.http.middlewares.middleware03.buffering.memresponsebodybytes": "42", +"traefik.http.middlewares.middleware03.buffering.retryexpression": "foobar", +"traefik.http.middlewares.middleware04.chain.middlewares": "foobar, foobar", +"traefik.http.middlewares.middleware05.circuitbreaker.checkperiod": "42s", +"traefik.http.middlewares.middleware05.circuitbreaker.expression": "foobar", +"traefik.http.middlewares.middleware05.circuitbreaker.fallbackduration": "42s", +"traefik.http.middlewares.middleware05.circuitbreaker.recoveryduration": "42s", +"traefik.http.middlewares.middleware06.compress": "true", +"traefik.http.middlewares.middleware06.compress.excludedcontenttypes": "foobar, foobar", +"traefik.http.middlewares.middleware06.compress.minresponsebodybytes": "42", +"traefik.http.middlewares.middleware07.contenttype.autodetect": "true", +"traefik.http.middlewares.middleware08.digestauth.headerfield": "foobar", +"traefik.http.middlewares.middleware08.digestauth.realm": "foobar", +"traefik.http.middlewares.middleware08.digestauth.removeheader": "true", +"traefik.http.middlewares.middleware08.digestauth.users": "foobar, foobar", +"traefik.http.middlewares.middleware08.digestauth.usersfile": "foobar", +"traefik.http.middlewares.middleware09.errors.query": "foobar", +"traefik.http.middlewares.middleware09.errors.service": "foobar", +"traefik.http.middlewares.middleware09.errors.status": "foobar, foobar", +"traefik.http.middlewares.middleware10.forwardauth.address": "foobar", +"traefik.http.middlewares.middleware10.forwardauth.authrequestheaders": "foobar, foobar", +"traefik.http.middlewares.middleware10.forwardauth.authresponseheaders": "foobar, foobar", +"traefik.http.middlewares.middleware10.forwardauth.authresponseheadersregex": "foobar", +"traefik.http.middlewares.middleware10.forwardauth.tls.ca": "foobar", +"traefik.http.middlewares.middleware10.forwardauth.tls.caoptional": "true", +"traefik.http.middlewares.middleware10.forwardauth.tls.cert": "foobar", +"traefik.http.middlewares.middleware10.forwardauth.tls.insecureskipverify": "true", +"traefik.http.middlewares.middleware10.forwardauth.tls.key": "foobar", +"traefik.http.middlewares.middleware10.forwardauth.trustforwardheader": "true", +"traefik.http.middlewares.middleware11.headers.accesscontrolallowcredentials": "true", +"traefik.http.middlewares.middleware11.headers.accesscontrolallowheaders": "foobar, foobar", +"traefik.http.middlewares.middleware11.headers.accesscontrolallowmethods": "foobar, foobar", +"traefik.http.middlewares.middleware11.headers.accesscontrolalloworiginlist": "foobar, foobar", +"traefik.http.middlewares.middleware11.headers.accesscontrolalloworiginlistregex": "foobar, foobar", +"traefik.http.middlewares.middleware11.headers.accesscontrolexposeheaders": "foobar, foobar", +"traefik.http.middlewares.middleware11.headers.accesscontrolmaxage": "42", +"traefik.http.middlewares.middleware11.headers.addvaryheader": "true", +"traefik.http.middlewares.middleware11.headers.allowedhosts": "foobar, foobar", +"traefik.http.middlewares.middleware11.headers.browserxssfilter": "true", +"traefik.http.middlewares.middleware11.headers.contentsecuritypolicy": "foobar", +"traefik.http.middlewares.middleware11.headers.contenttypenosniff": "true", +"traefik.http.middlewares.middleware11.headers.custombrowserxssvalue": "foobar", +"traefik.http.middlewares.middleware11.headers.customframeoptionsvalue": "foobar", +"traefik.http.middlewares.middleware11.headers.customrequestheaders.name0": "foobar", +"traefik.http.middlewares.middleware11.headers.customrequestheaders.name1": "foobar", +"traefik.http.middlewares.middleware11.headers.customresponseheaders.name0": "foobar", +"traefik.http.middlewares.middleware11.headers.customresponseheaders.name1": "foobar", +"traefik.http.middlewares.middleware11.headers.featurepolicy": "foobar", +"traefik.http.middlewares.middleware11.headers.forcestsheader": "true", +"traefik.http.middlewares.middleware11.headers.framedeny": "true", +"traefik.http.middlewares.middleware11.headers.hostsproxyheaders": "foobar, foobar", +"traefik.http.middlewares.middleware11.headers.isdevelopment": "true", +"traefik.http.middlewares.middleware11.headers.permissionspolicy": "foobar", +"traefik.http.middlewares.middleware11.headers.publickey": "foobar", +"traefik.http.middlewares.middleware11.headers.referrerpolicy": "foobar", +"traefik.http.middlewares.middleware11.headers.sslforcehost": "true", +"traefik.http.middlewares.middleware11.headers.sslhost": "foobar", +"traefik.http.middlewares.middleware11.headers.sslproxyheaders.name0": "foobar", +"traefik.http.middlewares.middleware11.headers.sslproxyheaders.name1": "foobar", +"traefik.http.middlewares.middleware11.headers.sslredirect": "true", +"traefik.http.middlewares.middleware11.headers.ssltemporaryredirect": "true", +"traefik.http.middlewares.middleware11.headers.stsincludesubdomains": "true", +"traefik.http.middlewares.middleware11.headers.stspreload": "true", +"traefik.http.middlewares.middleware11.headers.stsseconds": "42", +"traefik.http.middlewares.middleware12.ipallowlist.ipstrategy": "true", +"traefik.http.middlewares.middleware12.ipallowlist.ipstrategy.depth": "42", +"traefik.http.middlewares.middleware12.ipallowlist.ipstrategy.excludedips": "foobar, foobar", +"traefik.http.middlewares.middleware12.ipallowlist.sourcerange": "foobar, foobar", +"traefik.http.middlewares.middleware13.ipwhitelist.ipstrategy": "true", +"traefik.http.middlewares.middleware13.ipwhitelist.ipstrategy.depth": "42", +"traefik.http.middlewares.middleware13.ipwhitelist.ipstrategy.excludedips": "foobar, foobar", +"traefik.http.middlewares.middleware13.ipwhitelist.sourcerange": "foobar, foobar", +"traefik.http.middlewares.middleware14.inflightreq.amount": "42", +"traefik.http.middlewares.middleware14.inflightreq.sourcecriterion.ipstrategy.depth": "42", +"traefik.http.middlewares.middleware14.inflightreq.sourcecriterion.ipstrategy.excludedips": "foobar, foobar", +"traefik.http.middlewares.middleware14.inflightreq.sourcecriterion.requestheadername": "foobar", +"traefik.http.middlewares.middleware14.inflightreq.sourcecriterion.requesthost": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.issuer.commonname": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.issuer.country": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.issuer.domaincomponent": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.issuer.locality": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.issuer.organization": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.issuer.province": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.issuer.serialnumber": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.notafter": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.notbefore": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.sans": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.serialnumber": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.commonname": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.country": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.domaincomponent": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.locality": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.organization": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.organizationalunit": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.province": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.info.subject.serialnumber": "true", +"traefik.http.middlewares.middleware15.passtlsclientcert.pem": "true", +"traefik.http.middlewares.middleware16.plugin.pluginconf0.name0": "foobar", +"traefik.http.middlewares.middleware16.plugin.pluginconf0.name1": "foobar", +"traefik.http.middlewares.middleware16.plugin.pluginconf1.name0": "foobar", +"traefik.http.middlewares.middleware16.plugin.pluginconf1.name1": "foobar", +"traefik.http.middlewares.middleware17.ratelimit.average": "42", +"traefik.http.middlewares.middleware17.ratelimit.burst": "42", +"traefik.http.middlewares.middleware17.ratelimit.period": "42s", +"traefik.http.middlewares.middleware17.ratelimit.sourcecriterion.ipstrategy.depth": "42", +"traefik.http.middlewares.middleware17.ratelimit.sourcecriterion.ipstrategy.excludedips": "foobar, foobar", +"traefik.http.middlewares.middleware17.ratelimit.sourcecriterion.requestheadername": "foobar", +"traefik.http.middlewares.middleware17.ratelimit.sourcecriterion.requesthost": "true", +"traefik.http.middlewares.middleware18.redirectregex.permanent": "true", +"traefik.http.middlewares.middleware18.redirectregex.regex": "foobar", +"traefik.http.middlewares.middleware18.redirectregex.replacement": "foobar", +"traefik.http.middlewares.middleware19.redirectscheme.permanent": "true", +"traefik.http.middlewares.middleware19.redirectscheme.port": "foobar", +"traefik.http.middlewares.middleware19.redirectscheme.scheme": "foobar", +"traefik.http.middlewares.middleware20.replacepath.path": "foobar", +"traefik.http.middlewares.middleware21.replacepathregex.regex": "foobar", +"traefik.http.middlewares.middleware21.replacepathregex.replacement": "foobar", +"traefik.http.middlewares.middleware22.retry.attempts": "42", +"traefik.http.middlewares.middleware22.retry.initialinterval": "42s", +"traefik.http.middlewares.middleware23.stripprefix.forceslash": "true", +"traefik.http.middlewares.middleware23.stripprefix.prefixes": "foobar, foobar", +"traefik.http.middlewares.middleware24.stripprefixregex.regex": "foobar, foobar", "traefik.http.routers.router0.entrypoints": "foobar, foobar", "traefik.http.routers.router0.middlewares": "foobar, foobar", "traefik.http.routers.router0.priority": "42", @@ -148,33 +155,34 @@ "traefik.http.routers.router1.tls.domains[1].main": "foobar", "traefik.http.routers.router1.tls.domains[1].sans": "foobar, foobar", "traefik.http.routers.router1.tls.options": "foobar", -"traefik.http.services.service01.loadbalancer.healthcheck.followredirects": "true", -"traefik.http.services.service01.loadbalancer.healthcheck.headers.name0": "foobar", -"traefik.http.services.service01.loadbalancer.healthcheck.headers.name1": "foobar", -"traefik.http.services.service01.loadbalancer.healthcheck.hostname": "foobar", -"traefik.http.services.service01.loadbalancer.healthcheck.interval": "foobar", -"traefik.http.services.service01.loadbalancer.healthcheck.path": "foobar", -"traefik.http.services.service01.loadbalancer.healthcheck.method": "foobar", -"traefik.http.services.service01.loadbalancer.healthcheck.port": "42", -"traefik.http.services.service01.loadbalancer.healthcheck.scheme": "foobar", -"traefik.http.services.service01.loadbalancer.healthcheck.timeout": "foobar", -"traefik.http.services.service01.loadbalancer.passhostheader": "true", -"traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval": "foobar", -"traefik.http.services.service01.loadbalancer.serverstransport": "foobar", -"traefik.http.services.service01.loadbalancer.sticky.cookie": "true", -"traefik.http.services.service01.loadbalancer.sticky.cookie.httponly": "true", -"traefik.http.services.service01.loadbalancer.sticky.cookie.name": "foobar", -"traefik.http.services.service01.loadbalancer.sticky.cookie.samesite": "foobar", -"traefik.http.services.service01.loadbalancer.sticky.cookie.secure": "true", -"traefik.http.services.service01.loadbalancer.server.port": "foobar", -"traefik.http.services.service01.loadbalancer.server.scheme": "foobar", -"traefik.tcp.middlewares.tcpmiddleware00.ipwhitelist.sourcerange": "foobar, foobar", -"traefik.tcp.middlewares.tcpmiddleware01.inflightconn.amount": "42", -"traefik.tcp.middlewares.tcpmiddleware02.ipallowlist.sourcerange": "foobar, foobar", +"traefik.http.services.service02.loadbalancer.healthcheck.followredirects": "true", +"traefik.http.services.service02.loadbalancer.healthcheck.headers.name0": "foobar", +"traefik.http.services.service02.loadbalancer.healthcheck.headers.name1": "foobar", +"traefik.http.services.service02.loadbalancer.healthcheck.hostname": "foobar", +"traefik.http.services.service02.loadbalancer.healthcheck.interval": "foobar", +"traefik.http.services.service02.loadbalancer.healthcheck.method": "foobar", +"traefik.http.services.service02.loadbalancer.healthcheck.path": "foobar", +"traefik.http.services.service02.loadbalancer.healthcheck.port": "42", +"traefik.http.services.service02.loadbalancer.healthcheck.scheme": "foobar", +"traefik.http.services.service02.loadbalancer.healthcheck.timeout": "foobar", +"traefik.http.services.service02.loadbalancer.passhostheader": "true", +"traefik.http.services.service02.loadbalancer.responseforwarding.flushinterval": "foobar", +"traefik.http.services.service02.loadbalancer.serverstransport": "foobar", +"traefik.http.services.service02.loadbalancer.sticky": "true", +"traefik.http.services.service02.loadbalancer.sticky.cookie": "true", +"traefik.http.services.service02.loadbalancer.sticky.cookie.httponly": "true", +"traefik.http.services.service02.loadbalancer.sticky.cookie.name": "foobar", +"traefik.http.services.service02.loadbalancer.sticky.cookie.samesite": "foobar", +"traefik.http.services.service02.loadbalancer.sticky.cookie.secure": "true", +"traefik.http.services.service02.loadbalancer.server.port": "foobar", +"traefik.http.services.service02.loadbalancer.server.scheme": "foobar", +"traefik.tcp.middlewares.tcpmiddleware01.ipallowlist.sourcerange": "foobar, foobar", +"traefik.tcp.middlewares.tcpmiddleware02.ipwhitelist.sourcerange": "foobar, foobar", +"traefik.tcp.middlewares.tcpmiddleware03.inflightconn.amount": "42", "traefik.tcp.routers.tcprouter0.entrypoints": "foobar, foobar", "traefik.tcp.routers.tcprouter0.middlewares": "foobar, foobar", -"traefik.tcp.routers.tcprouter0.rule": "foobar", "traefik.tcp.routers.tcprouter0.priority": "42", +"traefik.tcp.routers.tcprouter0.rule": "foobar", "traefik.tcp.routers.tcprouter0.service": "foobar", "traefik.tcp.routers.tcprouter0.tls": "true", "traefik.tcp.routers.tcprouter0.tls.certresolver": "foobar", @@ -186,8 +194,8 @@ "traefik.tcp.routers.tcprouter0.tls.passthrough": "true", "traefik.tcp.routers.tcprouter1.entrypoints": "foobar, foobar", "traefik.tcp.routers.tcprouter1.middlewares": "foobar, foobar", -"traefik.tcp.routers.tcprouter1.rule": "foobar", "traefik.tcp.routers.tcprouter1.priority": "42", +"traefik.tcp.routers.tcprouter1.rule": "foobar", "traefik.tcp.routers.tcprouter1.service": "foobar", "traefik.tcp.routers.tcprouter1.tls": "true", "traefik.tcp.routers.tcprouter1.tls.certresolver": "foobar", @@ -197,6 +205,7 @@ "traefik.tcp.routers.tcprouter1.tls.domains[1].sans": "foobar, foobar", "traefik.tcp.routers.tcprouter1.tls.options": "foobar", "traefik.tcp.routers.tcprouter1.tls.passthrough": "true", +"traefik.tcp.services.tcpservice01.loadbalancer.proxyprotocol": "true", "traefik.tcp.services.tcpservice01.loadbalancer.proxyprotocol.version": "42", "traefik.tcp.services.tcpservice01.loadbalancer.terminationdelay": "42", "traefik.tcp.services.tcpservice01.loadbalancer.server.port": "foobar", @@ -205,13 +214,3 @@ "traefik.udp.routers.udprouter1.entrypoints": "foobar, foobar", "traefik.udp.routers.udprouter1.service": "foobar", "traefik.udp.services.udpservice01.loadbalancer.server.port": "foobar", -"traefik.tls.stores.Store0.defaultcertificate.certfile": "foobar", -"traefik.tls.stores.Store0.defaultcertificate.keyfile": "foobar", -"traefik.tls.stores.Store0.defaultgeneratedcert.domain.main": "foobar", -"traefik.tls.stores.Store0.defaultgeneratedcert.domain.sans": "foobar, foobar", -"traefik.tls.stores.Store0.defaultgeneratedcert.resolver": "foobar", -"traefik.tls.stores.Store1.defaultcertificate.certfile": "foobar", -"traefik.tls.stores.Store1.defaultcertificate.keyfile": "foobar", -"traefik.tls.stores.Store1.defaultgeneratedcert.domain.main": "foobar", -"traefik.tls.stores.Store1.defaultgeneratedcert.domain.sans": "foobar, foobar", -"traefik.tls.stores.Store1.defaultgeneratedcert.resolver": "foobar", diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index dc38a2cea..42c4e6fdf 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -1,3 +1,5 @@ +## CODE GENERATED AUTOMATICALLY +## THIS FILE MUST NOT BE EDITED BY HAND [global] checkNewVersion = true sendAnonymousUsage = true @@ -15,8 +17,8 @@ [entryPoints.EntryPoint0] address = "foobar" [entryPoints.EntryPoint0.transport] - keepAliveMaxRequests = 42 keepAliveMaxTime = "42s" + keepAliveMaxRequests = 42 [entryPoints.EntryPoint0.transport.lifeCycle] requestAcceptGraceTimeout = "42s" graceTimeOut = "42s" @@ -183,10 +185,10 @@ constraints = "foobar" prefix = "foobar" stale = true - namespace = "foobar" - namespaces = ["foobar", "foobar"] exposedByDefault = true refreshInterval = "42s" + namespace = "foobar" + namespaces = ["foobar", "foobar"] [providers.nomad.endpoint] address = "foobar" region = "foobar" @@ -205,10 +207,10 @@ defaultRule = "foobar" clusters = ["foobar", "foobar"] autoDiscoverClusters = true + ecsAnywhere = true region = "foobar" accessKeyID = "foobar" secretAccessKey = "foobar" - ecsAnywhere = true [providers.consul] rootKey = "foobar" endpoints = ["foobar", "foobar"] @@ -268,14 +270,18 @@ key = "foobar" insecureSkipVerify = true [providers.plugin] - [providers.plugin.Descriptor0] - [providers.plugin.Descriptor1] + [providers.plugin.PluginConf0] + name0 = "foobar" + name1 = "foobar" + [providers.plugin.PluginConf1] + name0 = "foobar" + name1 = "foobar" [api] insecure = true dashboard = true debug = true - disabledashboardad = false + disableDashboardAd = true [metrics] [metrics.prometheus] @@ -286,8 +292,8 @@ entryPoint = "foobar" manualRouting = true [metrics.prometheus.headerLabels] - label1 = "foobar" - label2 = "foobar" + name0 = "foobar" + name1 = "foobar" [metrics.datadog] address = "foobar" pushInterval = "42s" @@ -383,15 +389,15 @@ localAgentHostPort = "foobar" localAgentSocket = "foobar" globalTag = "foobar" - [tracing.datadog.globalTags] - tag1 = "foobar" - tag2 = "foobar" debug = true prioritySampling = true traceIDHeaderName = "foobar" parentIDHeaderName = "foobar" samplingPriorityHeaderName = "foobar" bagagePrefixHeaderName = "foobar" + [tracing.datadog.globalTags] + name0 = "foobar" + name1 = "foobar" [tracing.instana] localAgentHost = "foobar" localAgentPort = 42 @@ -455,6 +461,10 @@ entryPoint = "foobar" [certificatesResolvers.CertificateResolver1.acme.tlsChallenge] +[pilot] + token = "foobar" + dashboard = true + [experimental] kubernetesGateway = true http3 = true @@ -466,7 +476,7 @@ moduleName = "foobar" version = "foobar" [experimental.localPlugins] - [experimental.localPlugins.Descriptor0] + [experimental.localPlugins.LocalDescriptor0] moduleName = "foobar" - [experimental.localPlugins.Descriptor1] + [experimental.localPlugins.LocalDescriptor1] moduleName = "foobar" diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index 6a0cb264b..abb8e05d8 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -1,3 +1,5 @@ +## CODE GENERATED AUTOMATICALLY +## THIS FILE MUST NOT BE EDITED BY HAND global: checkNewVersion: true sendAnonymousUsage: true @@ -15,8 +17,6 @@ entryPoints: EntryPoint0: address: foobar transport: - keepAliveMaxRequests: 42 - keepAliveMaxTime: 42s lifeCycle: requestAcceptGraceTimeout: 42s graceTimeOut: 42s @@ -24,6 +24,8 @@ entryPoints: readTimeout: 42s writeTimeout: 42s idleTimeout: 42s + keepAliveMaxTime: 42s + keepAliveMaxRequests: 42 proxyProtocol: insecure: true trustedIPs: @@ -35,7 +37,6 @@ entryPoints: - foobar - foobar http: - encodeQuerySemicolons: true redirections: entryPoint: to: foobar @@ -57,6 +58,7 @@ entryPoints: sans: - foobar - foobar + encodeQuerySemicolons: true http2: maxConcurrentStreams: 42 http3: @@ -120,13 +122,13 @@ providers: - foobar labelSelector: foobar ingressClass: foobar - throttleDuration: 42s - allowEmptyServices: true - allowExternalNameServices: true ingressEndpoint: ip: foobar hostname: foobar publishedService: foobar + throttleDuration: 42s + allowEmptyServices: true + allowExternalNameServices: true kubernetesCRD: endpoint: foobar token: foobar @@ -162,6 +164,21 @@ providers: prefix: foobar consulCatalog: constraints: foobar + endpoint: + address: foobar + scheme: foobar + datacenter: foobar + token: foobar + tls: + ca: foobar + caOptional: true + cert: foobar + key: foobar + insecureSkipVerify: true + httpAuth: + username: foobar + password: foobar + endpointWaitTime: 42s prefix: foobar refreshInterval: 42s requireConsistent: true @@ -177,43 +194,28 @@ providers: namespaces: - foobar - foobar - endpoint: - address: foobar - scheme: foobar - datacenter: foobar - token: foobar - endpointWaitTime: 42s - tls: - ca: foobar - caOptional: true - cert: foobar - key: foobar - insecureSkipVerify: true - httpAuth: - username: foobar - password: foobar nomad: defaultRule: foobar constraints: foobar - prefix: foobar - stale: true - namespace: foobar - namespaces: - - foobar - - foobar - exposedByDefault: true - refreshInterval: 42s endpoint: address: foobar region: foobar token: foobar - endpointWaitTime: 42s tls: ca: foobar caOptional: true cert: foobar key: foobar insecureSkipVerify: true + endpointWaitTime: 42s + prefix: foobar + stale: true + exposedByDefault: true + refreshInterval: 42s + namespace: foobar + namespaces: + - foobar + - foobar ecs: constraints: foobar exposedByDefault: true @@ -223,39 +225,39 @@ providers: - foobar - foobar autoDiscoverClusters: true + ecsAnywhere: true region: foobar accessKeyID: foobar secretAccessKey: foobar - ecsAnywhere: true consul: rootKey: foobar endpoints: - foobar - foobar token: foobar - namespace: foobar - namespaces: - - foobar - - foobar tls: ca: foobar caOptional: true cert: foobar key: foobar insecureSkipVerify: true + namespace: foobar + namespaces: + - foobar + - foobar etcd: rootKey: foobar endpoints: - foobar - foobar - username: foobar - password: foobar tls: ca: foobar caOptional: true cert: foobar key: foobar insecureSkipVerify: true + username: foobar + password: foobar zooKeeper: rootKey: foobar endpoints: @@ -268,15 +270,15 @@ providers: endpoints: - foobar - foobar - username: foobar - password: foobar - db: 42 tls: ca: foobar caOptional: true cert: foobar key: foobar insecureSkipVerify: true + username: foobar + password: foobar + db: 42 sentinel: masterName: foobar username: foobar @@ -296,13 +298,17 @@ providers: key: foobar insecureSkipVerify: true plugin: - Descriptor0: {} - Descriptor1: {} + PluginConf0: + name0: foobar + name1: foobar + PluginConf1: + name0: foobar + name1: foobar api: insecure: true dashboard: true debug: true - disabledashboardad: false + disableDashboardAd: true metrics: prometheus: buckets: @@ -314,8 +320,8 @@ metrics: entryPoint: foobar manualRouting: true headerLabels: - label1: foobar - label2: foobar + name0: foobar + name1: foobar datadog: address: foobar pushInterval: 42s @@ -395,11 +401,11 @@ tracing: gen128Bit: true propagation: foobar traceContextHeaderName: foobar - disableAttemptReconnecting: true collector: endpoint: foobar user: foobar password: foobar + disableAttemptReconnecting: true zipkin: httpEndpoint: foobar sameSpan: true @@ -410,8 +416,8 @@ tracing: localAgentSocket: foobar globalTag: foobar globalTags: - tag1: foobar - tag2: foobar + name0: foobar + name1: foobar debug: true prioritySampling: true traceIDHeaderName: foobar @@ -444,13 +450,13 @@ certificatesResolvers: acme: email: foobar caServer: foobar - certificatesDuration: 42 preferredChain: foobar storage: foobar keyType: foobar eab: kid: foobar hmacEncoded: foobar + certificatesDuration: 42 dnsChallenge: provider: foobar delayBeforeCheck: 42s @@ -465,13 +471,13 @@ certificatesResolvers: acme: email: foobar caServer: foobar - certificatesDuration: 42 preferredChain: foobar storage: foobar keyType: foobar eab: kid: foobar hmacEncoded: foobar + certificatesDuration: 42 dnsChallenge: provider: foobar delayBeforeCheck: 42s @@ -482,10 +488,10 @@ certificatesResolvers: httpChallenge: entryPoint: foobar tlsChallenge: {} - +pilot: + token: foobar + dashboard: true experimental: - kubernetesGateway: true - http3: true plugins: Descriptor0: moduleName: foobar @@ -494,7 +500,9 @@ experimental: moduleName: foobar version: foobar localPlugins: - Descriptor0: + LocalDescriptor0: moduleName: foobar - Descriptor1: + LocalDescriptor1: moduleName: foobar + kubernetesGateway: true + http3: true diff --git a/internal/gendoc.go b/internal/gendoc.go index afc6320f9..d27c0fe3d 100644 --- a/internal/gendoc.go +++ b/internal/gendoc.go @@ -1,10 +1,13 @@ package main import ( + "bytes" "fmt" "io" "os" "path" + "path/filepath" + "reflect" "sort" "strconv" "strings" @@ -15,10 +18,66 @@ import ( "github.com/traefik/paerser/generator" "github.com/traefik/paerser/parser" "github.com/traefik/traefik/v2/cmd" + "github.com/traefik/traefik/v2/pkg/collector/hydratation" + "github.com/traefik/traefik/v2/pkg/config/dynamic" + "github.com/traefik/traefik/v2/pkg/config/static" "github.com/traefik/traefik/v2/pkg/log" + "gopkg.in/yaml.v3" ) +var commentGenerated = `## CODE GENERATED AUTOMATICALLY +## THIS FILE MUST NOT BE EDITED BY HAND +` + func main() { + logger := log.WithoutContext() + + dynConf := &dynamic.Configuration{} + + err := hydratation.Hydrate(dynConf) + if err != nil { + logger.Fatal(err) + } + + dynConf.HTTP.Models = map[string]*dynamic.Model{} + clean(dynConf.HTTP.Middlewares) + clean(dynConf.TCP.Middlewares) + clean(dynConf.HTTP.Services) + clean(dynConf.TCP.Services) + clean(dynConf.UDP.Services) + + err = tomlWrite("./docs/content/reference/dynamic-configuration/file.toml", dynConf) + if err != nil { + logger.Fatal(err) + } + err = yamlWrite("./docs/content/reference/dynamic-configuration/file.yaml", dynConf) + if err != nil { + logger.Fatal(err) + } + + err = labelsWrite("./docs/content/reference/dynamic-configuration", dynConf) + if err != nil { + logger.Fatal(err) + } + + staticConf := &static.Configuration{} + + err = hydratation.Hydrate(staticConf) + if err != nil { + logger.Fatal(err) + } + + delete(staticConf.EntryPoints, "EntryPoint1") + + err = tomlWrite("./docs/content/reference/static-configuration/file.toml", staticConf) + if err != nil { + logger.Fatal(err) + } + err = yamlWrite("./docs/content/reference/static-configuration/file.yaml", staticConf) + if err != nil { + logger.Fatal(err) + } + genStaticConfDoc("./docs/content/reference/static-configuration/env-ref.md", "", func(i interface{}) ([]parser.Flat, error) { return env.Encode(env.DefaultNamePrefix, i) }) @@ -26,6 +85,161 @@ func main() { genKVDynConfDoc("./docs/content/reference/dynamic-configuration/kv-ref.md") } +func labelsWrite(outputDir string, element *dynamic.Configuration) error { + cleanServers(element) + + etnOpts := parser.EncoderToNodeOpts{OmitEmpty: true, TagName: parser.TagLabel, AllowSliceAsStruct: true} + node, err := parser.EncodeToNode(element, parser.DefaultRootName, etnOpts) + if err != nil { + return err + } + + metaOpts := parser.MetadataOpts{TagName: parser.TagLabel, AllowSliceAsStruct: true} + err = parser.AddMetadata(element, node, metaOpts) + if err != nil { + return err + } + + labels := make(map[string]string) + encodeNode(labels, node.Name, node) + + var keys []string + for k := range labels { + keys = append(keys, k) + } + + sort.Strings(keys) + + dockerLabels, err := os.Create(filepath.Join(outputDir, "docker-labels.yml")) + if err != nil { + return err + } + defer dockerLabels.Close() + + // Write the comment at the beginning of the file + if _, err := dockerLabels.WriteString(commentGenerated); err != nil { + return err + } + + marathonLabels, err := os.Create(filepath.Join(outputDir, "marathon-labels.json")) + if err != nil { + return err + } + defer marathonLabels.Close() + + // Write the comment at the beginning of the file + if _, err := marathonLabels.WriteString(strings.ReplaceAll(commentGenerated, "##", "//")); err != nil { + return err + } + + for i, k := range keys { + v := labels[k] + if v != "" { + if v == "42000000000" { + v = "42s" + } + fmt.Fprintln(dockerLabels, `- "`+strings.ToLower(k)+`=`+v+`"`) + + if i == len(keys)-1 { + fmt.Fprintln(marathonLabels, `"`+strings.ToLower(k)+`": "`+v+`"`) + } else { + fmt.Fprintln(marathonLabels, `"`+strings.ToLower(k)+`": "`+v+`",`) + } + } + } + + return nil +} + +func cleanServers(element *dynamic.Configuration) { + for _, svc := range element.HTTP.Services { + if svc.LoadBalancer != nil { + server := svc.LoadBalancer.Servers[0] + svc.LoadBalancer.Servers = nil + svc.LoadBalancer.Servers = append(svc.LoadBalancer.Servers, server) + } + } + + for _, svc := range element.TCP.Services { + if svc.LoadBalancer != nil { + server := svc.LoadBalancer.Servers[0] + svc.LoadBalancer.Servers = nil + svc.LoadBalancer.Servers = append(svc.LoadBalancer.Servers, server) + } + } + + for _, svc := range element.UDP.Services { + if svc.LoadBalancer != nil { + server := svc.LoadBalancer.Servers[0] + svc.LoadBalancer.Servers = nil + svc.LoadBalancer.Servers = append(svc.LoadBalancer.Servers, server) + } + } +} + +func yamlWrite(outputFile string, element any) error { + file, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666) + if err != nil { + return err + } + defer file.Close() + + // Write the comment at the beginning of the file + if _, err := file.WriteString(commentGenerated); err != nil { + return err + } + + buf := new(bytes.Buffer) + encoder := yaml.NewEncoder(buf) + encoder.SetIndent(2) + err = encoder.Encode(element) + if err != nil { + return err + } + + _, err = file.Write(buf.Bytes()) + return err +} + +func tomlWrite(outputFile string, element any) error { + file, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666) + if err != nil { + return err + } + defer file.Close() + + // Write the comment at the beginning of the file + if _, err := file.WriteString(commentGenerated); err != nil { + return err + } + + return toml.NewEncoder(file).Encode(element) +} + +func clean(element any) { + valSvcs := reflect.ValueOf(element) + + key := valSvcs.MapKeys()[0] + valueSvcRoot := valSvcs.MapIndex(key).Elem() + + var svcFieldNames []string + for i := 0; i < valueSvcRoot.NumField(); i++ { + svcFieldNames = append(svcFieldNames, valueSvcRoot.Type().Field(i).Name) + } + + sort.Strings(svcFieldNames) + + for i, fieldName := range svcFieldNames { + v := reflect.New(valueSvcRoot.Type()) + v.Elem().FieldByName(fieldName).Set(valueSvcRoot.FieldByName(fieldName)) + + valSvcs.SetMapIndex(reflect.ValueOf(fmt.Sprintf("%s%.2d", valueSvcRoot.Type().Name(), i+1)), v) + } + + valSvcs.SetMapIndex(reflect.ValueOf(fmt.Sprintf("%s0", valueSvcRoot.Type().Name())), reflect.Value{}) + valSvcs.SetMapIndex(reflect.ValueOf(fmt.Sprintf("%s1", valueSvcRoot.Type().Name())), reflect.Value{}) +} + func genStaticConfDoc(outputFile, prefix string, encodeFn func(interface{}) ([]parser.Flat, error)) { logger := log.WithoutContext().WithField("file", outputFile) @@ -117,6 +331,7 @@ func genKVDynConfDoc(outputFile string) { } store := storeWriter{data: map[string]string{}} + c := client{store: store} err = c.load("traefik", conf) if err != nil { @@ -130,6 +345,12 @@ func genKVDynConfDoc(outputFile string) { sort.Strings(keys) + _, _ = fmt.Fprintf(file, ` +`) + for _, k := range keys { _, _ = fmt.Fprintf(file, "| `%s` | `%s` |\n", k, store.data[k]) } diff --git a/internal/parser.go b/internal/parser.go new file mode 100644 index 000000000..3fa85617e --- /dev/null +++ b/internal/parser.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + "reflect" + "strings" + + "github.com/traefik/paerser/parser" +) + +func encodeNode(labels map[string]string, root string, node *parser.Node) { + for _, child := range node.Children { + if child.Disabled { + continue + } + + var sep string + if child.Name[0] != '[' { + sep = "." + } + + childName := root + sep + child.Name + + if child.RawValue != nil { + encodeRawValue(labels, childName, child.RawValue) + continue + } + + if strings.Contains(child.Tag.Get(parser.TagLabel), parser.TagLabelAllowEmpty) { + labels[childName] = "true" + } + + if len(child.Children) > 0 { + encodeNode(labels, childName, child) + } else if len(child.Name) > 0 { + labels[childName] = child.Value + } + } +} + +func encodeRawValue(labels map[string]string, root string, rawValue interface{}) { + if rawValue == nil { + return + } + + tValue := reflect.TypeOf(rawValue) + + if tValue.Kind() == reflect.Map && tValue.Elem().Kind() == reflect.Interface { + r := reflect.ValueOf(rawValue). + Convert(reflect.TypeOf((map[string]interface{})(nil))). + Interface().(map[string]interface{}) + + for k, v := range r { + switch tv := v.(type) { + case string: + labels[root+"."+k] = tv + case []interface{}: + for i, e := range tv { + encodeRawValue(labels, fmt.Sprintf("%s.%s[%d]", root, k, i), e) + } + default: + encodeRawValue(labels, root+"."+k, v) + } + } + } +} diff --git a/pkg/collector/collector_test.go b/pkg/collector/collector_test.go index b091f0c4f..fdb50c549 100644 --- a/pkg/collector/collector_test.go +++ b/pkg/collector/collector_test.go @@ -5,13 +5,14 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/traefik/traefik/v2/pkg/collector/hydratation" "github.com/traefik/traefik/v2/pkg/config/static" ) func Test_createBody(t *testing.T) { var staticConfiguration static.Configuration - err := hydrate(&staticConfiguration) + err := hydratation.Hydrate(&staticConfiguration) require.NoError(t, err) buffer, err := createBody(&staticConfiguration) diff --git a/pkg/collector/hydration_test.go b/pkg/collector/hydratation/hydration.go similarity index 94% rename from pkg/collector/hydration_test.go rename to pkg/collector/hydratation/hydration.go index 8814888a6..a5ec52dee 100644 --- a/pkg/collector/hydration_test.go +++ b/pkg/collector/hydratation/hydration.go @@ -1,4 +1,4 @@ -package collector +package hydratation import ( "fmt" @@ -17,7 +17,8 @@ const ( defaultMapKeyPrefix = "name" ) -func hydrate(element interface{}) error { +// Hydrate hydrates a configuration. +func Hydrate(element interface{}) error { field := reflect.ValueOf(element) return fill(field) } @@ -41,9 +42,7 @@ func fill(field reflect.Value) error { return err } case reflect.Interface: - if err := fill(field.Elem()); err != nil { - return err - } + setTyped(field, defaultString) case reflect.String: setTyped(field, defaultString) case reflect.Int: @@ -118,7 +117,7 @@ func makeKeyName(typ reflect.Type) string { case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, - reflect.Bool, reflect.Float32, reflect.Float64: + reflect.Bool, reflect.Float32, reflect.Float64, reflect.Interface: return defaultMapKeyPrefix default: return typ.Name() diff --git a/pkg/config/dynamic/config.go b/pkg/config/dynamic/config.go index d23315d6b..469c04acc 100644 --- a/pkg/config/dynamic/config.go +++ b/pkg/config/dynamic/config.go @@ -24,7 +24,7 @@ type Configuration struct { HTTP *HTTPConfiguration `json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" export:"true"` TCP *TCPConfiguration `json:"tcp,omitempty" toml:"tcp,omitempty" yaml:"tcp,omitempty" export:"true"` UDP *UDPConfiguration `json:"udp,omitempty" toml:"udp,omitempty" yaml:"udp,omitempty" export:"true"` - TLS *TLSConfiguration `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` + TLS *TLSConfiguration `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"-" export:"true"` } // +k8s:deepcopy-gen=true From 7a315bb04380ac80f072eaadedd2ec2f1936757c Mon Sep 17 00:00:00 2001 From: Robin Moser Date: Tue, 16 Jan 2024 15:30:06 +0100 Subject: [PATCH 5/9] Disable br compression when no Accept-Encoding header is present --- docs/content/middlewares/http/compress.md | 2 +- pkg/middlewares/compress/compress.go | 6 +++--- pkg/middlewares/compress/compress_test.go | 14 +++++--------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/docs/content/middlewares/http/compress.md b/docs/content/middlewares/http/compress.md index eec8e73b0..cd49d757b 100644 --- a/docs/content/middlewares/http/compress.md +++ b/docs/content/middlewares/http/compress.md @@ -55,7 +55,7 @@ http: Responses are compressed when the following criteria are all met: * The `Accept-Encoding` request header contains `gzip`, `*`, and/or `br` with or without [quality values](https://developer.mozilla.org/en-US/docs/Glossary/Quality_values). - If the `Accept-Encoding` request header is absent, it is meant as br compression is requested. + If the `Accept-Encoding` request header is absent, the response won't be encoded. If it is present, but its value is the empty string, then compression is disabled. * The response is not already compressed, i.e. the `Content-Encoding` response header is not already set. * The response`Content-Type` header is not one among the [excludedContentTypes options](#excludedcontenttypes). diff --git a/pkg/middlewares/compress/compress.go b/pkg/middlewares/compress/compress.go index b7ae85b4d..4618a0a12 100644 --- a/pkg/middlewares/compress/compress.go +++ b/pkg/middlewares/compress/compress.go @@ -92,11 +92,11 @@ func (c *compress) ServeHTTP(rw http.ResponseWriter, req *http.Request) { return } - // Client allows us to do whatever we want, so we br compress. - // See https://www.rfc-editor.org/rfc/rfc9110.html#section-12.5.3 + // Client doesn't specify a preferred encoding, for compatibility don't encode the request + // See https://github.com/traefik/traefik/issues/9734 acceptEncoding, ok := req.Header["Accept-Encoding"] if !ok { - c.brotliHandler.ServeHTTP(rw, req) + c.next.ServeHTTP(rw, req) return } diff --git a/pkg/middlewares/compress/compress_test.go b/pkg/middlewares/compress/compress_test.go index 23d6d765e..44e28b11f 100644 --- a/pkg/middlewares/compress/compress_test.go +++ b/pkg/middlewares/compress/compress_test.go @@ -10,7 +10,6 @@ import ( "net/textproto" "testing" - "github.com/andybalholm/brotli" "github.com/klauspost/compress/gzhttp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -35,7 +34,7 @@ func TestNegotiation(t *testing.T) { }{ { desc: "no accept header", - expEncoding: "br", + expEncoding: "", }, { desc: "unsupported accept header", @@ -151,7 +150,7 @@ func TestShouldNotCompressWhenContentEncodingHeader(t *testing.T) { assert.EqualValues(t, rw.Body.Bytes(), fakeCompressedBody) } -func TestShouldCompressWhenNoAcceptEncodingHeader(t *testing.T) { +func TestShouldNotCompressWhenNoAcceptEncodingHeader(t *testing.T) { req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil) fakeBody := generateBytes(gzhttp.DefaultMinSize) @@ -167,12 +166,9 @@ func TestShouldCompressWhenNoAcceptEncodingHeader(t *testing.T) { rw := httptest.NewRecorder() handler.ServeHTTP(rw, req) - assert.Equal(t, brotliValue, rw.Header().Get(contentEncodingHeader)) - assert.Equal(t, acceptEncodingHeader, rw.Header().Get(varyHeader)) - - got, err := io.ReadAll(brotli.NewReader(rw.Body)) - require.NoError(t, err) - assert.Equal(t, got, fakeBody) + assert.Empty(t, rw.Header().Get(contentEncodingHeader)) + assert.Empty(t, rw.Header().Get(varyHeader)) + assert.EqualValues(t, rw.Body.Bytes(), fakeBody) } func TestShouldNotCompressWhenIdentityAcceptEncodingHeader(t *testing.T) { From 39b0aa6650d94dd4c8320d23fae4e69986930cb9 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 17 Jan 2024 11:12:05 +0100 Subject: [PATCH 6/9] Improve makefile --- .dockerignore | 2 +- .github/workflows/experimental.yaml | 34 ++++- .github/workflows/test-integration.yaml | 3 +- .github/workflows/validate.yaml | 2 +- Dockerfile | 13 +- Makefile | 138 ++++++++++-------- build.Dockerfile | 37 ----- debug.Dockerfile | 10 -- docs/content/contributing/building-testing.md | 15 +- exp.Dockerfile | 47 ------ integration/integration_test.go | 11 +- script/binary | 30 ---- ...sbinary-default => crossbinary-default.sh} | 0 script/generate | 4 - script/make.sh | 35 ----- script/release-packages.sh | 28 ++++ script/test-integration | 11 -- script/test-unit | 29 ---- script/validate-lint | 3 - ...validate-misspell => validate-misspell.sh} | 0 .../{validate-vendor => validate-vendor.sh} | 0 21 files changed, 162 insertions(+), 290 deletions(-) delete mode 100644 build.Dockerfile delete mode 100644 debug.Dockerfile delete mode 100644 exp.Dockerfile delete mode 100755 script/binary rename script/{crossbinary-default => crossbinary-default.sh} (100%) delete mode 100755 script/generate delete mode 100755 script/make.sh create mode 100755 script/release-packages.sh delete mode 100755 script/test-integration delete mode 100755 script/test-unit delete mode 100755 script/validate-lint rename script/{validate-misspell => validate-misspell.sh} (100%) rename script/{validate-vendor => validate-vendor.sh} (100%) diff --git a/.dockerignore b/.dockerignore index 8390d87b6..bcd757988 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,5 @@ dist/ -!dist/traefik +!dist/**/traefik site/ vendor/ .idea/ diff --git a/.github/workflows/experimental.yaml b/.github/workflows/experimental.yaml index 63d7c7bef..a26178315 100644 --- a/.github/workflows/experimental.yaml +++ b/.github/workflows/experimental.yaml @@ -6,6 +6,10 @@ on: - master - v* +env: + GO_VERSION: '1.21' + CGO_ENABLED: 0 + jobs: experimental: @@ -21,17 +25,35 @@ jobs: with: fetch-depth: 0 + - name: Build webui + run: | + make clean-webui generate-webui + + - name: Set up Go ${{ env.GO_VERSION }} + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Build + run: make generate binary + - name: Branch name run: echo ${GITHUB_REF##*/} - - name: Build docker experimental image - run: docker build -t traefik/traefik:experimental-${GITHUB_REF##*/} -f exp.Dockerfile . - - name: Login to Docker Hub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Push to Docker Hub - run: docker push traefik/traefik:experimental-${GITHUB_REF##*/} + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Build docker experimental image + env: + DOCKER_BUILDX_ARGS: "--push" + run: | + make multi-arch-image-experimental-${GITHUB_REF##*/} diff --git a/.github/workflows/test-integration.yaml b/.github/workflows/test-integration.yaml index bcbc3ec21..d5c4e5ca1 100644 --- a/.github/workflows/test-integration.yaml +++ b/.github/workflows/test-integration.yaml @@ -71,4 +71,5 @@ jobs: - name: Run Integration tests run: | - go test ./integration -test.timeout=20m -failfast -v -run "${{ steps.test_split.outputs.run}}" + TESTS=$(echo "${{ steps.test_split.outputs.run}}" | sed 's/\$/\$\$/g') + TESTFLAGS="-run \"${TESTS}\"" make test-integration diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index 1c0822abe..f836e94ee 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -54,7 +54,7 @@ jobs: - name: go generate run: | - go generate + make generate git diff --exit-code - name: go mod tidy diff --git a/Dockerfile b/Dockerfile index 873d55312..fea809225 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,13 @@ -FROM scratch -COPY script/ca-certificates.crt /etc/ssl/certs/ -COPY dist/traefik / +# syntax=docker/dockerfile:1.2 +FROM alpine:3.19 + +RUN apk --no-cache --no-progress add ca-certificates tzdata \ + && rm -rf /var/cache/apk/* + +ARG TARGETPLATFORM +COPY ./dist/$TARGETPLATFORM/traefik / + EXPOSE 80 VOLUME ["/tmp"] + ENTRYPOINT ["/traefik"] diff --git a/Makefile b/Makefile index 668c5404e..1a8f40b37 100644 --- a/Makefile +++ b/Makefile @@ -7,8 +7,22 @@ VERSION := $(if $(VERSION),$(VERSION),$(VERSION_GIT)) GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)) +REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]') +BIN_NAME := traefik +CODENAME := cheddar + +DATE := $(shell date -u '+%Y-%m-%d_%I:%M:%S%p') + +# Default build target +GOOS := $(shell go env GOOS) +GOARCH := $(shell go env GOARCH) + +LINT_EXECUTABLES = misspell shellcheck + +DOCKER_BUILD_PLATFORMS ?= linux/amd64,linux/arm64 + .PHONY: default -default: binary +default: generate binary ## Create the "dist" directory dist: @@ -35,43 +49,57 @@ webui/static/index.html: .PHONY: generate-webui generate-webui: webui/static/index.html +## Generate code +.PHONY: generate +generate: + go generate + ## Build the binary .PHONY: binary -binary: generate-webui - ./script/make.sh generate binary +binary: generate-webui dist + @echo SHA: $(VERSION) $(CODENAME) $(DATE) + CGO_ENABLED=0 GOGC=off GOOS=${GOOS} GOARCH=${GOARCH} go build ${FLAGS[*]} -ldflags "-s -w \ + -X github.com/traefik/traefik/v2/pkg/version.Version=$(VERSION) \ + -X github.com/traefik/traefik/v2/pkg/version.Codename=$(CODENAME) \ + -X github.com/traefik/traefik/v2/pkg/version.BuildDate=$(DATE)" \ + -installsuffix nocgo -o "./dist/${GOOS}/${GOARCH}/$(BIN_NAME)" ./cmd/traefik -## Build the linux binary locally -.PHONY: binary-debug -binary-debug: generate-webui - GOOS=linux ./script/make.sh binary +binary-linux-arm64: export GOOS := linux +binary-linux-arm64: export GOARCH := arm64 +binary-linux-arm64: + @$(MAKE) binary + +binary-linux-amd64: export GOOS := linux +binary-linux-amd64: export GOARCH := amd64 +binary-linux-amd64: + @$(MAKE) binary + +binary-windows-amd64: export GOOS := windows +binary-windows-amd64: export GOARCH := amd64 +binary-windows-amd64: export BIN_NAME := traefik.exe +binary-windows-amd64: + @$(MAKE) binary ## Build the binary for the standard platforms (linux, darwin, windows) .PHONY: crossbinary-default -crossbinary-default: generate-webui - ./script/make.sh generate crossbinary-default - -## Build the binary for the standard platforms (linux, darwin, windows) in parallel -.PHONY: crossbinary-default-parallel -crossbinary-default-parallel: - $(MAKE) generate-webui - $(MAKE) crossbinary-default +crossbinary-default: generate generate-webui + $(CURDIR)/script/crossbinary-default.sh ## Run the unit and integration tests .PHONY: test -test: - ./script/make.sh generate test-unit binary test-integration +test: test-unit test-integration ## Run the unit tests .PHONY: test-unit test-unit: - ./script/make.sh generate test-unit + GOOS=$(GOOS) GOARCH=$(GOARCH) go test -cover "-coverprofile=cover.out" -v $(TESTFLAGS) ./pkg/... ./cmd/... ## Run the integration tests .PHONY: test-integration -test-integration: - ./script/make.sh generate binary test-integration +test-integration: binary + GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -test.timeout=20m -failfast -v $(TESTFLAGS) -## Pull all images for integration tests +## Pull all Docker images to avoid timeout during integration tests .PHONY: pull-images pull-images: grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml \ @@ -80,38 +108,47 @@ pull-images: | uniq \ | xargs -P 6 -n 1 docker pull -EXECUTABLES = misspell shellcheck +## Lint run golangci-lint +.PHONY: lint +lint: + golangci-lint run ## Validate code and docs .PHONY: validate-files -validate-files: - $(foreach exec,$(EXECUTABLES),\ +validate-files: lint + $(foreach exec,$(LINT_EXECUTABLES),\ $(if $(shell which $(exec)),,$(error "No $(exec) in PATH"))) - ./script/make.sh generate validate-lint validate-misspell - bash $(CURDIR)/script/validate-shell-script.sh + $(CURDIR)/script/validate-misspell.sh + $(CURDIR)/script/validate-shell-script.sh ## Validate code, docs, and vendor .PHONY: validate -validate: +validate: lint $(foreach exec,$(EXECUTABLES),\ $(if $(shell which $(exec)),,$(error "No $(exec) in PATH"))) - ./script/make.sh generate validate-lint validate-misspell validate-vendor - bash $(CURDIR)/script/validate-shell-script.sh + $(CURDIR)/script/validate-vendor.sh + $(CURDIR)/script/validate-misspell.sh + $(CURDIR)/script/validate-shell-script.sh + +# Target for building images for multiple architectures. +.PHONY: multi-arch-image-% +multi-arch-image-%: binary-linux-amd64 binary-linux-arm64 + docker buildx build $(DOCKER_BUILDX_ARGS) -t traefik/traefik:$* --platform=$(DOCKER_BUILD_PLATFORMS) -f Dockerfile . + ## Clean up static directory and build a Docker Traefik image .PHONY: build-image -build-image: clean-webui binary - docker build -t $(TRAEFIK_IMAGE) . +build-image: export DOCKER_BUILDX_ARGS := --load +build-image: export DOCKER_BUILD_PLATFORMS := linux/$(GOARCH) +build-image: clean-webui + @$(MAKE) multi-arch-image-latest -## Build a Docker Traefik image without re-building the webui +## Build a Docker Traefik image without re-building the webui when it's already built .PHONY: build-image-dirty -build-image-dirty: binary - docker build -t $(TRAEFIK_IMAGE) . - -## Locally build traefik for linux, then shove it an alpine image, with basic tools. -.PHONY: build-image-debug -build-image-debug: binary-debug - docker build -t $(TRAEFIK_IMAGE) -f debug.Dockerfile . +build-image-dirty: export DOCKER_BUILDX_ARGS := --load +build-image-dirty: export DOCKER_BUILD_PLATFORMS := linux/$(GOARCH) +build-image-dirty: + @$(MAKE) multi-arch-image-latest ## Build documentation site .PHONY: docs @@ -141,30 +178,9 @@ generate-genconf: ## Create packages for the release .PHONY: release-packages release-packages: generate-webui - rm -rf dist - @- $(foreach os, linux darwin windows freebsd openbsd, \ - goreleaser release --skip-publish -p 2 --timeout="90m" --config $(shell go run ./internal/release $(os)); \ - go clean -cache; \ - ) - - cat dist/**/*_checksums.txt >> dist/traefik_${VERSION}_checksums.txt - rm dist/**/*_checksums.txt - tar cfz dist/traefik-${VERSION}.src.tar.gz \ - --exclude-vcs \ - --exclude .idea \ - --exclude .travis \ - --exclude .semaphoreci \ - --exclude .github \ - --exclude dist . - chown -R $(shell id -u):$(shell id -g) dist/ + $(CURDIR)/script/release-packages.sh ## Format the Code .PHONY: fmt fmt: gofmt -s -l -w $(SRCS) - -.PHONY: run-dev -run-dev: - go generate - GO111MODULE=on go build ./cmd/traefik - ./traefik diff --git a/build.Dockerfile b/build.Dockerfile deleted file mode 100644 index 587424c38..000000000 --- a/build.Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM golang:1.21-alpine - -RUN apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \ - && update-ca-certificates \ - && rm -rf /var/cache/apk/* - -# Which docker version to test on -ARG DOCKER_VERSION=18.09.7 - -# Download docker -RUN mkdir -p /usr/local/bin \ - && curl -fL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz \ - | tar -xzC /usr/local/bin --transform 's#^.+/##x' - -# Download golangci-lint binary to bin folder in $GOPATH -RUN curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b $GOPATH/bin v1.55.2 - -# Download misspell binary to bin folder in $GOPATH -RUN curl -sfL https://raw.githubusercontent.com/golangci/misspell/master/install-misspell.sh | bash -s -- -b $GOPATH/bin v0.4.0 - -# Download goreleaser binary to bin folder in $GOPATH -RUN curl -sfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | sh - -WORKDIR /go/src/github.com/traefik/traefik - -# Because of CVE-2022-24765 (https://github.blog/2022-04-12-git-security-vulnerability-announced/), -# we configure git to allow the Traefik codebase path on the Host for docker in docker usages. -ARG HOST_PWD="" - -RUN git config --global --add safe.directory "${HOST_PWD}" - -# Download go modules -COPY go.mod . -COPY go.sum . -RUN GO111MODULE=on GOPROXY=https://proxy.golang.org go mod download - -COPY . /go/src/github.com/traefik/traefik diff --git a/debug.Dockerfile b/debug.Dockerfile deleted file mode 100644 index 4dcf88bf8..000000000 --- a/debug.Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM alpine:3.14 -# Feel free to add below any helpful dependency for debugging. -# iproute2 is for ss. -RUN apk --no-cache --no-progress add bash curl ca-certificates tzdata lsof iproute2 \ - && update-ca-certificates \ - && rm -rf /var/cache/apk/* -COPY dist/traefik / -EXPOSE 80 -VOLUME ["/tmp"] -ENTRYPOINT ["/traefik"] diff --git a/docs/content/contributing/building-testing.md b/docs/content/contributing/building-testing.md index 838688869..20d05740a 100644 --- a/docs/content/contributing/building-testing.md +++ b/docs/content/contributing/building-testing.md @@ -58,10 +58,12 @@ Once you've set up your go environment and cloned the source repository, you can ```bash $ make binary -./script/make.sh generate binary ----> Making bundle: generate (in .) - ----> Making bundle: binary (in .) +SHA: 8fddfe118288bb5280eb5e77fa952f52def360b4 cheddar 2024-01-11_03:14:57PM +CGO_ENABLED=0 GOGC=off GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w \ + -X github.com/traefik/traefik/v2/pkg/version.Version=8fddfe118288bb5280eb5e77fa952f52def360b4 \ + -X github.com/traefik/traefik/v2/pkg/version.Codename=cheddar \ + -X github.com/traefik/traefik/v2/pkg/version.BuildDate=2024-01-11_03:14:57PM" \ + -installsuffix nocgo -o "./dist/darwin/arm64/traefik" ./cmd/traefik $ ls dist/ traefik* @@ -77,10 +79,7 @@ Run all tests (unit and integration) using the `test` target. ```bash $ make test-unit -./script/make.sh generate test-unit ----> Making bundle: generate (in .) - ----> Making bundle: test-unit (in .) +GOOS=darwin GOARCH=arm64 go test -cover "-coverprofile=cover.out" -v ./pkg/... ./cmd/... + go test -cover -coverprofile=cover.out . ok github.com/traefik/traefik 0.005s coverage: 4.1% of statements diff --git a/exp.Dockerfile b/exp.Dockerfile deleted file mode 100644 index 8098f9218..000000000 --- a/exp.Dockerfile +++ /dev/null @@ -1,47 +0,0 @@ -# WEBUI -FROM node:12.11 as webui - -ENV WEBUI_DIR /src/webui -RUN mkdir -p $WEBUI_DIR - -COPY ./webui/ $WEBUI_DIR/ - -WORKDIR $WEBUI_DIR - -RUN yarn install -RUN yarn build - -# BUILD -FROM golang:1.21-alpine as gobuild - -RUN apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \ - && update-ca-certificates \ - && rm -rf /var/cache/apk/* - -WORKDIR /go/src/github.com/traefik/traefik - -# Download go modules -COPY go.mod . -COPY go.sum . -RUN GO111MODULE=on GOPROXY=https://proxy.golang.org go mod download - -COPY . /go/src/github.com/traefik/traefik - -RUN rm -rf /go/src/github.com/traefik/traefik/webui/static/ -COPY --from=webui /src/webui/static/ /go/src/github.com/traefik/traefik/webui/static/ - -RUN ./script/make.sh generate binary - -## IMAGE -FROM alpine:3.14 - -RUN apk --no-cache --no-progress add bash curl ca-certificates tzdata \ - && update-ca-certificates \ - && rm -rf /var/cache/apk/* - -COPY --from=gobuild /go/src/github.com/traefik/traefik/dist/traefik / - -EXPOSE 80 -VOLUME ["/tmp"] - -ENTRYPOINT ["/traefik"] diff --git a/integration/integration_test.go b/integration/integration_test.go index 9dc04dc38..0ded401de 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -15,6 +15,7 @@ import ( "os/exec" "path/filepath" "regexp" + "runtime" "slices" "strings" "testing" @@ -56,8 +57,6 @@ type composeDeploy struct { Replicas int `yaml:"replicas"` } -var traefikBinary = "../dist/traefik" - type BaseSuite struct { suite.Suite containers map[string]testcontainers.Container @@ -308,7 +307,13 @@ func (s *BaseSuite) composeDown() { } func (s *BaseSuite) cmdTraefik(args ...string) (*exec.Cmd, *bytes.Buffer) { - cmd := exec.Command(traefikBinary, args...) + binName := "traefik" + if runtime.GOOS == "windows" { + binName += ".exe" + } + + traefikBinPath := filepath.Join("..", "dist", runtime.GOOS, runtime.GOARCH, binName) + cmd := exec.Command(traefikBinPath, args...) s.T().Cleanup(func() { s.killCmd(cmd) diff --git a/script/binary b/script/binary deleted file mode 100755 index 3a3e94b61..000000000 --- a/script/binary +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash -set -e - -rm -f dist/traefik - -FLAGS=() -if [ -n "${VERBOSE}" ]; then - FLAGS+=(-v) -fi - -if [ -z "${VERSION}" ]; then - VERSION=$(git rev-parse HEAD) -fi - -if [ -z "$CODENAME" ]; then - CODENAME=cheddar -fi - -if [ -z "$DATE" ]; then - DATE=$(date -u '+%Y-%m-%d_%I:%M:%S%p') -fi - -# Build binaries -# shellcheck disable=SC2086 -# shellcheck disable=SC2048 -CGO_ENABLED=0 GOGC=off go build ${FLAGS[*]} -ldflags "-s -w \ - -X github.com/traefik/traefik/v2/pkg/version.Version=$VERSION \ - -X github.com/traefik/traefik/v2/pkg/version.Codename=$CODENAME \ - -X github.com/traefik/traefik/v2/pkg/version.BuildDate=$DATE" \ - -installsuffix nocgo -o dist/traefik ./cmd/traefik diff --git a/script/crossbinary-default b/script/crossbinary-default.sh similarity index 100% rename from script/crossbinary-default rename to script/crossbinary-default.sh diff --git a/script/generate b/script/generate deleted file mode 100755 index 423970b5a..000000000 --- a/script/generate +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash -set -e - -go generate diff --git a/script/make.sh b/script/make.sh deleted file mode 100755 index 2f3870e74..000000000 --- a/script/make.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -set -e - -export GO111MODULE=on -export GOPROXY=https://proxy.golang.org - -# List of bundles to create when no argument is passed -DEFAULT_BUNDLES=( - generate - validate-lint - binary - - test-unit - test-integration -) - -SCRIPT_DIR="$(cd "$(dirname "${0}")" && pwd -P)" - -bundle() { - local bundle="$1"; shift - echo "---> Making bundle: $(basename "${bundle}") (in $SCRIPT_DIR)" - # shellcheck source=/dev/null - source "${SCRIPT_DIR}/${bundle}" -} - -if [ $# -lt 1 ]; then - bundles=${DEFAULT_BUNDLES[*]} -else - bundles=${*} -fi -# shellcheck disable=SC2048 -for bundle in ${bundles[*]}; do - bundle "${bundle}" - echo -done diff --git a/script/release-packages.sh b/script/release-packages.sh new file mode 100755 index 000000000..f442e12dd --- /dev/null +++ b/script/release-packages.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -e + +if [ -n "${SEMAPHORE_GIT_TAG_NAME}" ]; then + echo "Releasing packages..." +else + echo "Skipping release" + exit 0 +fi + +rm -rf dist + +for os in linux darwin windows freebsd openbsd; do + goreleaser release --snapshot --skip=publish -p 2 --timeout="90m" --config "$(go run ./internal/release "$os")" + go clean -cache +done + +cat dist/**/*_checksums.txt >> dist/traefik_${VERSION}_checksums.txt +rm dist/**/*_checksums.txt +tar cfz dist/traefik-${VERSION}.src.tar.gz \ + --exclude-vcs \ + --exclude .idea \ + --exclude .travis \ + --exclude .semaphoreci \ + --exclude .github \ + --exclude dist . + +chown -R $(id -u):$(id -g) dist/ diff --git a/script/test-integration b/script/test-integration deleted file mode 100755 index a6afba7d1..000000000 --- a/script/test-integration +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -set -e - -export DEST=. - -echo "Testing against..." -docker version - -# shellcheck disable=SC2086 -# shellcheck disable=SC2048 -go test ./integration -test.timeout=20m -failfast -v ${TESTFLAGS[*]} diff --git a/script/test-unit b/script/test-unit deleted file mode 100755 index 8f5ef0189..000000000 --- a/script/test-unit +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -set -e - -RED=$'\033[31m' -GREEN=$'\033[32m' -TEXTRESET=$'\033[0m' # reset the foreground colour - -# -failfast -timeout=5m -TESTFLAGS=(-cover "-coverprofile=cover.out" "${TESTFLAGS}") - -if [ -n "${VERBOSE}" ]; then - TESTFLAGS+=(-v) -elif [ -n "${VERBOSE_UNIT}" ]; then - TESTFLAGS+=(-v) -fi - -set +e - -# shellcheck disable=SC2086 -# shellcheck disable=SC2048 -go test ${TESTFLAGS[*]} ./pkg/... - -CODE=$? -if [ ${CODE} != 0 ]; then - echo "${RED}Tests failed [code ${CODE}].${TEXTRESET}" - exit ${CODE} -else - echo "${GREEN}Tests succeed.${TEXTRESET}" -fi diff --git a/script/validate-lint b/script/validate-lint deleted file mode 100755 index 937f7f13b..000000000 --- a/script/validate-lint +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -golangci-lint run diff --git a/script/validate-misspell b/script/validate-misspell.sh similarity index 100% rename from script/validate-misspell rename to script/validate-misspell.sh diff --git a/script/validate-vendor b/script/validate-vendor.sh similarity index 100% rename from script/validate-vendor rename to script/validate-vendor.sh From 4e0a05406bf57b301ae11dd0ff77794490df6526 Mon Sep 17 00:00:00 2001 From: Robert Socha Date: Wed, 17 Jan 2024 11:32:06 +0100 Subject: [PATCH 7/9] Implements the includedContentTypes option for the compress middleware --- docs/content/middlewares/http/compress.md | 59 ++++- .../dynamic-configuration/docker-labels.yml | 1 + .../reference/dynamic-configuration/file.toml | 1 + .../reference/dynamic-configuration/file.yaml | 3 + .../kubernetes-crd-definition-v1.yml | 7 + .../reference/dynamic-configuration/kv-ref.md | 2 + .../marathon-labels.json | 1 + .../traefik.io_middlewares.yaml | 7 + integration/fixtures/k8s/01-traefik-crd.yml | 7 + pkg/config/dynamic/middlewares.go | 2 + pkg/config/dynamic/zz_generated.deepcopy.go | 5 + pkg/middlewares/compress/brotli/brotli.go | 46 +++- .../compress/brotli/brotli_test.go | 238 +++++++++++++++++- pkg/middlewares/compress/compress.go | 45 +++- pkg/middlewares/compress/compress_test.go | 69 ++++- 15 files changed, 469 insertions(+), 24 deletions(-) diff --git a/docs/content/middlewares/http/compress.md b/docs/content/middlewares/http/compress.md index cd49d757b..e98954660 100644 --- a/docs/content/middlewares/http/compress.md +++ b/docs/content/middlewares/http/compress.md @@ -58,7 +58,7 @@ http: If the `Accept-Encoding` request header is absent, the response won't be encoded. If it is present, but its value is the empty string, then compression is disabled. * The response is not already compressed, i.e. the `Content-Encoding` response header is not already set. - * The response`Content-Type` header is not one among the [excludedContentTypes options](#excludedcontenttypes). + * The response`Content-Type` header is not one among the [excludedContentTypes options](#excludedcontenttypes), or is one among the [includedContentTypes options](#includedcontenttypes). * The response body is larger than the [configured minimum amount of bytes](#minresponsebodybytes) (default is `1024`). ## Configuration Options @@ -73,6 +73,10 @@ The responses with content types defined in `excludedContentTypes` are not compr Content types are compared in a case-insensitive, whitespace-ignored manner. +!!! info + + The `excludedContentTypes` and `includedContentTypes` options are mutually exclusive. + !!! info "In the case of gzip" If the `Content-Type` header is not defined, or empty, the compress middleware will automatically [detect](https://mimesniff.spec.whatwg.org/) a content type. @@ -117,6 +121,59 @@ http: excludedContentTypes = ["text/event-stream"] ``` +### `includedContentTypes` + +_Optional, Default=""_ + +`includedContentTypes` specifies a list of content types to compare the `Content-Type` header of the responses before compressing. + +The responses with content types defined in `includedContentTypes` are compressed. + +Content types are compared in a case-insensitive, whitespace-ignored manner. + +!!! info + + The `excludedContentTypes` and `includedContentTypes` options are mutually exclusive. + +```yaml tab="Docker & Swarm" +labels: + - "traefik.http.middlewares.test-compress.compress.includedcontenttypes=application/json,text/html,text/plain" +``` + +```yaml tab="Kubernetes" +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: test-compress +spec: + compress: + includedContentTypes: + - application/json + - text/html + - text/plain +``` + +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-compress.compress.includedcontenttypes=application/json,text/html,text/plain" +``` + +```yaml tab="File (YAML)" +http: + middlewares: + test-compress: + compress: + includedContentTypes: + - application/json + - text/html + - text/plain +``` + +```toml tab="File (TOML)" +[http.middlewares] + [http.middlewares.test-compress.compress] + includedContentTypes = ["application/json","text/html","text/plain"] +``` + ### `minResponseBodyBytes` _Optional, Default=1024_ diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml index bfd7b0b2b..1c9bbb97d 100644 --- a/docs/content/reference/dynamic-configuration/docker-labels.yml +++ b/docs/content/reference/dynamic-configuration/docker-labels.yml @@ -18,6 +18,7 @@ - "traefik.http.middlewares.middleware05.circuitbreaker.recoveryduration=42s" - "traefik.http.middlewares.middleware06.compress=true" - "traefik.http.middlewares.middleware06.compress.excludedcontenttypes=foobar, foobar" +- "traefik.http.middlewares.middleware06.compress.includedcontenttypes=foobar, foobar" - "traefik.http.middlewares.middleware06.compress.minresponsebodybytes=42" - "traefik.http.middlewares.middleware07.contenttype=true" - "traefik.http.middlewares.middleware08.digestauth.headerfield=foobar" diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index 9be839a89..59a101d10 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -134,6 +134,7 @@ [http.middlewares.Middleware06] [http.middlewares.Middleware06.compress] excludedContentTypes = ["foobar", "foobar"] + includedContentTypes = ["foobar", "foobar"] minResponseBodyBytes = 42 [http.middlewares.Middleware07] [http.middlewares.Middleware07.contentType] diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index fdab6249a..44dfd6beb 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -141,6 +141,9 @@ http: excludedContentTypes: - foobar - foobar + includedContentTypes: + - foobar + - foobar minResponseBodyBytes: 42 Middleware07: contentType: {} diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml index ed7b19817..2e3ae67a5 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml @@ -750,6 +750,13 @@ spec: items: type: string type: array + includedContentTypes: + description: IncludedContentTypes defines the list of content + types to compare the Content-Type header of the responses before + compressing. + items: + type: string + type: array minResponseBodyBytes: description: 'MinResponseBodyBytes defines the minimum amount of bytes a response body must have to be compressed. Default: diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md index f9196fb71..c8030e44c 100644 --- a/docs/content/reference/dynamic-configuration/kv-ref.md +++ b/docs/content/reference/dynamic-configuration/kv-ref.md @@ -22,6 +22,8 @@ THIS FILE MUST NOT BE EDITED BY HAND | `traefik/http/middlewares/Middleware05/circuitBreaker/recoveryDuration` | `42s` | | `traefik/http/middlewares/Middleware06/compress/excludedContentTypes/0` | `foobar` | | `traefik/http/middlewares/Middleware06/compress/excludedContentTypes/1` | `foobar` | +| `traefik/http/middlewares/Middleware06/compress/includedContentTypes/0` | `foobar` | +| `traefik/http/middlewares/Middleware06/compress/includedContentTypes/1` | `foobar` | | `traefik/http/middlewares/Middleware06/compress/minResponseBodyBytes` | `42` | | `traefik/http/middlewares/Middleware07/contentType` | `` | | `traefik/http/middlewares/Middleware08/digestAuth/headerField` | `foobar` | diff --git a/docs/content/reference/dynamic-configuration/marathon-labels.json b/docs/content/reference/dynamic-configuration/marathon-labels.json index 7b971dd6f..8faf12ad7 100644 --- a/docs/content/reference/dynamic-configuration/marathon-labels.json +++ b/docs/content/reference/dynamic-configuration/marathon-labels.json @@ -18,6 +18,7 @@ "traefik.http.middlewares.middleware05.circuitbreaker.recoveryduration": "42s", "traefik.http.middlewares.middleware06.compress": "true", "traefik.http.middlewares.middleware06.compress.excludedcontenttypes": "foobar, foobar", +"traefik.http.middlewares.middleware06.compress.includedcontenttypes": "foobar, foobar", "traefik.http.middlewares.middleware06.compress.minresponsebodybytes": "42", "traefik.http.middlewares.middleware07.contenttype": "true", "traefik.http.middlewares.middleware08.digestauth.headerfield": "foobar", diff --git a/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml b/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml index 54301dbae..9565cf958 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml @@ -175,6 +175,13 @@ spec: items: type: string type: array + includedContentTypes: + description: IncludedContentTypes defines the list of content + types to compare the Content-Type header of the responses before + compressing. + items: + type: string + type: array minResponseBodyBytes: description: 'MinResponseBodyBytes defines the minimum amount of bytes a response body must have to be compressed. Default: diff --git a/integration/fixtures/k8s/01-traefik-crd.yml b/integration/fixtures/k8s/01-traefik-crd.yml index ed7b19817..2e3ae67a5 100644 --- a/integration/fixtures/k8s/01-traefik-crd.yml +++ b/integration/fixtures/k8s/01-traefik-crd.yml @@ -750,6 +750,13 @@ spec: items: type: string type: array + includedContentTypes: + description: IncludedContentTypes defines the list of content + types to compare the Content-Type header of the responses before + compressing. + items: + type: string + type: array minResponseBodyBytes: description: 'MinResponseBodyBytes defines the minimum amount of bytes a response body must have to be compressed. Default: diff --git a/pkg/config/dynamic/middlewares.go b/pkg/config/dynamic/middlewares.go index b6482d8ef..a2c1794eb 100644 --- a/pkg/config/dynamic/middlewares.go +++ b/pkg/config/dynamic/middlewares.go @@ -159,6 +159,8 @@ type Compress struct { // ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing. // `application/grpc` is always excluded. ExcludedContentTypes []string `json:"excludedContentTypes,omitempty" toml:"excludedContentTypes,omitempty" yaml:"excludedContentTypes,omitempty" export:"true"` + // IncludedContentTypes defines the list of content types to compare the Content-Type header of the responses before compressing. + IncludedContentTypes []string `json:"includedContentTypes,omitempty" toml:"includedContentTypes,omitempty" yaml:"includedContentTypes,omitempty" export:"true"` // MinResponseBodyBytes defines the minimum amount of bytes a response body must have to be compressed. // Default: 1024. MinResponseBodyBytes int `json:"minResponseBodyBytes,omitempty" toml:"minResponseBodyBytes,omitempty" yaml:"minResponseBodyBytes,omitempty" export:"true"` diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go index 84755c5cb..847e350ba 100644 --- a/pkg/config/dynamic/zz_generated.deepcopy.go +++ b/pkg/config/dynamic/zz_generated.deepcopy.go @@ -132,6 +132,11 @@ func (in *Compress) DeepCopyInto(out *Compress) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.IncludedContentTypes != nil { + in, out := &in.IncludedContentTypes, &out.IncludedContentTypes + *out = make([]string, len(*in)) + copy(*out, *in) + } return } diff --git a/pkg/middlewares/compress/brotli/brotli.go b/pkg/middlewares/compress/brotli/brotli.go index ebc93b5b3..c95a5ff66 100644 --- a/pkg/middlewares/compress/brotli/brotli.go +++ b/pkg/middlewares/compress/brotli/brotli.go @@ -22,7 +22,11 @@ const ( // Config is the Brotli handler configuration. type Config struct { // ExcludedContentTypes is the list of content types for which we should not compress. + // Mutually exclusive with the IncludedContentTypes option. ExcludedContentTypes []string + // IncludedContentTypes is the list of content types for which compression should be exclusively enabled. + // Mutually exclusive with the ExcludedContentTypes option. + IncludedContentTypes []string // MinSize is the minimum size (in bytes) required to enable compression. MinSize int } @@ -33,14 +37,28 @@ func NewWrapper(cfg Config) (func(http.Handler) http.HandlerFunc, error) { return nil, fmt.Errorf("minimum size must be greater than or equal to zero") } - var contentTypes []parsedContentType + if len(cfg.ExcludedContentTypes) > 0 && len(cfg.IncludedContentTypes) > 0 { + return nil, fmt.Errorf("excludedContentTypes and includedContentTypes options are mutually exclusive") + } + + var excludedContentTypes []parsedContentType for _, v := range cfg.ExcludedContentTypes { mediaType, params, err := mime.ParseMediaType(v) if err != nil { - return nil, fmt.Errorf("parsing media type: %w", err) + return nil, fmt.Errorf("parsing excluded media type: %w", err) } - contentTypes = append(contentTypes, parsedContentType{mediaType, params}) + excludedContentTypes = append(excludedContentTypes, parsedContentType{mediaType, params}) + } + + var includedContentTypes []parsedContentType + for _, v := range cfg.IncludedContentTypes { + mediaType, params, err := mime.ParseMediaType(v) + if err != nil { + return nil, fmt.Errorf("parsing included media type: %w", err) + } + + includedContentTypes = append(includedContentTypes, parsedContentType{mediaType, params}) } return func(h http.Handler) http.HandlerFunc { @@ -52,7 +70,8 @@ func NewWrapper(cfg Config) (func(http.Handler) http.HandlerFunc, error) { bw: brotli.NewWriter(rw), minSize: cfg.MinSize, statusCode: http.StatusOK, - excludedContentTypes: contentTypes, + excludedContentTypes: excludedContentTypes, + includedContentTypes: includedContentTypes, } defer brw.close() @@ -69,6 +88,7 @@ type responseWriter struct { minSize int excludedContentTypes []parsedContentType + includedContentTypes []parsedContentType buf []byte hijacked bool @@ -121,11 +141,25 @@ func (r *responseWriter) Write(p []byte) (int, error) { return r.rw.Write(p) } - // Disable compression according to user wishes in excludedContentTypes. + // Disable compression according to user wishes in excludedContentTypes or includedContentTypes. if ct := r.rw.Header().Get(contentType); ct != "" { mediaType, params, err := mime.ParseMediaType(ct) if err != nil { - return 0, fmt.Errorf("parsing media type: %w", err) + return 0, fmt.Errorf("parsing content-type media type: %w", err) + } + + if len(r.includedContentTypes) > 0 { + var found bool + for _, includedContentType := range r.includedContentTypes { + if includedContentType.equals(mediaType, params) { + found = true + break + } + } + if !found { + r.compressionDisabled = true + return r.rw.Write(p) + } } for _, excludedContentType := range r.excludedContentTypes { diff --git a/pkg/middlewares/compress/brotli/brotli_test.go b/pkg/middlewares/compress/brotli/brotli_test.go index ddbced0b7..5e1bfeea7 100644 --- a/pkg/middlewares/compress/brotli/brotli_test.go +++ b/pkg/middlewares/compress/brotli/brotli_test.go @@ -291,10 +291,9 @@ func Test_ExcludedContentTypes(t *testing.T) { expCompression bool }{ { - desc: "Always compress when content types are empty", - contentType: "", - excludedContentTypes: []string{}, - expCompression: true, + desc: "Always compress when content types are empty", + contentType: "", + expCompression: true, }, { desc: "MIME match", @@ -389,6 +388,111 @@ func Test_ExcludedContentTypes(t *testing.T) { } } +func Test_IncludedContentTypes(t *testing.T) { + testCases := []struct { + desc string + contentType string + includedContentTypes []string + expCompression bool + }{ + { + desc: "Always compress when content types are empty", + contentType: "", + expCompression: true, + }, + { + desc: "MIME match", + contentType: "application/json", + includedContentTypes: []string{"application/json"}, + expCompression: true, + }, + { + desc: "MIME no match", + contentType: "text/xml", + includedContentTypes: []string{"application/json"}, + expCompression: false, + }, + { + desc: "MIME match with no other directive ignores non-MIME directives", + contentType: "application/json; charset=utf-8", + includedContentTypes: []string{"application/json"}, + expCompression: true, + }, + { + desc: "MIME match with other directives requires all directives be equal, different charset", + contentType: "application/json; charset=ascii", + includedContentTypes: []string{"application/json; charset=utf-8"}, + expCompression: false, + }, + { + desc: "MIME match with other directives requires all directives be equal, same charset", + contentType: "application/json; charset=utf-8", + includedContentTypes: []string{"application/json; charset=utf-8"}, + expCompression: true, + }, + { + desc: "MIME match with other directives requires all directives be equal, missing charset", + contentType: "application/json", + includedContentTypes: []string{"application/json; charset=ascii"}, + expCompression: false, + }, + { + desc: "MIME match case insensitive", + contentType: "Application/Json", + includedContentTypes: []string{"application/json"}, + expCompression: true, + }, + { + desc: "MIME match ignore whitespace", + contentType: "application/json;charset=utf-8", + includedContentTypes: []string{"application/json; charset=utf-8"}, + expCompression: true, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + cfg := Config{ + MinSize: 1024, + IncludedContentTypes: test.includedContentTypes, + } + h := mustNewWrapper(t, cfg)(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set(contentType, test.contentType) + + rw.WriteHeader(http.StatusOK) + + _, err := rw.Write(bigTestBody) + require.NoError(t, err) + })) + + req, _ := http.NewRequest(http.MethodGet, "/whatever", nil) + req.Header.Set(acceptEncoding, "br") + + rw := httptest.NewRecorder() + h.ServeHTTP(rw, req) + + assert.Equal(t, http.StatusOK, rw.Code) + + if test.expCompression { + assert.Equal(t, "br", rw.Header().Get(contentEncoding)) + + got, err := io.ReadAll(brotli.NewReader(rw.Body)) + assert.NoError(t, err) + assert.Equal(t, bigTestBody, got) + } else { + assert.NotEqual(t, "br", rw.Header().Get("Content-Encoding")) + + got, err := io.ReadAll(rw.Body) + assert.NoError(t, err) + assert.Equal(t, bigTestBody, got) + } + }) + } +} + func Test_FlushExcludedContentTypes(t *testing.T) { testCases := []struct { desc string @@ -397,10 +501,9 @@ func Test_FlushExcludedContentTypes(t *testing.T) { expCompression bool }{ { - desc: "Always compress when content types are empty", - contentType: "", - excludedContentTypes: []string{}, - expCompression: true, + desc: "Always compress when content types are empty", + contentType: "", + expCompression: true, }, { desc: "MIME match", @@ -509,6 +612,125 @@ func Test_FlushExcludedContentTypes(t *testing.T) { } } +func Test_FlushIncludedContentTypes(t *testing.T) { + testCases := []struct { + desc string + contentType string + includedContentTypes []string + expCompression bool + }{ + { + desc: "Always compress when content types are empty", + contentType: "", + expCompression: true, + }, + { + desc: "MIME match", + contentType: "application/json", + includedContentTypes: []string{"application/json"}, + expCompression: true, + }, + { + desc: "MIME no match", + contentType: "text/xml", + includedContentTypes: []string{"application/json"}, + expCompression: false, + }, + { + desc: "MIME match with no other directive ignores non-MIME directives", + contentType: "application/json; charset=utf-8", + includedContentTypes: []string{"application/json"}, + expCompression: true, + }, + { + desc: "MIME match with other directives requires all directives be equal, different charset", + contentType: "application/json; charset=ascii", + includedContentTypes: []string{"application/json; charset=utf-8"}, + expCompression: false, + }, + { + desc: "MIME match with other directives requires all directives be equal, same charset", + contentType: "application/json; charset=utf-8", + includedContentTypes: []string{"application/json; charset=utf-8"}, + expCompression: true, + }, + { + desc: "MIME match with other directives requires all directives be equal, missing charset", + contentType: "application/json", + includedContentTypes: []string{"application/json; charset=ascii"}, + expCompression: false, + }, + { + desc: "MIME match case insensitive", + contentType: "Application/Json", + includedContentTypes: []string{"application/json"}, + expCompression: true, + }, + { + desc: "MIME match ignore whitespace", + contentType: "application/json;charset=utf-8", + includedContentTypes: []string{"application/json; charset=utf-8"}, + expCompression: true, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + cfg := Config{ + MinSize: 1024, + IncludedContentTypes: test.includedContentTypes, + } + h := mustNewWrapper(t, cfg)(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set(contentType, test.contentType) + rw.WriteHeader(http.StatusOK) + + tb := bigTestBody + for len(tb) > 0 { + // Write 100 bytes per run + // Detection should not be affected (we send 100 bytes) + toWrite := 100 + if toWrite > len(tb) { + toWrite = len(tb) + } + + _, err := rw.Write(tb[:toWrite]) + require.NoError(t, err) + + // Flush between each write + rw.(http.Flusher).Flush() + tb = tb[toWrite:] + } + })) + + req, _ := http.NewRequest(http.MethodGet, "/whatever", nil) + req.Header.Set(acceptEncoding, "br") + + // This doesn't allow checking flushes, but we validate if content is correct. + rw := httptest.NewRecorder() + h.ServeHTTP(rw, req) + + assert.Equal(t, http.StatusOK, rw.Code) + + if test.expCompression { + assert.Equal(t, "br", rw.Header().Get(contentEncoding)) + + got, err := io.ReadAll(brotli.NewReader(rw.Body)) + assert.NoError(t, err) + assert.Equal(t, bigTestBody, got) + } else { + assert.NotEqual(t, "br", rw.Header().Get(contentEncoding)) + + got, err := io.ReadAll(rw.Body) + assert.NoError(t, err) + assert.Equal(t, bigTestBody, got) + } + }) + } +} + func mustNewWrapper(t *testing.T, cfg Config) func(http.Handler) http.HandlerFunc { t.Helper() diff --git a/pkg/middlewares/compress/compress.go b/pkg/middlewares/compress/compress.go index 4618a0a12..0cdd29883 100644 --- a/pkg/middlewares/compress/compress.go +++ b/pkg/middlewares/compress/compress.go @@ -26,6 +26,7 @@ type compress struct { next http.Handler name string excludes []string + includes []string minSize int brotliHandler http.Handler @@ -36,16 +37,30 @@ type compress struct { func New(ctx context.Context, next http.Handler, conf dynamic.Compress, name string) (http.Handler, error) { middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware") + if len(conf.ExcludedContentTypes) > 0 && len(conf.IncludedContentTypes) > 0 { + return nil, fmt.Errorf("excludedContentTypes and includedContentTypes options are mutually exclusive") + } + excludes := []string{"application/grpc"} for _, v := range conf.ExcludedContentTypes { mediaType, _, err := mime.ParseMediaType(v) if err != nil { - return nil, err + return nil, fmt.Errorf("parsing excluded media type: %w", err) } excludes = append(excludes, mediaType) } + var includes []string + for _, v := range conf.IncludedContentTypes { + mediaType, _, err := mime.ParseMediaType(v) + if err != nil { + return nil, fmt.Errorf("parsing included media type: %w", err) + } + + includes = append(includes, mediaType) + } + minSize := DefaultMinSize if conf.MinResponseBodyBytes > 0 { minSize = conf.MinResponseBodyBytes @@ -55,6 +70,7 @@ func New(ctx context.Context, next http.Handler, conf dynamic.Compress, name str next: next, name: name, excludes: excludes, + includes: includes, minSize: minSize, } @@ -118,10 +134,21 @@ func (c *compress) GetTracingInformation() (string, string, trace.SpanKind) { } func (c *compress) newGzipHandler() (http.Handler, error) { - wrapper, err := gzhttp.NewWrapper( - gzhttp.ExceptContentTypes(c.excludes), - gzhttp.MinSize(c.minSize), - ) + var wrapper func(http.Handler) http.HandlerFunc + var err error + + if len(c.includes) > 0 { + wrapper, err = gzhttp.NewWrapper( + gzhttp.ContentTypes(c.includes), + gzhttp.MinSize(c.minSize), + ) + } else { + wrapper, err = gzhttp.NewWrapper( + gzhttp.ExceptContentTypes(c.excludes), + gzhttp.MinSize(c.minSize), + ) + } + if err != nil { return nil, fmt.Errorf("new gzip wrapper: %w", err) } @@ -130,9 +157,11 @@ func (c *compress) newGzipHandler() (http.Handler, error) { } func (c *compress) newBrotliHandler() (http.Handler, error) { - cfg := brotli.Config{ - ExcludedContentTypes: c.excludes, - MinSize: c.minSize, + cfg := brotli.Config{MinSize: c.minSize} + if len(c.includes) > 0 { + cfg.IncludedContentTypes = c.includes + } else { + cfg.ExcludedContentTypes = c.excludes } wrapper, err := brotli.NewWrapper(cfg) diff --git a/pkg/middlewares/compress/compress_test.go b/pkg/middlewares/compress/compress_test.go index 44e28b11f..722001da3 100644 --- a/pkg/middlewares/compress/compress_test.go +++ b/pkg/middlewares/compress/compress_test.go @@ -271,7 +271,28 @@ func TestShouldNotCompressWhenSpecificContentType(t *testing.T) { respContentType: "text/event-stream", }, { - desc: "application/grpc", + desc: "Include Response Content-Type", + conf: dynamic.Compress{ + IncludedContentTypes: []string{"text/plain"}, + }, + respContentType: "text/html", + }, + { + desc: "Ignoring application/grpc with exclude option", + conf: dynamic.Compress{ + ExcludedContentTypes: []string{"application/json"}, + }, + reqContentType: "application/grpc", + }, + { + desc: "Ignoring application/grpc with include option", + conf: dynamic.Compress{ + IncludedContentTypes: []string{"application/json"}, + }, + reqContentType: "application/grpc", + }, + { + desc: "Ignoring application/grpc with no option", conf: dynamic.Compress{}, reqContentType: "application/grpc", }, @@ -312,6 +333,52 @@ func TestShouldNotCompressWhenSpecificContentType(t *testing.T) { } } +func TestShouldCompressWhenSpecificContentType(t *testing.T) { + baseBody := generateBytes(gzhttp.DefaultMinSize) + + testCases := []struct { + desc string + conf dynamic.Compress + respContentType string + }{ + { + desc: "Include Response Content-Type", + conf: dynamic.Compress{ + IncludedContentTypes: []string{"text/html"}, + }, + respContentType: "text/html", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil) + req.Header.Add(acceptEncodingHeader, gzipValue) + + next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.Header().Set(contentTypeHeader, test.respContentType) + + if _, err := rw.Write(baseBody); err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + } + }) + + handler, err := New(context.Background(), next, test.conf, "test") + require.NoError(t, err) + + rw := httptest.NewRecorder() + handler.ServeHTTP(rw, req) + + assert.Equal(t, gzipValue, rw.Header().Get(contentEncodingHeader)) + assert.Equal(t, acceptEncodingHeader, rw.Header().Get(varyHeader)) + assert.NotEqualValues(t, rw.Body.Bytes(), baseBody) + }) + } +} + func TestIntegrationShouldNotCompress(t *testing.T) { fakeCompressedBody := generateBytes(100000) From 64ff214ff8d3b81f4d7feb60789a4d8fd451002c Mon Sep 17 00:00:00 2001 From: Baptiste Mayelle Date: Wed, 17 Jan 2024 14:54:05 +0100 Subject: [PATCH 8/9] remove marathon labels from gendoc --- .../marathon-labels.json | 216 ------------------ internal/gendoc.go | 19 +- 2 files changed, 1 insertion(+), 234 deletions(-) delete mode 100644 docs/content/reference/dynamic-configuration/marathon-labels.json diff --git a/docs/content/reference/dynamic-configuration/marathon-labels.json b/docs/content/reference/dynamic-configuration/marathon-labels.json deleted file mode 100644 index 8faf12ad7..000000000 --- a/docs/content/reference/dynamic-configuration/marathon-labels.json +++ /dev/null @@ -1,216 +0,0 @@ -// CODE GENERATED AUTOMATICALLY -// THIS FILE MUST NOT BE EDITED BY HAND -"traefik.http.middlewares.middleware01.addprefix.prefix": "foobar", -"traefik.http.middlewares.middleware02.basicauth.headerfield": "foobar", -"traefik.http.middlewares.middleware02.basicauth.realm": "foobar", -"traefik.http.middlewares.middleware02.basicauth.removeheader": "true", -"traefik.http.middlewares.middleware02.basicauth.users": "foobar, foobar", -"traefik.http.middlewares.middleware02.basicauth.usersfile": "foobar", -"traefik.http.middlewares.middleware03.buffering.maxrequestbodybytes": "42", -"traefik.http.middlewares.middleware03.buffering.maxresponsebodybytes": "42", -"traefik.http.middlewares.middleware03.buffering.memrequestbodybytes": "42", -"traefik.http.middlewares.middleware03.buffering.memresponsebodybytes": "42", -"traefik.http.middlewares.middleware03.buffering.retryexpression": "foobar", -"traefik.http.middlewares.middleware04.chain.middlewares": "foobar, foobar", -"traefik.http.middlewares.middleware05.circuitbreaker.checkperiod": "42s", -"traefik.http.middlewares.middleware05.circuitbreaker.expression": "foobar", -"traefik.http.middlewares.middleware05.circuitbreaker.fallbackduration": "42s", -"traefik.http.middlewares.middleware05.circuitbreaker.recoveryduration": "42s", -"traefik.http.middlewares.middleware06.compress": "true", -"traefik.http.middlewares.middleware06.compress.excludedcontenttypes": "foobar, foobar", -"traefik.http.middlewares.middleware06.compress.includedcontenttypes": "foobar, foobar", -"traefik.http.middlewares.middleware06.compress.minresponsebodybytes": "42", -"traefik.http.middlewares.middleware07.contenttype": "true", -"traefik.http.middlewares.middleware08.digestauth.headerfield": "foobar", -"traefik.http.middlewares.middleware08.digestauth.realm": "foobar", -"traefik.http.middlewares.middleware08.digestauth.removeheader": "true", -"traefik.http.middlewares.middleware08.digestauth.users": "foobar, foobar", -"traefik.http.middlewares.middleware08.digestauth.usersfile": "foobar", -"traefik.http.middlewares.middleware09.errors.query": "foobar", -"traefik.http.middlewares.middleware09.errors.service": "foobar", -"traefik.http.middlewares.middleware09.errors.status": "foobar, foobar", -"traefik.http.middlewares.middleware10.forwardauth.addauthcookiestoresponse": "foobar, foobar", -"traefik.http.middlewares.middleware10.forwardauth.address": "foobar", -"traefik.http.middlewares.middleware10.forwardauth.authrequestheaders": "foobar, foobar", -"traefik.http.middlewares.middleware10.forwardauth.authresponseheaders": "foobar, foobar", -"traefik.http.middlewares.middleware10.forwardauth.authresponseheadersregex": "foobar", -"traefik.http.middlewares.middleware10.forwardauth.tls.ca": "foobar", -"traefik.http.middlewares.middleware10.forwardauth.tls.cert": "foobar", -"traefik.http.middlewares.middleware10.forwardauth.tls.insecureskipverify": "true", -"traefik.http.middlewares.middleware10.forwardauth.tls.key": "foobar", -"traefik.http.middlewares.middleware10.forwardauth.trustforwardheader": "true", -"traefik.http.middlewares.middleware11.grpcweb.alloworigins": "foobar, foobar", -"traefik.http.middlewares.middleware12.headers.accesscontrolallowcredentials": "true", -"traefik.http.middlewares.middleware12.headers.accesscontrolallowheaders": "foobar, foobar", -"traefik.http.middlewares.middleware12.headers.accesscontrolallowmethods": "foobar, foobar", -"traefik.http.middlewares.middleware12.headers.accesscontrolalloworiginlist": "foobar, foobar", -"traefik.http.middlewares.middleware12.headers.accesscontrolalloworiginlistregex": "foobar, foobar", -"traefik.http.middlewares.middleware12.headers.accesscontrolexposeheaders": "foobar, foobar", -"traefik.http.middlewares.middleware12.headers.accesscontrolmaxage": "42", -"traefik.http.middlewares.middleware12.headers.addvaryheader": "true", -"traefik.http.middlewares.middleware12.headers.allowedhosts": "foobar, foobar", -"traefik.http.middlewares.middleware12.headers.browserxssfilter": "true", -"traefik.http.middlewares.middleware12.headers.contentsecuritypolicy": "foobar", -"traefik.http.middlewares.middleware12.headers.contenttypenosniff": "true", -"traefik.http.middlewares.middleware12.headers.custombrowserxssvalue": "foobar", -"traefik.http.middlewares.middleware12.headers.customframeoptionsvalue": "foobar", -"traefik.http.middlewares.middleware12.headers.customrequestheaders.name0": "foobar", -"traefik.http.middlewares.middleware12.headers.customrequestheaders.name1": "foobar", -"traefik.http.middlewares.middleware12.headers.customresponseheaders.name0": "foobar", -"traefik.http.middlewares.middleware12.headers.customresponseheaders.name1": "foobar", -"traefik.http.middlewares.middleware12.headers.forcestsheader": "true", -"traefik.http.middlewares.middleware12.headers.framedeny": "true", -"traefik.http.middlewares.middleware12.headers.hostsproxyheaders": "foobar, foobar", -"traefik.http.middlewares.middleware12.headers.isdevelopment": "true", -"traefik.http.middlewares.middleware12.headers.permissionspolicy": "foobar", -"traefik.http.middlewares.middleware12.headers.publickey": "foobar", -"traefik.http.middlewares.middleware12.headers.referrerpolicy": "foobar", -"traefik.http.middlewares.middleware12.headers.sslproxyheaders.name0": "foobar", -"traefik.http.middlewares.middleware12.headers.sslproxyheaders.name1": "foobar", -"traefik.http.middlewares.middleware12.headers.stsincludesubdomains": "true", -"traefik.http.middlewares.middleware12.headers.stspreload": "true", -"traefik.http.middlewares.middleware12.headers.stsseconds": "42", -"traefik.http.middlewares.middleware13.ipallowlist.ipstrategy": "true", -"traefik.http.middlewares.middleware13.ipallowlist.ipstrategy.depth": "42", -"traefik.http.middlewares.middleware13.ipallowlist.ipstrategy.excludedips": "foobar, foobar", -"traefik.http.middlewares.middleware13.ipallowlist.rejectstatuscode": "42", -"traefik.http.middlewares.middleware13.ipallowlist.sourcerange": "foobar, foobar", -"traefik.http.middlewares.middleware14.ipwhitelist.ipstrategy": "true", -"traefik.http.middlewares.middleware14.ipwhitelist.ipstrategy.depth": "42", -"traefik.http.middlewares.middleware14.ipwhitelist.ipstrategy.excludedips": "foobar, foobar", -"traefik.http.middlewares.middleware14.ipwhitelist.sourcerange": "foobar, foobar", -"traefik.http.middlewares.middleware15.inflightreq.amount": "42", -"traefik.http.middlewares.middleware15.inflightreq.sourcecriterion.ipstrategy.depth": "42", -"traefik.http.middlewares.middleware15.inflightreq.sourcecriterion.ipstrategy.excludedips": "foobar, foobar", -"traefik.http.middlewares.middleware15.inflightreq.sourcecriterion.requestheadername": "foobar", -"traefik.http.middlewares.middleware15.inflightreq.sourcecriterion.requesthost": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.issuer.commonname": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.issuer.country": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.issuer.domaincomponent": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.issuer.locality": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.issuer.organization": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.issuer.province": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.issuer.serialnumber": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.notafter": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.notbefore": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.sans": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.serialnumber": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.commonname": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.country": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.domaincomponent": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.locality": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.organization": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.organizationalunit": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.province": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.serialnumber": "true", -"traefik.http.middlewares.middleware16.passtlsclientcert.pem": "true", -"traefik.http.middlewares.middleware17.plugin.pluginconf0.name0": "foobar", -"traefik.http.middlewares.middleware17.plugin.pluginconf0.name1": "foobar", -"traefik.http.middlewares.middleware17.plugin.pluginconf1.name0": "foobar", -"traefik.http.middlewares.middleware17.plugin.pluginconf1.name1": "foobar", -"traefik.http.middlewares.middleware18.ratelimit.average": "42", -"traefik.http.middlewares.middleware18.ratelimit.burst": "42", -"traefik.http.middlewares.middleware18.ratelimit.period": "42s", -"traefik.http.middlewares.middleware18.ratelimit.sourcecriterion.ipstrategy.depth": "42", -"traefik.http.middlewares.middleware18.ratelimit.sourcecriterion.ipstrategy.excludedips": "foobar, foobar", -"traefik.http.middlewares.middleware18.ratelimit.sourcecriterion.requestheadername": "foobar", -"traefik.http.middlewares.middleware18.ratelimit.sourcecriterion.requesthost": "true", -"traefik.http.middlewares.middleware19.redirectregex.permanent": "true", -"traefik.http.middlewares.middleware19.redirectregex.regex": "foobar", -"traefik.http.middlewares.middleware19.redirectregex.replacement": "foobar", -"traefik.http.middlewares.middleware20.redirectscheme.permanent": "true", -"traefik.http.middlewares.middleware20.redirectscheme.port": "foobar", -"traefik.http.middlewares.middleware20.redirectscheme.scheme": "foobar", -"traefik.http.middlewares.middleware21.replacepath.path": "foobar", -"traefik.http.middlewares.middleware22.replacepathregex.regex": "foobar", -"traefik.http.middlewares.middleware22.replacepathregex.replacement": "foobar", -"traefik.http.middlewares.middleware23.retry.attempts": "42", -"traefik.http.middlewares.middleware23.retry.initialinterval": "42s", -"traefik.http.middlewares.middleware24.stripprefix.prefixes": "foobar, foobar", -"traefik.http.middlewares.middleware25.stripprefixregex.regex": "foobar, foobar", -"traefik.http.routers.router0.entrypoints": "foobar, foobar", -"traefik.http.routers.router0.middlewares": "foobar, foobar", -"traefik.http.routers.router0.priority": "42", -"traefik.http.routers.router0.rule": "foobar", -"traefik.http.routers.router0.service": "foobar", -"traefik.http.routers.router0.tls": "true", -"traefik.http.routers.router0.tls.certresolver": "foobar", -"traefik.http.routers.router0.tls.domains[0].main": "foobar", -"traefik.http.routers.router0.tls.domains[0].sans": "foobar, foobar", -"traefik.http.routers.router0.tls.domains[1].main": "foobar", -"traefik.http.routers.router0.tls.domains[1].sans": "foobar, foobar", -"traefik.http.routers.router0.tls.options": "foobar", -"traefik.http.routers.router1.entrypoints": "foobar, foobar", -"traefik.http.routers.router1.middlewares": "foobar, foobar", -"traefik.http.routers.router1.priority": "42", -"traefik.http.routers.router1.rule": "foobar", -"traefik.http.routers.router1.service": "foobar", -"traefik.http.routers.router1.tls": "true", -"traefik.http.routers.router1.tls.certresolver": "foobar", -"traefik.http.routers.router1.tls.domains[0].main": "foobar", -"traefik.http.routers.router1.tls.domains[0].sans": "foobar, foobar", -"traefik.http.routers.router1.tls.domains[1].main": "foobar", -"traefik.http.routers.router1.tls.domains[1].sans": "foobar, foobar", -"traefik.http.routers.router1.tls.options": "foobar", -"traefik.http.services.service02.loadbalancer.healthcheck.followredirects": "true", -"traefik.http.services.service02.loadbalancer.healthcheck.headers.name0": "foobar", -"traefik.http.services.service02.loadbalancer.healthcheck.headers.name1": "foobar", -"traefik.http.services.service02.loadbalancer.healthcheck.hostname": "foobar", -"traefik.http.services.service02.loadbalancer.healthcheck.interval": "42s", -"traefik.http.services.service02.loadbalancer.healthcheck.method": "foobar", -"traefik.http.services.service02.loadbalancer.healthcheck.mode": "foobar", -"traefik.http.services.service02.loadbalancer.healthcheck.path": "foobar", -"traefik.http.services.service02.loadbalancer.healthcheck.port": "42", -"traefik.http.services.service02.loadbalancer.healthcheck.scheme": "foobar", -"traefik.http.services.service02.loadbalancer.healthcheck.status": "42", -"traefik.http.services.service02.loadbalancer.healthcheck.timeout": "42s", -"traefik.http.services.service02.loadbalancer.passhostheader": "true", -"traefik.http.services.service02.loadbalancer.responseforwarding.flushinterval": "42s", -"traefik.http.services.service02.loadbalancer.serverstransport": "foobar", -"traefik.http.services.service02.loadbalancer.sticky": "true", -"traefik.http.services.service02.loadbalancer.sticky.cookie": "true", -"traefik.http.services.service02.loadbalancer.sticky.cookie.httponly": "true", -"traefik.http.services.service02.loadbalancer.sticky.cookie.name": "foobar", -"traefik.http.services.service02.loadbalancer.sticky.cookie.samesite": "foobar", -"traefik.http.services.service02.loadbalancer.sticky.cookie.secure": "true", -"traefik.http.services.service02.loadbalancer.server.port": "foobar", -"traefik.http.services.service02.loadbalancer.server.scheme": "foobar", -"traefik.tcp.middlewares.tcpmiddleware01.ipallowlist.sourcerange": "foobar, foobar", -"traefik.tcp.middlewares.tcpmiddleware02.ipwhitelist.sourcerange": "foobar, foobar", -"traefik.tcp.middlewares.tcpmiddleware03.inflightconn.amount": "42", -"traefik.tcp.routers.tcprouter0.entrypoints": "foobar, foobar", -"traefik.tcp.routers.tcprouter0.middlewares": "foobar, foobar", -"traefik.tcp.routers.tcprouter0.priority": "42", -"traefik.tcp.routers.tcprouter0.rule": "foobar", -"traefik.tcp.routers.tcprouter0.service": "foobar", -"traefik.tcp.routers.tcprouter0.tls": "true", -"traefik.tcp.routers.tcprouter0.tls.certresolver": "foobar", -"traefik.tcp.routers.tcprouter0.tls.domains[0].main": "foobar", -"traefik.tcp.routers.tcprouter0.tls.domains[0].sans": "foobar, foobar", -"traefik.tcp.routers.tcprouter0.tls.domains[1].main": "foobar", -"traefik.tcp.routers.tcprouter0.tls.domains[1].sans": "foobar, foobar", -"traefik.tcp.routers.tcprouter0.tls.options": "foobar", -"traefik.tcp.routers.tcprouter0.tls.passthrough": "true", -"traefik.tcp.routers.tcprouter1.entrypoints": "foobar, foobar", -"traefik.tcp.routers.tcprouter1.middlewares": "foobar, foobar", -"traefik.tcp.routers.tcprouter1.priority": "42", -"traefik.tcp.routers.tcprouter1.rule": "foobar", -"traefik.tcp.routers.tcprouter1.service": "foobar", -"traefik.tcp.routers.tcprouter1.tls": "true", -"traefik.tcp.routers.tcprouter1.tls.certresolver": "foobar", -"traefik.tcp.routers.tcprouter1.tls.domains[0].main": "foobar", -"traefik.tcp.routers.tcprouter1.tls.domains[0].sans": "foobar, foobar", -"traefik.tcp.routers.tcprouter1.tls.domains[1].main": "foobar", -"traefik.tcp.routers.tcprouter1.tls.domains[1].sans": "foobar, foobar", -"traefik.tcp.routers.tcprouter1.tls.options": "foobar", -"traefik.tcp.routers.tcprouter1.tls.passthrough": "true", -"traefik.tcp.services.tcpservice01.loadbalancer.proxyprotocol": "true", -"traefik.tcp.services.tcpservice01.loadbalancer.proxyprotocol.version": "42", -"traefik.tcp.services.tcpservice01.loadbalancer.serverstransport": "foobar", -"traefik.tcp.services.tcpservice01.loadbalancer.server.port": "foobar", -"traefik.tcp.services.tcpservice01.loadbalancer.server.tls": "true", -"traefik.udp.routers.udprouter0.entrypoints": "foobar, foobar", -"traefik.udp.routers.udprouter0.service": "foobar", -"traefik.udp.routers.udprouter1.entrypoints": "foobar, foobar", -"traefik.udp.routers.udprouter1.service": "foobar", -"traefik.udp.services.udpservice01.loadbalancer.server.port": "foobar", diff --git a/internal/gendoc.go b/internal/gendoc.go index bfabd225e..93c03366e 100644 --- a/internal/gendoc.go +++ b/internal/gendoc.go @@ -121,30 +121,13 @@ func labelsWrite(outputDir string, element *dynamic.Configuration) error { return err } - marathonLabels, err := os.Create(filepath.Join(outputDir, "marathon-labels.json")) - if err != nil { - return err - } - defer marathonLabels.Close() - - // Write the comment at the beginning of the file - if _, err := marathonLabels.WriteString(strings.ReplaceAll(commentGenerated, "##", "//")); err != nil { - return err - } - - for i, k := range keys { + for _, k := range keys { v := labels[k] if v != "" { if v == "42000000000" { v = "42s" } fmt.Fprintln(dockerLabels, `- "`+strings.ToLower(k)+`=`+v+`"`) - - if i == len(keys)-1 { - fmt.Fprintln(marathonLabels, `"`+strings.ToLower(k)+`": "`+v+`"`) - } else { - fmt.Fprintln(marathonLabels, `"`+strings.ToLower(k)+`": "`+v+`",`) - } } } From 0eeb85d01d9f42b76b60004ad4ae5112d0e2e4b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B3=BD?= Date: Thu, 18 Jan 2024 16:30:06 +0800 Subject: [PATCH 9/9] Support setting sticky cookie max age --- .../dynamic-configuration/docker-labels.yml | 1 + .../reference/dynamic-configuration/file.toml | 2 ++ .../reference/dynamic-configuration/file.yaml | 2 ++ .../kubernetes-crd-definition-v1.yml | 36 +++++++++++++++++++ .../reference/dynamic-configuration/kv-ref.md | 2 ++ .../traefik.io_ingressroutes.yaml | 6 ++++ .../traefik.io_middlewares.yaml | 6 ++++ .../traefik.io_traefikservices.yaml | 24 +++++++++++++ .../routing/providers/consul-catalog.md | 8 +++++ docs/content/routing/providers/docker.md | 8 +++++ docs/content/routing/providers/ecs.md | 8 +++++ .../routing/providers/kubernetes-crd.md | 1 + .../routing/providers/kubernetes-ingress.md | 8 +++++ docs/content/routing/providers/kv.md | 14 ++++++++ docs/content/routing/providers/nomad.md | 8 +++++ docs/content/routing/services/index.md | 7 ++++ integration/fixtures/k8s/01-traefik-crd.yml | 36 +++++++++++++++++++ pkg/config/dynamic/http_config.go | 4 +++ pkg/config/label/label_test.go | 1 + pkg/server/service/loadbalancer/wrr/wrr.go | 3 ++ .../service/loadbalancer/wrr/wrr_test.go | 2 ++ 21 files changed, 187 insertions(+) diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml index 1c9bbb97d..93a785ff4 100644 --- a/docs/content/reference/dynamic-configuration/docker-labels.yml +++ b/docs/content/reference/dynamic-configuration/docker-labels.yml @@ -170,6 +170,7 @@ - "traefik.http.services.service02.loadbalancer.sticky=true" - "traefik.http.services.service02.loadbalancer.sticky.cookie=true" - "traefik.http.services.service02.loadbalancer.sticky.cookie.httponly=true" +- "traefik.http.services.service02.loadbalancer.sticky.cookie.maxage=42" - "traefik.http.services.service02.loadbalancer.sticky.cookie.name=foobar" - "traefik.http.services.service02.loadbalancer.sticky.cookie.samesite=foobar" - "traefik.http.services.service02.loadbalancer.sticky.cookie.secure=true" diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index 59a101d10..a8264d2ed 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -52,6 +52,7 @@ secure = true httpOnly = true sameSite = "foobar" + maxAge = 42 [[http.services.Service02.loadBalancer.servers]] url = "foobar" @@ -103,6 +104,7 @@ secure = true httpOnly = true sameSite = "foobar" + maxAge = 42 [http.services.Service04.weighted.healthCheck] [http.middlewares] [http.middlewares.Middleware01] diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index 44dfd6beb..7ae72ad7d 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -60,6 +60,7 @@ http: secure: true httpOnly: true sameSite: foobar + maxAge: 42 servers: - url: foobar - url: foobar @@ -104,6 +105,7 @@ http: secure: true httpOnly: true sameSite: foobar + maxAge: 42 healthCheck: {} middlewares: Middleware01: diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml index 2e3ae67a5..5a4c63c3c 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml @@ -160,6 +160,12 @@ spec: can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds + until the cookie expires. When set to a negative + number, the cookie expires immediately. When + set to zero, the cookie never expires. + type: integer name: description: Name defines the Cookie name. type: string @@ -877,6 +883,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds + until the cookie expires. When set to a negative + number, the cookie expires immediately. When set + to zero, the cookie never expires. + type: integer name: description: Name defines the Cookie name. type: string @@ -2158,6 +2170,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds + until the cookie expires. When set to a negative + number, the cookie expires immediately. When set + to zero, the cookie never expires. + type: integer name: description: Name defines the Cookie name. type: string @@ -2249,6 +2267,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds until + the cookie expires. When set to a negative number, the + cookie expires immediately. When set to zero, the cookie + never expires. + type: integer name: description: Name defines the Cookie name. type: string @@ -2356,6 +2380,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds + until the cookie expires. When set to a negative + number, the cookie expires immediately. When set + to zero, the cookie never expires. + type: integer name: description: Name defines the Cookie name. type: string @@ -2395,6 +2425,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds until + the cookie expires. When set to a negative number, the + cookie expires immediately. When set to zero, the cookie + never expires. + type: integer name: description: Name defines the Cookie name. type: string diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md index c8030e44c..f078b166a 100644 --- a/docs/content/reference/dynamic-configuration/kv-ref.md +++ b/docs/content/reference/dynamic-configuration/kv-ref.md @@ -241,6 +241,7 @@ THIS FILE MUST NOT BE EDITED BY HAND | `traefik/http/services/Service02/loadBalancer/servers/1/url` | `foobar` | | `traefik/http/services/Service02/loadBalancer/serversTransport` | `foobar` | | `traefik/http/services/Service02/loadBalancer/sticky/cookie/httpOnly` | `true` | +| `traefik/http/services/Service02/loadBalancer/sticky/cookie/maxAge` | `42` | | `traefik/http/services/Service02/loadBalancer/sticky/cookie/name` | `foobar` | | `traefik/http/services/Service02/loadBalancer/sticky/cookie/sameSite` | `foobar` | | `traefik/http/services/Service02/loadBalancer/sticky/cookie/secure` | `true` | @@ -257,6 +258,7 @@ THIS FILE MUST NOT BE EDITED BY HAND | `traefik/http/services/Service04/weighted/services/1/name` | `foobar` | | `traefik/http/services/Service04/weighted/services/1/weight` | `42` | | `traefik/http/services/Service04/weighted/sticky/cookie/httpOnly` | `true` | +| `traefik/http/services/Service04/weighted/sticky/cookie/maxAge` | `42` | | `traefik/http/services/Service04/weighted/sticky/cookie/name` | `foobar` | | `traefik/http/services/Service04/weighted/sticky/cookie/sameSite` | `foobar` | | `traefik/http/services/Service04/weighted/sticky/cookie/secure` | `true` | diff --git a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml index 94daf2095..41628b58a 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml @@ -160,6 +160,12 @@ spec: can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds + until the cookie expires. When set to a negative + number, the cookie expires immediately. When + set to zero, the cookie never expires. + type: integer name: description: Name defines the Cookie name. type: string diff --git a/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml b/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml index 9565cf958..b7d8c6b52 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml @@ -302,6 +302,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds + until the cookie expires. When set to a negative + number, the cookie expires immediately. When set + to zero, the cookie never expires. + type: integer name: description: Name defines the Cookie name. type: string diff --git a/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml b/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml index 9724e59b6..8620b4d74 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml @@ -134,6 +134,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds + until the cookie expires. When set to a negative + number, the cookie expires immediately. When set + to zero, the cookie never expires. + type: integer name: description: Name defines the Cookie name. type: string @@ -225,6 +231,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds until + the cookie expires. When set to a negative number, the + cookie expires immediately. When set to zero, the cookie + never expires. + type: integer name: description: Name defines the Cookie name. type: string @@ -332,6 +344,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds + until the cookie expires. When set to a negative + number, the cookie expires immediately. When set + to zero, the cookie never expires. + type: integer name: description: Name defines the Cookie name. type: string @@ -371,6 +389,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds until + the cookie expires. When set to a negative number, the + cookie expires immediately. When set to zero, the cookie + never expires. + type: integer name: description: Name defines the Cookie name. type: string diff --git a/docs/content/routing/providers/consul-catalog.md b/docs/content/routing/providers/consul-catalog.md index 8eb985043..678e23b92 100644 --- a/docs/content/routing/providers/consul-catalog.md +++ b/docs/content/routing/providers/consul-catalog.md @@ -273,6 +273,14 @@ you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.pass traefik.http.services.myservice.loadbalancer.sticky.cookie.samesite=none ``` +??? info "`traefik.http.services..loadbalancer.sticky.cookie.maxage`" + + See [sticky sessions](../services/index.md#sticky-sessions) for more information. + + ```yaml + traefik.http.services.myservice.loadbalancer.sticky.cookie.maxage=42 + ``` + ??? info "`traefik.http.services..loadbalancer.responseforwarding.flushinterval`" See [response forwarding](../services/index.md#response-forwarding) for more information. diff --git a/docs/content/routing/providers/docker.md b/docs/content/routing/providers/docker.md index 4a2b32436..0f6df9a9d 100644 --- a/docs/content/routing/providers/docker.md +++ b/docs/content/routing/providers/docker.md @@ -376,6 +376,14 @@ you'd add the label `traefik.http.services..loadbalancer.pa - "traefik.http.services.myservice.loadbalancer.sticky.cookie.samesite=none" ``` +??? info "`traefik.http.services..loadbalancer.sticky.cookie.maxage`" + + See [sticky sessions](../services/index.md#sticky-sessions) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.sticky.cookie.maxage=42" + ``` + ??? info "`traefik.http.services..loadbalancer.responseforwarding.flushinterval`" See [response forwarding](../services/index.md#response-forwarding) for more information. diff --git a/docs/content/routing/providers/ecs.md b/docs/content/routing/providers/ecs.md index 718303c0b..e5c66a8c3 100644 --- a/docs/content/routing/providers/ecs.md +++ b/docs/content/routing/providers/ecs.md @@ -275,6 +275,14 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa traefik.http.services.myservice.loadbalancer.sticky.cookie.samesite=none ``` +??? info "`traefik.http.services..loadbalancer.sticky.cookie.maxage`" + + See [sticky sessions](../services/index.md#sticky-sessions) for more information. + + ```yaml + traefik.http.services.myservice.loadbalancer.sticky.cookie.maxage=42 + ``` + ??? info "`traefik.http.services..loadbalancer.responseforwarding.flushinterval`" See [response forwarding](../services/index.md#response-forwarding) for more information. diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index c66cb3d52..55f25494a 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -348,6 +348,7 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne name: cookie secure: true sameSite: none + maxAge: 42 strategy: RoundRobin weight: 10 nativeLB: true # [11] diff --git a/docs/content/routing/providers/kubernetes-ingress.md b/docs/content/routing/providers/kubernetes-ingress.md index 5f9a32073..01f1d5166 100644 --- a/docs/content/routing/providers/kubernetes-ingress.md +++ b/docs/content/routing/providers/kubernetes-ingress.md @@ -351,6 +351,14 @@ which in turn will create the resulting routers, services, handlers, etc. traefik.ingress.kubernetes.io/service.sticky.cookie.httponly: "true" ``` +??? info "`traefik.ingress.kubernetes.io/service.sticky.cookie.maxage`" + + See [sticky sessions](../services/index.md#sticky-sessions) for more information. + + ```yaml + traefik.ingress.kubernetes.io/service.sticky.cookie.maxage: 42 + ``` + ## Path Types on Kubernetes 1.18+ If the Kubernetes cluster version is 1.18+, diff --git a/docs/content/routing/providers/kv.md b/docs/content/routing/providers/kv.md index a54ef1c61..3a6cc7744 100644 --- a/docs/content/routing/providers/kv.md +++ b/docs/content/routing/providers/kv.md @@ -244,6 +244,14 @@ A Story of key & values |-----------------------------------------------------------------------|--------| | `traefik/http/services/myservice/loadbalancer/sticky/cookie/samesite` | `none` | +??? info "`traefik/http/services//loadbalancer/sticky/cookie/maxage`" + + See [sticky sessions](../services/index.md#sticky-sessions) for more information. + + | Key (Path) | Value | + |---------------------------------------------------------------------|-------| + | `traefik/http/services/myservice/loadbalancer/sticky/cookie/maxage` | `42` | + ??? info "`traefik/http/services//loadbalancer/responseforwarding/flushinterval`" See [response forwarding](../services/index.md#response-forwarding) for more information. @@ -306,6 +314,12 @@ A Story of key & values |------------------------------------------------------------------------|--------| | `traefik/http/services//weighted/sticky/cookie/httpOnly` | `true` | +??? info "`traefik/http/services//weighted/sticky/cookie/maxage`" + + | Key (Path) | Value | + |----------------------------------------------------------------------|-------| + | `traefik/http/services//weighted/sticky/cookie/maxage` | `42` | + ### Middleware More information about available middlewares in the dedicated [middlewares section](../../middlewares/overview.md). diff --git a/docs/content/routing/providers/nomad.md b/docs/content/routing/providers/nomad.md index b0143d094..e1176c51d 100644 --- a/docs/content/routing/providers/nomad.md +++ b/docs/content/routing/providers/nomad.md @@ -265,6 +265,14 @@ you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.pass traefik.http.services.myservice.loadbalancer.sticky.cookie.samesite=none ``` +??? info "`traefik.http.services..loadbalancer.sticky.cookie.maxage`" + + See [sticky sessions](../services/index.md#sticky-sessions) for more information. + + ```yaml + traefik.http.services.myservice.loadbalancer.sticky.cookie.maxage=42 + ``` + ??? info "`traefik.http.services..loadbalancer.responseforwarding.flushinterval`" See [response forwarding](../services/index.md#response-forwarding) for more information. diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index fb6af7563..7658195e1 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -187,6 +187,13 @@ On subsequent requests, to keep the session alive with the same server, the clie The default cookie name is an abbreviation of a sha1 (ex: `_1d52e`). +!!! info "MaxAge" + + By default, the affinity cookie will never expire as the `MaxAge` option is set to zero. + + This option indicates the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + !!! info "Secure & HTTPOnly & SameSite flags" By default, the affinity cookie is created without those flags. diff --git a/integration/fixtures/k8s/01-traefik-crd.yml b/integration/fixtures/k8s/01-traefik-crd.yml index 2e3ae67a5..5a4c63c3c 100644 --- a/integration/fixtures/k8s/01-traefik-crd.yml +++ b/integration/fixtures/k8s/01-traefik-crd.yml @@ -160,6 +160,12 @@ spec: can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds + until the cookie expires. When set to a negative + number, the cookie expires immediately. When + set to zero, the cookie never expires. + type: integer name: description: Name defines the Cookie name. type: string @@ -877,6 +883,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds + until the cookie expires. When set to a negative + number, the cookie expires immediately. When set + to zero, the cookie never expires. + type: integer name: description: Name defines the Cookie name. type: string @@ -2158,6 +2170,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds + until the cookie expires. When set to a negative + number, the cookie expires immediately. When set + to zero, the cookie never expires. + type: integer name: description: Name defines the Cookie name. type: string @@ -2249,6 +2267,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds until + the cookie expires. When set to a negative number, the + cookie expires immediately. When set to zero, the cookie + never expires. + type: integer name: description: Name defines the Cookie name. type: string @@ -2356,6 +2380,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds + until the cookie expires. When set to a negative + number, the cookie expires immediately. When set + to zero, the cookie never expires. + type: integer name: description: Name defines the Cookie name. type: string @@ -2395,6 +2425,12 @@ spec: description: HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. type: boolean + maxAge: + description: MaxAge indicates the number of seconds until + the cookie expires. When set to a negative number, the + cookie expires immediately. When set to zero, the cookie + never expires. + type: integer name: description: Name defines the Cookie name. type: string diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go index 0c31c8871..a447e94eb 100644 --- a/pkg/config/dynamic/http_config.go +++ b/pkg/config/dynamic/http_config.go @@ -155,6 +155,10 @@ type Cookie struct { // SameSite defines the same site policy. // More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite SameSite string `json:"sameSite,omitempty" toml:"sameSite,omitempty" yaml:"sameSite,omitempty" export:"true"` + // MaxAge indicates the number of seconds until the cookie expires. + // When set to a negative number, the cookie expires immediately. + // When set to zero, the cookie never expires. + MaxAge int `json:"maxAge,omitempty" toml:"maxAge,omitempty" yaml:"maxAge,omitempty" export:"true"` } // +k8s:deepcopy-gen=true diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go index c55a73c15..28606cdbc 100644 --- a/pkg/config/label/label_test.go +++ b/pkg/config/label/label_test.go @@ -1334,6 +1334,7 @@ func TestEncodeConfiguration(t *testing.T) { "traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.Name": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.HTTPOnly": "true", "traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.Secure": "false", + "traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.MaxAge": "0", "traefik.HTTP.Services.Service0.LoadBalancer.ServersTransport": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Headers.name0": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Headers.name1": "foobar", diff --git a/pkg/server/service/loadbalancer/wrr/wrr.go b/pkg/server/service/loadbalancer/wrr/wrr.go index 343c525d2..f38facf35 100644 --- a/pkg/server/service/loadbalancer/wrr/wrr.go +++ b/pkg/server/service/loadbalancer/wrr/wrr.go @@ -25,6 +25,7 @@ type stickyCookie struct { secure bool httpOnly bool sameSite string + maxAge int } func convertSameSite(sameSite string) http.SameSite { @@ -77,6 +78,7 @@ func New(sticky *dynamic.Sticky, wantHealthCheck bool) *Balancer { secure: sticky.Cookie.Secure, httpOnly: sticky.Cookie.HTTPOnly, sameSite: sticky.Cookie.SameSite, + maxAge: sticky.Cookie.MaxAge, } } @@ -238,6 +240,7 @@ func (b *Balancer) ServeHTTP(w http.ResponseWriter, req *http.Request) { HttpOnly: b.stickyCookie.httpOnly, Secure: b.stickyCookie.secure, SameSite: convertSameSite(b.stickyCookie.sameSite), + MaxAge: b.stickyCookie.maxAge, } http.SetCookie(w, cookie) } diff --git a/pkg/server/service/loadbalancer/wrr/wrr_test.go b/pkg/server/service/loadbalancer/wrr/wrr_test.go index 2392ad84b..3708fa618 100644 --- a/pkg/server/service/loadbalancer/wrr/wrr_test.go +++ b/pkg/server/service/loadbalancer/wrr/wrr_test.go @@ -225,6 +225,7 @@ func TestSticky(t *testing.T) { Secure: true, HTTPOnly: true, SameSite: "none", + MaxAge: 42, }, }, false) @@ -261,6 +262,7 @@ func TestSticky(t *testing.T) { assert.True(t, recorder.cookies["test"].HttpOnly) assert.True(t, recorder.cookies["test"].Secure) assert.Equal(t, http.SameSiteNoneMode, recorder.cookies["test"].SameSite) + assert.Equal(t, 42, recorder.cookies["test"].MaxAge) } func TestSticky_FallBack(t *testing.T) {