mirror of
https://github.com/traefik/traefik.git
synced 2026-05-04 20:06:21 +02:00
Merge branch v2.11 into v3.6
This commit is contained in:
commit
59357eeaf8
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,3 +1,13 @@
|
||||
## [v2.11.44](https://github.com/traefik/traefik/tree/v2.11.44) (2026-04-29)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.43...v2.11.44)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[middleware]** Add errorRequestHeaders option to Errors middleware ([#13034](https://github.com/traefik/traefik/pull/13034) @gndz07)
|
||||
- **[acme]** Bump github.com/go-acme/lego to v4.35.2 ([#13052](https://github.com/traefik/traefik/pull/13052) @mmatur)
|
||||
|
||||
**Misc:**
|
||||
- Make FLAGS Make variable usable ([#13009](https://github.com/traefik/traefik/pull/13009) @twz123)
|
||||
|
||||
## [v3.6.14](https://github.com/traefik/traefik/tree/v3.6.14) (2026-04-22)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v3.6.13...v3.6.14)
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@ -58,7 +58,7 @@ generate:
|
||||
#? binary: Build the binary
|
||||
binary: generate-webui dist
|
||||
@echo SHA: $(VERSION) $(CODENAME) $(DATE)
|
||||
CGO_ENABLED=0 GOGC=${GOGC} GOOS=${GOOS} GOARCH=${GOARCH} go build ${FLAGS[*]} -ldflags "-s -w \
|
||||
CGO_ENABLED=0 GOGC=${GOGC} GOOS=${GOOS} GOARCH=${GOARCH} go build ${FLAGS} -ldflags "-s -w \
|
||||
-X github.com/traefik/traefik/v3/pkg/version.Version=$(VERSION) \
|
||||
-X github.com/traefik/traefik/v3/pkg/version.Codename=$(CODENAME) \
|
||||
-X github.com/traefik/traefik/v3/pkg/version.BuildDate=$(DATE)" \
|
||||
|
||||
@ -9,6 +9,15 @@ This guide provides detailed migration steps for upgrading between different Tra
|
||||
|
||||
---
|
||||
|
||||
## v3.6.15
|
||||
|
||||
In `v3.6.15`, a new `errorRequestHeaders` option has been added to the Errors middleware.
|
||||
|
||||
By default, the behavior is unchanged: all original request headers are forwarded to the error page service.
|
||||
If the error page service is in a separate trust domain, consider using `errorRequestHeaders` to restrict which headers are forwarded.
|
||||
|
||||
Please check out the [Error Pages](../reference/routing-configuration/http/middlewares/errorpages.md#errorRequestHeaders) middleware documentation for more details.
|
||||
|
||||
## v3.6.14
|
||||
|
||||
### Kubernetes CRD: Chain middleware and `allowCrossNamespace`
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
- "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.errorrequestheaders=foobar, foobar"
|
||||
- "traefik.http.middlewares.middleware09.errors.query=foobar"
|
||||
- "traefik.http.middlewares.middleware09.errors.service=foobar"
|
||||
- "traefik.http.middlewares.middleware09.errors.status=foobar, foobar"
|
||||
|
||||
@ -193,6 +193,7 @@
|
||||
status = ["foobar", "foobar"]
|
||||
service = "foobar"
|
||||
query = "foobar"
|
||||
errorRequestHeaders = ["foobar", "foobar"]
|
||||
[http.middlewares.Middleware09.errors.statusRewrites]
|
||||
name0 = 42
|
||||
name1 = 42
|
||||
|
||||
@ -208,6 +208,9 @@ http:
|
||||
name1: 42
|
||||
service: foobar
|
||||
query: foobar
|
||||
errorRequestHeaders:
|
||||
- foobar
|
||||
- foobar
|
||||
Middleware10:
|
||||
forwardAuth:
|
||||
address: foobar
|
||||
|
||||
@ -129,3 +129,12 @@ The table below lists all the available variables and their associated values.
|
||||
| <a id="opt-status-2" href="#opt-status-2" title="#opt-status-2">`{status}`</a> | The response status code. |
|
||||
| <a id="opt-originalStatus" href="#opt-originalStatus" title="#opt-originalStatus">`{originalStatus}`</a> | The original response status code, if it has been modified by the `statusRewrites` option. |
|
||||
| <a id="opt-url" href="#opt-url" title="#opt-url">`{url}`</a> | The [escaped](https://pkg.go.dev/net/url#QueryEscape) request URL.|
|
||||
|
||||
### `errorRequestHeaders`
|
||||
|
||||
Defines the list of original request headers forwarded to the error page service.
|
||||
|
||||
By default (`errorRequestHeaders` not set), all request headers — including authentication material such as `Authorization` and `Cookie` — are forwarded.
|
||||
If the error page service is in a separate trust domain, use this option to restrict which headers cross the service boundary.
|
||||
|
||||
Set to an explicit list to forward only those headers, or set to an empty list (`errorRequestHeaders: []`) to forward no headers.
|
||||
|
||||
@ -195,6 +195,7 @@
|
||||
status = ["foobar", "foobar"]
|
||||
service = "foobar"
|
||||
query = "foobar"
|
||||
errorRequestHeaders = ["foobar", "foobar"]
|
||||
[http.middlewares.Middleware09.errors.statusRewrites]
|
||||
name0 = 42
|
||||
name1 = 42
|
||||
|
||||
@ -214,6 +214,9 @@ http:
|
||||
name1: 42
|
||||
service: foobar
|
||||
query: foobar
|
||||
errorRequestHeaders:
|
||||
- foobar
|
||||
- foobar
|
||||
Middleware10:
|
||||
forwardAuth:
|
||||
address: foobar
|
||||
|
||||
@ -236,6 +236,10 @@ type ErrorPage struct {
|
||||
// The {originalStatus} variable can be used in order to insert the upstream status code in the URL.
|
||||
// The {url} variable can be used in order to insert the escaped request URL.
|
||||
Query string `json:"query,omitempty" toml:"query,omitempty" yaml:"query,omitempty" export:"true"`
|
||||
// ErrorRequestHeaders defines the list of request headers forwarded to the error page service.
|
||||
// When nil (not set), all original request headers are forwarded.
|
||||
// Set to an empty list to forward no headers, or list specific headers to forward only those.
|
||||
ErrorRequestHeaders []string `json:"errorRequestHeaders,omitempty" toml:"errorRequestHeaders,omitempty" yaml:"errorRequestHeaders,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
@ -321,6 +321,11 @@ func (in *ErrorPage) DeepCopyInto(out *ErrorPage) {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.ErrorRequestHeaders != nil {
|
||||
in, out := &in.ErrorRequestHeaders, &out.ErrorRequestHeaders
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@ type customErrors struct {
|
||||
backendHandler http.Handler
|
||||
httpCodeRanges types.HTTPCodeRanges
|
||||
backendQuery string
|
||||
requestHeaders []string
|
||||
statusRewrites []statusRewrite
|
||||
}
|
||||
|
||||
@ -79,6 +80,7 @@ func New(ctx context.Context, next http.Handler, config dynamic.ErrorPage, servi
|
||||
backendHandler: backend,
|
||||
httpCodeRanges: httpCodeRanges,
|
||||
backendQuery: config.Query,
|
||||
requestHeaders: config.ErrorRequestHeaders,
|
||||
statusRewrites: statusRewrites,
|
||||
}, nil
|
||||
}
|
||||
@ -145,7 +147,15 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
utils.CopyHeaders(pageReq.Header, req.Header)
|
||||
if c.requestHeaders != nil {
|
||||
for _, header := range c.requestHeaders {
|
||||
if values := req.Header.Values(header); len(values) > 0 {
|
||||
pageReq.Header[http.CanonicalHeaderKey(header)] = values
|
||||
}
|
||||
}
|
||||
} else {
|
||||
utils.CopyHeaders(pageReq.Header, req.Header)
|
||||
}
|
||||
c.backendHandler.ServeHTTP(newCodeModifier(rw, code),
|
||||
pageReq.WithContext(req.Context()))
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ func TestHandler(t *testing.T) {
|
||||
backendCode int
|
||||
backendErrorHandler http.HandlerFunc
|
||||
validate func(t *testing.T, recorder *httptest.ResponseRecorder)
|
||||
requestHeaders map[string]string
|
||||
}{
|
||||
{
|
||||
desc: "no error",
|
||||
@ -154,6 +155,60 @@ func TestHandler(t *testing.T) {
|
||||
assert.Contains(t, recorder.Body.String(), "My 503 page.")
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "forward all headers by default",
|
||||
errorPage: &dynamic.ErrorPage{Service: "error", Query: "/test", Status: []string{"503"}},
|
||||
requestHeaders: map[string]string{
|
||||
"X-Request-Id": "trace-abc",
|
||||
"Authorization": "Bearer secret",
|
||||
},
|
||||
backendCode: http.StatusServiceUnavailable,
|
||||
backendErrorHandler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, r.Header.Get("X-Request-Id"))
|
||||
fmt.Fprintln(w, r.Header.Get("Authorization"))
|
||||
}),
|
||||
validate: func(t *testing.T, recorder *httptest.ResponseRecorder) {
|
||||
t.Helper()
|
||||
assert.Contains(t, recorder.Body.String(), "trace-abc")
|
||||
assert.Contains(t, recorder.Body.String(), "Bearer secret")
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "forward only allowlisted headers",
|
||||
errorPage: &dynamic.ErrorPage{Service: "error", Query: "/test", Status: []string{"503"}, ErrorRequestHeaders: []string{"X-Request-Id"}},
|
||||
requestHeaders: map[string]string{
|
||||
"X-Request-Id": "trace-abc",
|
||||
"Authorization": "Bearer secret",
|
||||
},
|
||||
backendCode: http.StatusServiceUnavailable,
|
||||
backendErrorHandler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, r.Header.Get("X-Request-Id"))
|
||||
fmt.Fprintln(w, r.Header.Get("Authorization"))
|
||||
}),
|
||||
validate: func(t *testing.T, recorder *httptest.ResponseRecorder) {
|
||||
t.Helper()
|
||||
assert.Contains(t, recorder.Body.String(), "trace-abc")
|
||||
assert.NotContains(t, recorder.Body.String(), "Bearer secret")
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "forward no headers",
|
||||
errorPage: &dynamic.ErrorPage{Service: "error", Query: "/test", Status: []string{"503"}, ErrorRequestHeaders: []string{}},
|
||||
requestHeaders: map[string]string{
|
||||
"X-Request-Id": "trace-abc",
|
||||
"Authorization": "Bearer secret",
|
||||
},
|
||||
backendCode: http.StatusServiceUnavailable,
|
||||
backendErrorHandler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, r.Header.Get("X-Request-Id"))
|
||||
fmt.Fprintln(w, r.Header.Get("Authorization"))
|
||||
}),
|
||||
validate: func(t *testing.T, recorder *httptest.ResponseRecorder) {
|
||||
t.Helper()
|
||||
assert.NotContains(t, recorder.Body.String(), "trace-abc")
|
||||
assert.NotContains(t, recorder.Body.String(), "Bearer secret")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@ -174,6 +229,9 @@ func TestHandler(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost/test?foo=bar&baz=buz", nil)
|
||||
for k, v := range test.requestHeaders {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
// Client like browser and curl will issue a relative HTTP request, which not have a host and scheme in the URL. But the http.NewRequest will set them automatically.
|
||||
req.URL.Host = ""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user