From 65cca1291dfd91fe6f251feb4291176d8e1069be Mon Sep 17 00:00:00 2001 From: Diego Monti Date: Wed, 11 Mar 2026 14:50:05 +0100 Subject: [PATCH 1/5] Fix unnecessary escaping of pipe in regexp examples --- .../http/routing/rules-and-priority.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/content/reference/routing-configuration/http/routing/rules-and-priority.md b/docs/content/reference/routing-configuration/http/routing/rules-and-priority.md index 0291b9efe8..69712a373b 100644 --- a/docs/content/reference/routing-configuration/http/routing/rules-and-priority.md +++ b/docs/content/reference/routing-configuration/http/routing/rules-and-priority.md @@ -41,8 +41,8 @@ The `Header` and `HeaderRegexp` matchers allow matching requests that contain sp | Behavior | Rule | |-----------------------------------------------------------------|:------------------------------------------------------------------------| | Match requests with a `Content-Type` header set to `application/yaml`. | ```Header(`Content-Type`, `application/yaml`)``` | -| Match requests with a `Content-Type` header set to either `application/json` or `application/yaml`. | ```HeaderRegexp(`Content-Type`, `^application/(json\|yaml)$`)``` | -| Match headers [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```HeaderRegexp(`Content-Type`, `(?i)^application/(json\|yaml)$`)``` | +| Match requests with a `Content-Type` header set to either `application/json` or `application/yaml`. | ```HeaderRegexp(`Content-Type`, `^application/(json|yaml)$`)``` | +| Match headers [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```HeaderRegexp(`Content-Type`, `(?i)^application/(json|yaml)$`)``` | ### Host and HostRegexp @@ -58,8 +58,8 @@ These matchers will match the request's host in lowercase. |-----------------------------------------------------------------|:------------------------------------------------------------------------| | Match requests with `Host` set to `example.com`. | ```Host(`example.com`)``` | | Match requests sent to any subdomain of `example.com`. | ```HostRegexp(`^.+\.example\.com$`)``` | -| Match requests with `Host` set to either `example.com` or `example.org`. | ```HostRegexp(`^example\.(com\|org)$`)``` | -| Match `Host` [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```HostRegexp(`(?i)^example\.(com\|org)$`)``` | +| Match requests with `Host` set to either `example.com` or `example.org`. | ```HostRegexp(`^example\.(com|org)$`)``` | +| Match `Host` [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```HostRegexp(`(?i)^example\.(com|org)$`)``` | ### Method @@ -81,8 +81,8 @@ Path are always starting with a `/`, except for `PathRegexp`. |-----------------------------------------------------------------|:------------------------------------------------------------------------| | Match `/products` but neither `/products/shoes` nor `/products/`. | ```Path(`/products`)``` | | Match `/products` as well as everything under `/products`, such as `/products/shoes`, `/products/` but also `/products-for-sale`. | ```PathPrefix(`/products`)``` | -| Match both `/products/shoes` and `/products/socks` with and ID like `/products/shoes/31`. | ```PathRegexp(`^/products/(shoes\|socks)/[0-9]+$`)``` | -| Match requests with a path ending in either `.jpeg`, `.jpg` or `.png`. | ```PathRegexp(`\.(jpeg\|jpg\|png)$`)``` | +| Match both `/products/shoes` and `/products/socks` with and ID like `/products/shoes/31`. | ```PathRegexp(`^/products/(shoes|socks)/[0-9]+$`)``` | +| Match requests with a path ending in either `.jpeg`, `.jpg` or `.png`. | ```PathRegexp(`\.(jpeg|jpg|png)$`)``` | | Match `/products` as well as everything under `/products`, such as `/products/shoes`, `/products/` but also `/products-for-sale`, [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```PathRegexp(`(?i)^/products`)``` | ### Query and QueryRegexp @@ -93,9 +93,9 @@ The `Query` and `QueryRegexp` matchers allow matching requests based on query pa |-----------------------------------------------------------------|:------------------------------------------------------------------------| | Match requests with a `mobile` query parameter set to `true`, such as in `/search?mobile=true`. | ```Query(`mobile`, `true`)``` | | Match requests with a query parameter `mobile` that has no value, such as in `/search?mobile`. | ```Query(`mobile`)``` | -| Match requests with a `mobile` query parameter set to either `true` or `yes`. | ```QueryRegexp(`mobile`, `^(true\|yes)$`)``` | +| Match requests with a `mobile` query parameter set to either `true` or `yes`. | ```QueryRegexp(`mobile`, `^(true|yes)$`)``` | | Match requests with a `mobile` query parameter set to any value (including the empty value). | ```QueryRegexp(`mobile`, `^.*$`)``` | -| Match query parameters [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```QueryRegexp(`mobile`, `(?i)^(true\|yes)$`)``` | +| Match query parameters [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```QueryRegexp(`mobile`, `(?i)^(true|yes)$`)``` | ### ClientIP From 6d6374d49906c6c4a53eebd586677f11ba148cc7 Mon Sep 17 00:00:00 2001 From: Emile Vauge <6207234+emilevauge@users.noreply.github.com> Date: Fri, 13 Mar 2026 10:50:04 +0100 Subject: [PATCH 2/5] Add vulnerability submission quality guidelines --- .../submitting-security-issues.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/content/contributing/submitting-security-issues.md b/docs/content/contributing/submitting-security-issues.md index 981c7fe4ce..b2d038b39c 100644 --- a/docs/content/contributing/submitting-security-issues.md +++ b/docs/content/contributing/submitting-security-issues.md @@ -21,3 +21,33 @@ We want to keep Traefik safe for everyone. If you've discovered a security vulnerability in Traefik, we appreciate your help in disclosing it to us in a responsible manner, by creating a [security advisory](https://github.com/traefik/traefik/security/advisories). + +## Submission Quality Guidelines + +We have been receiving an increasing number of low-quality vulnerability reports that are not actual security issues. +Many of these reports originate from AI/LLM tools and are submitted without any human validation or testing. +This wastes the time of our security team and delays the handling of legitimate vulnerabilities. + +Before submitting a security advisory, you **must**: + +- **Carefully test and validate** the vulnerability yourself before submitting. + You must be able to demonstrate a working proof of concept with clear reproduction steps. +- **Understand the impact** of the vulnerability and explain how it can be exploited in a realistic scenario. +- **Verify that the issue is not a false positive**. + Ensure the behavior you are reporting is actually a security concern and not expected behavior. + +### Policy on AI-Generated Reports + +Security reports that are **directly generated by AI/LLM tools without proper human validation** will be **closed immediately**. + +Indicators of unvalidated AI-generated reports include (but are not limited to): + +- No working proof of concept or reproduction steps. +- Generic or theoretical vulnerability descriptions with no evidence of actual testing. +- Misunderstanding of Traefik's architecture or threat model. +- Hallucinated code paths, configuration options, or behaviors that do not exist. + +**Contributors who repeatedly submit low-quality or unvalidated reports may have their accounts blocked.** + +We appreciate the work of security researchers who take the time to rigorously validate their findings. +Quality over quantity helps keep Traefik safe for everyone. From d7cc3342383d16e45179ec97eac682996ddd91c6 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 16 Mar 2026 11:52:57 +0100 Subject: [PATCH 3/5] Add OTel-conformant trace context attributes to access logs --- docs/content/observability/access-logs.md | 6 ++++-- pkg/middlewares/accesslog/logdata.go | 11 +++++++++-- pkg/middlewares/accesslog/logger.go | 2 ++ pkg/middlewares/accesslog/logger_test.go | 2 ++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/content/observability/access-logs.md b/docs/content/observability/access-logs.md index 4b5ce0b360..a8ccdeca4a 100644 --- a/docs/content/observability/access-logs.md +++ b/docs/content/observability/access-logs.md @@ -281,8 +281,10 @@ accessLog: | `TLSVersion` | The TLS version used by the connection (e.g. `1.2`) (if connection is TLS). | | `TLSCipher` | The TLS cipher used by the connection (e.g. `TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`) (if connection is TLS) | | `TLSClientSubject` | The string representation of the TLS client certificate's Subject (e.g. `CN=username,O=organization`) | - | `TraceId` | A consistent identifier for tracking requests across services, including upstream ones managed by Traefik, shown as a 32-hex digit string | - | `SpanId` | A unique identifier for Traefik’s root span (EntryPoint) within a request trace, formatted as a 16-hex digit string. | + | `TraceId` | (Deprecated) A consistent identifier for tracking requests across services, including upstream ones managed by Traefik, shown as a 32-hex digit string | + | `SpanId` | (Deprecated) A unique identifier for Traefik’s root span (EntryPoint) within a request trace, formatted as a 16-hex digit string. | + | `trace_id` | OTel-conformant trace identifier for tracking requests across services, including upstream ones managed by Traefik, shown as a 32-hex digit string | + | `span_id` | OTel-conformant span identifier for Traefik’s root span (EntryPoint) within a request trace, formatted as a 16-hex digit string. | ## Log Rotation diff --git a/pkg/middlewares/accesslog/logdata.go b/pkg/middlewares/accesslog/logdata.go index 451588b8a2..c9010fdd61 100644 --- a/pkg/middlewares/accesslog/logdata.go +++ b/pkg/middlewares/accesslog/logdata.go @@ -78,10 +78,15 @@ const ( // TLSClientSubject is the string representation of the TLS client certificate's Subject. TLSClientSubject = "TLSClientSubject" - // TraceID is the consistent identifier for tracking requests across services, including upstream ones managed by Traefik, shown as a 32-hex digit string. + // Deprecated: TraceID is the consistent identifier for tracking requests across services, including upstream ones managed by Traefik, shown as a 32-hex digit string. TraceID = "TraceId" - // SpanID is the unique identifier for Traefik’s root span (EntryPoint) within a request trace, formatted as a 16-hex digit string. + // Deprecated: SpanID is the unique identifier for Traefik’s root span (EntryPoint) within a request trace, formatted as a 16-hex digit string. SpanID = "SpanId" + + // OTelTraceID is the OTel-conformant log attribute for the trace identifier. + OTelTraceID = "trace_id" + // OTelSpanID is the OTel-conformant log attribute for the span identifier. + OTelSpanID = "span_id" ) // These are written out in the default case when no config is provided to specify keys of interest. @@ -126,6 +131,8 @@ func init() { allCoreKeys[TLSVersion] = struct{}{} allCoreKeys[TLSCipher] = struct{}{} allCoreKeys[TLSClientSubject] = struct{}{} + allCoreKeys[OTelTraceID] = struct{}{} + allCoreKeys[OTelSpanID] = struct{}{} } // CoreLogData holds the fields computed from the request/response. diff --git a/pkg/middlewares/accesslog/logger.go b/pkg/middlewares/accesslog/logger.go index fd6c9a686e..239d0708d0 100644 --- a/pkg/middlewares/accesslog/logger.go +++ b/pkg/middlewares/accesslog/logger.go @@ -207,6 +207,8 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http if spanContext.HasTraceID() && spanContext.HasSpanID() { logDataTable.Core[TraceID] = spanContext.TraceID().String() logDataTable.Core[SpanID] = spanContext.SpanID().String() + logDataTable.Core[OTelTraceID] = spanContext.TraceID().String() + logDataTable.Core[OTelSpanID] = spanContext.SpanID().String() } } diff --git a/pkg/middlewares/accesslog/logger_test.go b/pkg/middlewares/accesslog/logger_test.go index 2ccbffb660..78bf2ebe68 100644 --- a/pkg/middlewares/accesslog/logger_test.go +++ b/pkg/middlewares/accesslog/logger_test.go @@ -558,6 +558,8 @@ func TestLoggerJSON(t *testing.T) { "StartUTC": assertNotEmpty(), TraceID: assertString("01000000000000000000000000000000"), SpanID: assertString("0100000000000000"), + OTelTraceID: assertString("01000000000000000000000000000000"), + OTelSpanID: assertString("0100000000000000"), }, }, { From f143e15595a37887c8f6271c1172553ae7adceb8 Mon Sep 17 00:00:00 2001 From: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com> Date: Mon, 16 Mar 2026 15:58:05 +0100 Subject: [PATCH 4/5] Fix start up message format. --- cmd/traefik/traefik.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index 7d3d67a54b..b3ebaadc8f 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -97,9 +97,9 @@ func runCmd(staticConfiguration *static.Configuration) error { return fmt.Errorf("setting up logger: %w", err) } - log.Warn().Msg("Traefik can reject some encoded characters in the request path." + - "When your backend is not fully compliant with [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986)," + - "it is recommended to set these options to `false` to avoid split-view situation." + + log.Warn().Msg("Traefik can reject some encoded characters in the request path. " + + "When your backend is not fully compliant with [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986), " + + "it is recommended to set these options to `false` to avoid split-view situation. " + "Refer to the documentation for more details: https://doc.traefik.io/traefik/v3.6/migrate/v3/#encoded-characters-configuration-default-values") http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment From 69a738ccf095d17f13067a2b68f1256d1bd9975f Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 16 Mar 2026 16:42:05 +0100 Subject: [PATCH 5/5] Fix incorrect hostname matching between listener and route --- pkg/provider/kubernetes/gateway/kubernetes.go | 15 ++------------- .../kubernetes/gateway/kubernetes_test.go | 7 +++++++ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/pkg/provider/kubernetes/gateway/kubernetes.go b/pkg/provider/kubernetes/gateway/kubernetes.go index d09e2e751b..6525a75c38 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes.go +++ b/pkg/provider/kubernetes/gateway/kubernetes.go @@ -1093,24 +1093,13 @@ func findMatchingHostname(h1, h2 gatev1.Hostname) gatev1.Hostname { } trimmedH1 := strings.TrimPrefix(string(h1), "*") - // root domain doesn't match subdomain wildcard. - if trimmedH1 == string(h2) { - return "" - } if !strings.HasSuffix(string(h2), trimmedH1) { return "" } - return lessWildcards(h1, h2) -} - -func lessWildcards(h1, h2 gatev1.Hostname) gatev1.Hostname { - if strings.Count(string(h1), "*") > strings.Count(string(h2), "*") { - return h2 - } - - return h1 + // since h1 is a suffix of h2, we know h2 is the more specific host + return h2 } func allowRoute(listener gatewayListener, routeNamespace, routeKind string) bool { diff --git a/pkg/provider/kubernetes/gateway/kubernetes_test.go b/pkg/provider/kubernetes/gateway/kubernetes_test.go index 988ff73a99..f397c71155 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes_test.go +++ b/pkg/provider/kubernetes/gateway/kubernetes_test.go @@ -7630,6 +7630,13 @@ func Test_findMatchingHostnames(t *testing.T) { want: []gatev1.Hostname{"toto.foo.com", "test.foo.com"}, wantOk: true, }, + { + desc: "Matching wildcard subsubdomain with listener wildcard subdomain", + listenerHostname: ptr.To(gatev1.Hostname("*.foo.com")), + routeHostnames: []gatev1.Hostname{"*.bar.foo.com"}, + want: []gatev1.Hostname{"*.bar.foo.com"}, + wantOk: true, + }, } for _, test := range testCases {