mirror of
https://github.com/traefik/traefik.git
synced 2026-05-28 04:35:59 -04:00
Merge branch v3.6 into v3.7
Some checks are pending
CodeQL / Analyze (push) Waiting to run
Build and Publish Documentation / Doc Process (push) Waiting to run
Build experimental image on branch / build-webui (push) Waiting to run
Build experimental image on branch / Build experimental image on branch (push) Waiting to run
Some checks are pending
CodeQL / Analyze (push) Waiting to run
Build and Publish Documentation / Doc Process (push) Waiting to run
Build experimental image on branch / build-webui (push) Waiting to run
Build experimental image on branch / Build experimental image on branch (push) Waiting to run
This commit is contained in:
commit
d1a6841275
8 changed files with 64 additions and 25 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ The `Header` and `HeaderRegexp` matchers allow matching requests that contain sp
|
|||
| Behavior | Rule |
|
||||
|-----------------------------------------------------------------|:------------------------------------------------------------------------|
|
||||
| <a id="opt-Match-requests-with-a-Content-Type-header-set-to-applicationyaml" href="#opt-Match-requests-with-a-Content-Type-header-set-to-applicationyaml" title="#opt-Match-requests-with-a-Content-Type-header-set-to-applicationyaml">Match requests with a `Content-Type` header set to `application/yaml`.</a> | ```Header(`Content-Type`, `application/yaml`)``` |
|
||||
| <a id="opt-Match-requests-with-a-Content-Type-header-set-to-either-applicationjson-or-applicationyaml" href="#opt-Match-requests-with-a-Content-Type-header-set-to-either-applicationjson-or-applicationyaml" title="#opt-Match-requests-with-a-Content-Type-header-set-to-either-applicationjson-or-applicationyaml">Match requests with a `Content-Type` header set to either `application/json` or `application/yaml`.</a> | ```HeaderRegexp(`Content-Type`, `^application/(json\|yaml)$`)``` |
|
||||
| <a id="opt-Match-headers-case-insensitively" href="#opt-Match-headers-case-insensitively" title="#opt-Match-headers-case-insensitively">Match headers [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity).</a> | ```HeaderRegexp(`Content-Type`, `(?i)^application/(json\|yaml)$`)``` |
|
||||
| <a id="opt-Match-requests-with-a-Content-Type-header-set-to-either-applicationjson-or-applicationyaml" href="#opt-Match-requests-with-a-Content-Type-header-set-to-either-applicationjson-or-applicationyaml" title="#opt-Match-requests-with-a-Content-Type-header-set-to-either-applicationjson-or-applicationyaml">Match requests with a `Content-Type` header set to either `application/json` or `application/yaml`.</a> | ```HeaderRegexp(`Content-Type`, `^application/(json|yaml)$`)``` |
|
||||
| <a id="opt-Match-headers-case-insensitively" href="#opt-Match-headers-case-insensitively" title="#opt-Match-headers-case-insensitively">Match headers [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity).</a> | ```HeaderRegexp(`Content-Type`, `(?i)^application/(json|yaml)$`)``` |
|
||||
|
||||
### Host and HostRegexp
|
||||
|
||||
|
|
@ -58,8 +58,8 @@ These matchers will match the request's host in lowercase.
|
|||
|-----------------------------------------------------------------|:------------------------------------------------------------------------|
|
||||
| <a id="opt-Match-requests-with-Host-set-to-example-com" href="#opt-Match-requests-with-Host-set-to-example-com" title="#opt-Match-requests-with-Host-set-to-example-com">Match requests with `Host` set to `example.com`.</a> | ```Host(`example.com`)``` |
|
||||
| <a id="opt-Match-requests-sent-to-any-subdomain-of-example-com" href="#opt-Match-requests-sent-to-any-subdomain-of-example-com" title="#opt-Match-requests-sent-to-any-subdomain-of-example-com">Match requests sent to any subdomain of `example.com`.</a> | ```HostRegexp(`^.+\.example\.com$`)``` |
|
||||
| <a id="opt-Match-requests-with-Host-set-to-either-example-com-or-example-org" href="#opt-Match-requests-with-Host-set-to-either-example-com-or-example-org" title="#opt-Match-requests-with-Host-set-to-either-example-com-or-example-org">Match requests with `Host` set to either `example.com` or `example.org`.</a> | ```HostRegexp(`^example\.(com\|org)$`)``` |
|
||||
| <a id="opt-Match-Host-case-insensitively" href="#opt-Match-Host-case-insensitively" title="#opt-Match-Host-case-insensitively">Match `Host` [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity).</a> | ```HostRegexp(`(?i)^example\.(com\|org)$`)``` |
|
||||
| <a id="opt-Match-requests-with-Host-set-to-either-example-com-or-example-org" href="#opt-Match-requests-with-Host-set-to-either-example-com-or-example-org" title="#opt-Match-requests-with-Host-set-to-either-example-com-or-example-org">Match requests with `Host` set to either `example.com` or `example.org`.</a> | ```HostRegexp(`^example\.(com|org)$`)``` |
|
||||
| <a id="opt-Match-Host-case-insensitively" href="#opt-Match-Host-case-insensitively" title="#opt-Match-Host-case-insensitively">Match `Host` [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity).</a> | ```HostRegexp(`(?i)^example\.(com|org)$`)``` |
|
||||
|
||||
### Method
|
||||
|
||||
|
|
@ -81,8 +81,8 @@ Path are always starting with a `/`, except for `PathRegexp`.
|
|||
|-----------------------------------------------------------------|:------------------------------------------------------------------------|
|
||||
| <a id="opt-Match-products-but-neither-productsshoes-nor-products" href="#opt-Match-products-but-neither-productsshoes-nor-products" title="#opt-Match-products-but-neither-productsshoes-nor-products">Match `/products` but neither `/products/shoes` nor `/products/`.</a> | ```Path(`/products`)``` |
|
||||
| <a id="opt-Match-products-as-well-as-everything-under-products-such-as-productsshoes-products-but-also-products-for-sale" href="#opt-Match-products-as-well-as-everything-under-products-such-as-productsshoes-products-but-also-products-for-sale" title="#opt-Match-products-as-well-as-everything-under-products-such-as-productsshoes-products-but-also-products-for-sale">Match `/products` as well as everything under `/products`, such as `/products/shoes`, `/products/` but also `/products-for-sale`.</a> | ```PathPrefix(`/products`)``` |
|
||||
| <a id="opt-Match-both-productsshoes-and-productssocks-with-and-ID-like-productsshoes31" href="#opt-Match-both-productsshoes-and-productssocks-with-and-ID-like-productsshoes31" title="#opt-Match-both-productsshoes-and-productssocks-with-and-ID-like-productsshoes31">Match both `/products/shoes` and `/products/socks` with and ID like `/products/shoes/31`.</a> | ```PathRegexp(`^/products/(shoes\|socks)/[0-9]+$`)``` |
|
||||
| <a id="opt-Match-requests-with-a-path-ending-in-either-jpeg-jpg-or-png" href="#opt-Match-requests-with-a-path-ending-in-either-jpeg-jpg-or-png" title="#opt-Match-requests-with-a-path-ending-in-either-jpeg-jpg-or-png">Match requests with a path ending in either `.jpeg`, `.jpg` or `.png`.</a> | ```PathRegexp(`\.(jpeg\|jpg\|png)$`)``` |
|
||||
| <a id="opt-Match-both-productsshoes-and-productssocks-with-and-ID-like-productsshoes31" href="#opt-Match-both-productsshoes-and-productssocks-with-and-ID-like-productsshoes31" title="#opt-Match-both-productsshoes-and-productssocks-with-and-ID-like-productsshoes31">Match both `/products/shoes` and `/products/socks` with and ID like `/products/shoes/31`.</a> | ```PathRegexp(`^/products/(shoes|socks)/[0-9]+$`)``` |
|
||||
| <a id="opt-Match-requests-with-a-path-ending-in-either-jpeg-jpg-or-png" href="#opt-Match-requests-with-a-path-ending-in-either-jpeg-jpg-or-png" title="#opt-Match-requests-with-a-path-ending-in-either-jpeg-jpg-or-png">Match requests with a path ending in either `.jpeg`, `.jpg` or `.png`.</a> | ```PathRegexp(`\.(jpeg|jpg|png)$`)``` |
|
||||
| <a id="opt-Match-products-as-well-as-everything-under-products-such-as-productsshoes-products-but-also-products-for-sale-case-insensitively" href="#opt-Match-products-as-well-as-everything-under-products-such-as-productsshoes-products-but-also-products-for-sale-case-insensitively" title="#opt-Match-products-as-well-as-everything-under-products-such-as-productsshoes-products-but-also-products-for-sale-case-insensitively">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).</a> | ```PathRegexp(`(?i)^/products`)``` |
|
||||
|
||||
### Query and QueryRegexp
|
||||
|
|
@ -93,9 +93,9 @@ The `Query` and `QueryRegexp` matchers allow matching requests based on query pa
|
|||
|-----------------------------------------------------------------|:------------------------------------------------------------------------|
|
||||
| <a id="opt-Match-requests-with-a-mobile-query-parameter-set-to-true-such-as-in-searchmobiletrue" href="#opt-Match-requests-with-a-mobile-query-parameter-set-to-true-such-as-in-searchmobiletrue" title="#opt-Match-requests-with-a-mobile-query-parameter-set-to-true-such-as-in-searchmobiletrue">Match requests with a `mobile` query parameter set to `true`, such as in `/search?mobile=true`.</a> | ```Query(`mobile`, `true`)``` |
|
||||
| <a id="opt-Match-requests-with-a-query-parameter-mobile-that-has-no-value-such-as-in-searchmobile" href="#opt-Match-requests-with-a-query-parameter-mobile-that-has-no-value-such-as-in-searchmobile" title="#opt-Match-requests-with-a-query-parameter-mobile-that-has-no-value-such-as-in-searchmobile">Match requests with a query parameter `mobile` that has no value, such as in `/search?mobile`.</a> | ```Query(`mobile`)``` |
|
||||
| <a id="opt-Match-requests-with-a-mobile-query-parameter-set-to-either-true-or-yes" href="#opt-Match-requests-with-a-mobile-query-parameter-set-to-either-true-or-yes" title="#opt-Match-requests-with-a-mobile-query-parameter-set-to-either-true-or-yes">Match requests with a `mobile` query parameter set to either `true` or `yes`.</a> | ```QueryRegexp(`mobile`, `^(true\|yes)$`)``` |
|
||||
| <a id="opt-Match-requests-with-a-mobile-query-parameter-set-to-either-true-or-yes" href="#opt-Match-requests-with-a-mobile-query-parameter-set-to-either-true-or-yes" title="#opt-Match-requests-with-a-mobile-query-parameter-set-to-either-true-or-yes">Match requests with a `mobile` query parameter set to either `true` or `yes`.</a> | ```QueryRegexp(`mobile`, `^(true|yes)$`)``` |
|
||||
| <a id="opt-Match-requests-with-a-mobile-query-parameter-set-to-any-value-including-the-empty-value" href="#opt-Match-requests-with-a-mobile-query-parameter-set-to-any-value-including-the-empty-value" title="#opt-Match-requests-with-a-mobile-query-parameter-set-to-any-value-including-the-empty-value">Match requests with a `mobile` query parameter set to any value (including the empty value).</a> | ```QueryRegexp(`mobile`, `^.*$`)``` |
|
||||
| <a id="opt-Match-query-parameters-case-insensitively" href="#opt-Match-query-parameters-case-insensitively" title="#opt-Match-query-parameters-case-insensitively">Match query parameters [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity).</a> | ```QueryRegexp(`mobile`, `(?i)^(true\|yes)$`)``` |
|
||||
| <a id="opt-Match-query-parameters-case-insensitively" href="#opt-Match-query-parameters-case-insensitively" title="#opt-Match-query-parameters-case-insensitively">Match query parameters [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity).</a> | ```QueryRegexp(`mobile`, `(?i)^(true|yes)$`)``` |
|
||||
|
||||
### ClientIP
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -209,6 +209,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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -622,6 +622,8 @@ func TestLoggerJSON(t *testing.T) {
|
|||
"StartUTC": assertNotEmpty(),
|
||||
TraceID: assertString("01000000000000000000000000000000"),
|
||||
SpanID: assertString("0100000000000000"),
|
||||
OTelTraceID: assertString("01000000000000000000000000000000"),
|
||||
OTelSpanID: assertString("0100000000000000"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -7702,6 +7702,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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue