CVE-2026-41174
Description
Traefik is an HTTP reverse proxy and load balancer. Prior to versions 2.11.43, 3.6.14, and 3.7.0-rc.2, there is a potential vulnerability in Traefik's Kubernetes CRD provider cross-namespace isolation enforcement. When providers.kubernetesCRD.allowCrossNamespace=false, Traefik correctly rejects direct cross-namespace middleware references from IngressRoute objects, but fails to apply the same restriction to middleware references nested inside a Chain middleware's spec.chain.middlewares[]. An actor with permission to create or update Traefik CRDs in their own namespace can exploit this to cause Traefik to resolve and apply middleware objects from another namespace, bypassing the documented isolation boundary. This issue has been patched in versions 2.11.43, 3.6.14, and 3.7.0-rc.2.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/traefik/traefik/v3Go | >= 3.7.0-ea.1, < 3.7.0-rc.2 | 3.7.0-rc.2 |
github.com/traefik/traefik/v3Go | >= 3.0.0-beta1, < 3.6.14 | 3.6.14 |
github.com/traefik/traefik/v2Go | < 2.11.43 | 2.11.43 |
github.com/traefik/traefikGo | <= 1.7.34 | — |
Affected products
5cpe:2.3:a:traefik:traefik:*:*:*:*:*:*:*:*+ 4 more
- cpe:2.3:a:traefik:traefik:*:*:*:*:*:*:*:*range: <2.11.43
- cpe:2.3:a:traefik:traefik:3.7.0:ea1:*:*:*:*:*:*
- cpe:2.3:a:traefik:traefik:3.7.0:ea2:*:*:*:*:*:*
- cpe:2.3:a:traefik:traefik:3.7.0:ea3:*:*:*:*:*:*
- cpe:2.3:a:traefik:traefik:3.7.0:rc1:*:*:*:*:*:*
Patches
1df00d82fc7f1Honor allowCrossNamespace with chain middleware CRD
5 files changed · +129 −9
docs/content/migration/v2.md+10 −0 modified@@ -785,3 +785,13 @@ The default value for this option is -1, which means there is no limit to the re However, it is strongly recommended to set this option to a suitable value to avoid performance issues such as memory exhaustion. Please check out the [HTTP](../providers/http.md#maxresponsebodysize) provider documentation for more details. + +## v2.11.43 + +### Kubernetes CRD: Chain middleware and `allowCrossNamespace` + +In `v2.11.43`, the `Chain` middleware now honors the Kubernetes CRD provider's `allowCrossNamespace` option. +Previously, a `Chain` could reference middlewares in other namespaces regardless of the `allowCrossNamespace` configuration. + +If `allowCrossNamespace` is set to `false` (the default) and a `Chain` middleware references a middleware in a different namespace from its own, +the whole `Chain` is now rejected and an error is logged.
pkg/provider/kubernetes/crd/fixtures/with_middleware_cross_namespace.yml+35 −0 modified@@ -37,6 +37,16 @@ spec: port: 80 middlewares: - name: cross-ns-stripprefix@kubernetescrd + - match: Host(`foo.com`) && PathPrefix(`/chain`) + kind: Rule + priority: 12 + services: + - name: whoami + namespace: default + port: 80 + middlewares: + - name: test-chain + - name: test-chain-cross-provider --- apiVersion: traefik.io/v1alpha1 @@ -50,6 +60,31 @@ spec: prefixes: - /stripit +--- +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: test-chain + namespace: default + +spec: + chain: + middlewares: + - name: stripprefix + namespace: cross-ns + +--- +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: test-chain-cross-provider + namespace: default + +spec: + chain: + middlewares: + - name: other-middleware@kubernetescrd + --- apiVersion: traefik.io/v1alpha1 kind: Middleware
pkg/provider/kubernetes/crd/kubernetes.go+26 −7 modified@@ -279,13 +279,19 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client) continue } + chain, err := createChainMiddleware(ctxMid, middleware.Namespace, middleware.Spec.Chain, p.AllowCrossNamespace) + if err != nil { + log.FromContext(ctxMid).Errorf("Error while reading chain middleware: %v", err) + continue + } + conf.HTTP.Middlewares[id] = &dynamic.Middleware{ AddPrefix: middleware.Spec.AddPrefix, StripPrefix: middleware.Spec.StripPrefix, StripPrefixRegex: middleware.Spec.StripPrefixRegex, ReplacePath: middleware.Spec.ReplacePath, ReplacePathRegex: middleware.Spec.ReplacePathRegex, - Chain: createChainMiddleware(ctxMid, middleware.Namespace, middleware.Spec.Chain), + Chain: chain, IPWhiteList: middleware.Spec.IPWhiteList, IPAllowList: middleware.Spec.IPAllowList, Headers: middleware.Spec.Headers, @@ -854,13 +860,20 @@ func loadAuthCredentials(secret *corev1.Secret) ([]string, error) { return credentials, nil } -func createChainMiddleware(ctx context.Context, namespace string, chain *traefikv1alpha1.Chain) *dynamic.Chain { +func createChainMiddleware(ctx context.Context, parentNamespace string, chain *traefikv1alpha1.Chain, allowCrossNamespace bool) (*dynamic.Chain, error) { if chain == nil { - return nil + return nil, nil } var mds []string for _, mi := range chain.Middlewares { + if !allowCrossNamespace && strings.HasSuffix(mi.Name, providerNamespaceSeparator+providerName) { + // Since we are not able to know if another namespace is in the name (namespace-name@kubernetescrd), + // if the provider namespace kubernetescrd is used, + // we don't allow this format to avoid cross-namespace references. + return nil, fmt.Errorf("invalid reference to middleware %s: when allowCrossNamespace is disabled @kubernetescrd provider references are disallowed", mi.Name) + } + if strings.Contains(mi.Name, providerNamespaceSeparator) { if len(mi.Namespace) > 0 { log.FromContext(ctx). @@ -870,13 +883,19 @@ func createChainMiddleware(ctx context.Context, namespace string, chain *traefik continue } - ns := mi.Namespace - if len(ns) == 0 { - ns = namespace + ns := parentNamespace + if len(mi.Namespace) > 0 { + if !isNamespaceAllowed(allowCrossNamespace, parentNamespace, mi.Namespace) { + return nil, fmt.Errorf("middleware %s/%s is not in the chain namespace %s", mi.Namespace, mi.Name, parentNamespace) + } + + ns = mi.Namespace } + mds = append(mds, makeID(ns, mi.Name)) } - return &dynamic.Chain{Middlewares: mds} + + return &dynamic.Chain{Middlewares: mds}, nil } func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options {
pkg/provider/kubernetes/crd/kubernetes_http.go+2 −2 modified@@ -166,8 +166,8 @@ func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace str if !p.AllowCrossNamespace && strings.HasSuffix(mi.Name, providerNamespaceSeparator+providerName) { // Since we are not able to know if another namespace is in the name (namespace-name@kubernetescrd), // if the provider namespace kubernetescrd is used, - // we don't allow this format to avoid cross namespace references. - return nil, fmt.Errorf("invalid reference to middleware %s: with crossnamespace disallowed, the namespace field needs to be explicitly specified", mi.Name) + // we don't allow this format to avoid cross-namespace references. + return nil, fmt.Errorf("invalid reference to middleware %s: when allowCrossNamespace is disabled @kubernetescrd provider references are disallowed", mi.Name) } if strings.Contains(name, providerNamespaceSeparator) {
pkg/provider/kubernetes/crd/kubernetes_test.go+56 −0 modified@@ -5074,6 +5074,16 @@ func TestCrossNamespace(t *testing.T) { Priority: 12, Middlewares: []string{"default-test-errorpage"}, }, + "default-test-crossnamespace-route-4932ffbbcd99474df323": { + EntryPoints: []string{"foo"}, + Service: "default-test-crossnamespace-route-4932ffbbcd99474df323", + Rule: "Host(`foo.com`) && PathPrefix(`/chain`)", + Priority: 12, + Middlewares: []string{ + "default-test-chain", + "default-test-chain-cross-provider", + }, + }, }, Middlewares: map[string]*dynamic.Middleware{ "cross-ns-stripprefix": { @@ -5097,6 +5107,19 @@ func TestCrossNamespace(t *testing.T) { PassHostHeader: pointer(true), }, }, + "default-test-crossnamespace-route-4932ffbbcd99474df323": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.1:80", + }, + { + URL: "http://10.10.0.2:80", + }, + }, + PassHostHeader: pointer(true), + }, + }, }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, @@ -5142,6 +5165,16 @@ func TestCrossNamespace(t *testing.T) { Priority: 12, Middlewares: []string{"cross-ns-stripprefix@kubernetescrd"}, }, + "default-test-crossnamespace-route-4932ffbbcd99474df323": { + EntryPoints: []string{"foo"}, + Service: "default-test-crossnamespace-route-4932ffbbcd99474df323", + Rule: "Host(`foo.com`) && PathPrefix(`/chain`)", + Priority: 12, + Middlewares: []string{ + "default-test-chain", + "default-test-chain-cross-provider", + }, + }, }, Middlewares: map[string]*dynamic.Middleware{ "cross-ns-stripprefix": { @@ -5157,6 +5190,16 @@ func TestCrossNamespace(t *testing.T) { Query: "/{status}.html", }, }, + "default-test-chain": { + Chain: &dynamic.Chain{ + Middlewares: []string{"cross-ns-stripprefix"}, + }, + }, + "default-test-chain-cross-provider": { + Chain: &dynamic.Chain{ + Middlewares: []string{"other-middleware@kubernetescrd"}, + }, + }, }, Services: map[string]*dynamic.Service{ "default-test-crossnamespace-route-6b204d94623b3df4370c": { @@ -5211,6 +5254,19 @@ func TestCrossNamespace(t *testing.T) { PassHostHeader: pointer(true), }, }, + "default-test-crossnamespace-route-4932ffbbcd99474df323": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.1:80", + }, + { + URL: "http://10.10.0.2:80", + }, + }, + PassHostHeader: pointer(true), + }, + }, }, ServersTransports: map[string]*dynamic.ServersTransport{}, },
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/traefik/traefik/commit/df00d82fc7f12e07199551832b54de6d0e55414dnvdPatchWEB
- github.com/traefik/traefik/security/advisories/GHSA-xhjw-95fp-8vgqnvdExploitPatchVendor AdvisoryWEB
- github.com/advisories/GHSA-xhjw-95fp-8vgqghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-41174ghsaADVISORY
- github.com/traefik/traefik/releases/tag/v2.11.43nvdProductRelease NotesWEB
- github.com/traefik/traefik/releases/tag/v3.6.14nvdProductRelease NotesWEB
- github.com/traefik/traefik/releases/tag/v3.7.0-rc.2nvdProductRelease NotesWEB
News mentions
0No linked articles in our index yet.