CVE-2026-32695
Description
Traefik is an HTTP reverse proxy and load balancer. Prior to versions 3.6.11 and 3.7.0-ea.2, Traefik's Knative provider builds router rules by interpolating user-controlled values into backtick-delimited rule expressions without escaping. In live cluster validation, Knative rules[].hosts[] was exploitable for host restriction bypass (for example tenant.example.com) || Host(attacker.com), producing a router that serves attacker-controlled hosts. Knative headers[].exact also allows rule-syntax injection and proves unsafe rule construction. In multi-tenant clusters, this can route unauthorized traffic to victim services and lead to cross-tenant traffic exposure. Versions 3.6.11 and 3.7.0-ea.2 patch the issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/traefik/traefik/v3Go | < 3.6.11 | 3.6.11 |
github.com/traefik/traefik/v3Go | >= 3.7.0-ea.1, < 3.7.0-ea.2 | 3.7.0-ea.2 |
github.com/traefik/traefik/v2Go | <= 2.11.42 | — |
Affected products
2Patches
16 files changed · +119 −119
pkg/provider/kubernetes/ingress/kubernetes.go+8 −8 modified@@ -299,7 +299,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl } rt := &dynamic.Router{ - Rule: "PathPrefix(`/`)", + Rule: `PathPrefix("/")`, // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. RuleSyntax: "default", Priority: math.MinInt32, @@ -730,19 +730,19 @@ func (p *Provider) loadRouter(rule netv1.IngressRule, pa netv1.HTTPIngressPath, func buildHostRuleV2(host string) string { if strings.HasPrefix(host, "*.") { host = strings.Replace(host, "*.", "{subdomain:[a-zA-Z0-9-]+}.", 1) - return fmt.Sprintf("HostRegexp(`%s`)", host) + return fmt.Sprintf("HostRegexp(%q)", host) } - return fmt.Sprintf("Host(`%s`)", host) + return fmt.Sprintf("Host(%q)", host) } func buildHostRule(host string) string { if strings.HasPrefix(host, "*.") { host = strings.Replace(regexp.QuoteMeta(host), `\*\.`, `[a-zA-Z0-9-]+\.`, 1) - return fmt.Sprintf("HostRegexp(`^%s$`)", host) + return fmt.Sprintf("HostRegexp(%q)", fmt.Sprintf("^%s$", host)) } - return fmt.Sprintf("Host(`%s`)", host) + return fmt.Sprintf("Host(%q)", host) } func getCertificates(ctx context.Context, ingress *netv1.Ingress, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error { @@ -883,7 +883,7 @@ func buildRule(strictPrefixMatching bool, matcher string, path string) string { return buildStrictPrefixMatchingRule(path) } - return fmt.Sprintf("%s(`%s`)", matcher, path) + return fmt.Sprintf("%s(%q)", matcher, path) } // buildStrictPrefixMatchingRule is a helper function to build a path prefix rule that matches path prefix split by `/`. @@ -894,11 +894,11 @@ func buildRule(strictPrefixMatching bool, matcher string, path string) string { // Kubernetes Ingress API. func buildStrictPrefixMatchingRule(path string) string { if path == "/" { - return "PathPrefix(`/`)" + return `PathPrefix("/")` } path = strings.TrimSuffix(path, "/") - return fmt.Sprintf("(Path(`%[1]s`) || PathPrefix(`%[1]s/`))", path) + return fmt.Sprintf("(Path(%q) || PathPrefix(%q))", path, fmt.Sprintf("%s/", path)) } func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *safe.Pool, eventsChan <-chan any) chan any {
pkg/provider/kubernetes/ingress/kubernetes_test.go+64 −64 modified@@ -71,7 +71,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -104,7 +104,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "Path(`/bar`)", + Rule: `Path("/bar")`, EntryPoints: []string{"ep1", "ep2"}, Service: "testing-service1-80", Middlewares: []string{"md1", "md2"}, @@ -170,11 +170,11 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-80", }, "testing-foo": { - Rule: "PathPrefix(`/foo`)", + Rule: `PathPrefix("/foo")`, Service: "testing-service1-80", }, }, @@ -206,12 +206,12 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "testing-bar-bar-aba9a7d00e9b06a78e16": { - Rule: "HostRegexp(`^[a-zA-Z0-9-]+\\.bar$`) && PathPrefix(`/bar`)", + "testing-bar-bar-97cb2ba265f7a5df4ab9": { + Rule: `HostRegexp("^[a-zA-Z0-9-]+\\.bar$") && PathPrefix("/bar")`, Service: "testing-service1-80", }, - "testing-bar-bar-636bf36c00fedaab3d44": { - Rule: "Host(`bar`) && PathPrefix(`/bar`)", + "testing-bar-bar-605945111a3c9f84dc65": { + Rule: `Host("bar") && PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -243,12 +243,12 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "testing-foo-bar-d0b30949e54d6a7515ca": { - Rule: "PathPrefix(`/foo/bar`)", + "testing-foo-bar-930f0e8b221e60bc7ab7": { + Rule: `PathPrefix("/foo/bar")`, Service: "testing-service1-80", }, - "testing-foo-bar-dcd54bae39a6d7557f48": { - Rule: "PathPrefix(`/foo-bar`)", + "testing-foo-bar-207cc2245cb31ba18e29": { + Rule: `PathPrefix("/foo-bar")`, Service: "testing-service1-80", }, }, @@ -281,11 +281,11 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-80", }, "testing-foo": { - Rule: "PathPrefix(`/foo`)", + Rule: `PathPrefix("/foo")`, Service: "testing-service1-80", }, }, @@ -318,7 +318,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -351,7 +351,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-example-com": { - Rule: "Host(`example.com`)", + Rule: `Host("example.com")`, Service: "testing-example-com-80", }, }, @@ -381,11 +381,11 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-80", }, "testing-traefik-tchouk-foo": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/foo`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/foo")`, Service: "testing-service1-80", }, }, @@ -418,11 +418,11 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-80", }, "testing-traefik-courgette-carotte": { - Rule: "Host(`traefik.courgette`) && PathPrefix(`/carotte`)", + Rule: `Host("traefik.courgette") && PathPrefix("/carotte")`, Service: "testing-service1-80", }, }, @@ -455,11 +455,11 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-80", }, "testing-traefik-courgette-carotte": { - Rule: "Host(`traefik.courgette`) && PathPrefix(`/carotte`)", + Rule: `Host("traefik.courgette") && PathPrefix("/carotte")`, Service: "testing-service2-8082", }, }, @@ -510,7 +510,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -587,7 +587,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "default-router": { - Rule: "PathPrefix(`/`)", + Rule: `PathPrefix("/")`, RuleSyntax: "default", Service: "default-backend", Priority: math.MinInt32, @@ -622,7 +622,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -655,7 +655,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-tchouk", }, }, @@ -688,7 +688,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-tchouk", }, }, @@ -721,11 +721,11 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-tchouk", }, "testing-traefik-tchouk-foo": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/foo`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/foo")`, Service: "testing-service1-carotte", }, }, @@ -775,7 +775,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-tchouk", }, }, @@ -808,11 +808,11 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-tchouk", }, "toto-toto-traefik-tchouk-bar": { - Rule: "Host(`toto.traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("toto.traefik.tchouk") && PathPrefix("/bar")`, Service: "toto-service1-tchouk", }, }, @@ -882,7 +882,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-port-port": { - Rule: "Host(`traefik.port`) && PathPrefix(`/port`)", + Rule: `Host("traefik.port") && PathPrefix("/port")`, Service: "testing-service1-8080", }, }, @@ -912,7 +912,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-example-com": { - Rule: "Host(`example.com`)", + Rule: `Host("example.com")`, Service: "testing-example-com-80", TLS: &dynamic.RouterTLSConfig{}, }, @@ -953,7 +953,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-443", }, }, @@ -986,7 +986,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-8443", }, }, @@ -1020,7 +1020,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-8443", }, }, @@ -1053,7 +1053,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "default-router": { - Rule: "PathPrefix(`/`)", + Rule: `PathPrefix("/")`, RuleSyntax: "default", Service: "default-backend", Priority: math.MinInt32, @@ -1088,7 +1088,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -1161,7 +1161,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-foobar-com-bar": { - Rule: "HostRegexp(`^[a-zA-Z0-9-]+\\.foobar\\.com$`) && PathPrefix(`/bar`)", + Rule: `HostRegexp("^[a-zA-Z0-9-]+\\.foobar\\.com$") && PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -1194,7 +1194,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-foobar-com-bar": { - Rule: "HostRegexp(`{subdomain:[a-zA-Z0-9-]+}.foobar.com`) && PathPrefix(`/bar`)", + Rule: `HostRegexp("{subdomain:[a-zA-Z0-9-]+}.foobar.com") && PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -1226,11 +1226,11 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-foo": { - Rule: "PathPrefix(`/foo`)", + Rule: `PathPrefix("/foo")`, Service: "testing-service1-80", }, "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -1261,7 +1261,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-foo": { - Rule: "PathPrefix(`/foo`)", + Rule: `PathPrefix("/foo")`, Service: "testing-service1-80", }, }, @@ -1291,7 +1291,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -1321,7 +1321,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "Path(`/bar`)", + Rule: `Path("/bar")`, Service: "testing-service1-80", }, }, @@ -1351,7 +1351,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "Path(`/bar`)", + Rule: `Path("/bar")`, Service: "testing-service1-80", }, }, @@ -1381,7 +1381,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "Path(`/bar`)", + Rule: `Path("/bar")`, Service: "testing-service1-80", }, }, @@ -1411,7 +1411,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -1444,7 +1444,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -1477,7 +1477,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -1507,7 +1507,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -1563,7 +1563,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-foobar", }, }, @@ -1603,7 +1603,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "default-router": { - Rule: "PathPrefix(`/`)", + Rule: `PathPrefix("/")`, RuleSyntax: "default", Priority: math.MinInt32, Service: "default-backend", @@ -1635,7 +1635,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service1-80", }, }, @@ -1676,7 +1676,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-bar": { - Rule: "(Path(`/bar`) || PathPrefix(`/bar/`))", + Rule: `(Path("/bar") || PathPrefix("/bar/"))`, Service: "testing-service1-80", }, }, @@ -1750,7 +1750,7 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-8080", }, }, @@ -1780,7 +1780,7 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-example-com-bar": { - Rule: "PathPrefix(`/bar`)", + Rule: `PathPrefix("/bar")`, Service: "testing-service-bar-8080", }, }, @@ -1811,7 +1811,7 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-example-com-foo": { - Rule: "PathPrefix(`/foo`)", + Rule: `PathPrefix("/foo")`, Service: "testing-service-foo-8080", }, }, @@ -1864,7 +1864,7 @@ func TestLoadConfigurationFromIngressesWithNativeLB(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-8080", }, }, @@ -1914,7 +1914,7 @@ func TestLoadConfigurationFromIngressesWithNodePortLB(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-8080", }, }, @@ -2152,7 +2152,7 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "testing-service1-8080", }, }, @@ -2180,7 +2180,7 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "default-global-native-lb-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "default-service1-8080", }, }, @@ -2208,7 +2208,7 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ "default-global-native-lb-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Rule: `Host("traefik.tchouk") && PathPrefix("/bar")`, Service: "default-native-disabled-svc-web", }, },
pkg/provider/kubernetes/ingress-nginx/kubernetes.go+11 −11 modified@@ -232,15 +232,15 @@ func (p *Provider) loadConfiguration(ctx context.Context) *dynamic.Configuration // Add the default backend service router to the configuration. conf.HTTP.Routers[defaultBackendName] = &dynamic.Router{ - Rule: "PathPrefix(`/`)", + Rule: `PathPrefix("/")`, // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. RuleSyntax: "default", Priority: math.MinInt32, Service: defaultBackendName, } conf.HTTP.Routers[defaultBackendTLSName] = &dynamic.Router{ - Rule: "PathPrefix(`/`)", + Rule: `PathPrefix("/")`, // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. RuleSyntax: "default", Priority: math.MinInt32, @@ -309,7 +309,7 @@ func (p *Provider) loadConfiguration(ctx context.Context) *dynamic.Configuration if defaultBackendService != nil && len(ingress.Spec.Rules) == 0 { rt := &dynamic.Router{ - Rule: "PathPrefix(`/`)", + Rule: `PathPrefix("/")`, // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. RuleSyntax: "default", Priority: math.MinInt32, @@ -323,7 +323,7 @@ func (p *Provider) loadConfiguration(ctx context.Context) *dynamic.Configuration conf.HTTP.Routers[defaultBackendName] = rt rtTLS := &dynamic.Router{ - Rule: "PathPrefix(`/`)", + Rule: `PathPrefix("/")`, // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. RuleSyntax: "default", Priority: math.MinInt32, @@ -386,7 +386,7 @@ func (p *Provider) loadConfiguration(ctx context.Context) *dynamic.Configuration routerKey := strings.TrimPrefix(provider.Normalize(ingress.Namespace+"-"+ingress.Name+"-"+rule.Host), "-") conf.TCP.Routers[routerKey] = &dynamic.TCPRouter{ - Rule: fmt.Sprintf("HostSNI(`%s`)", rule.Host), + Rule: fmt.Sprintf("HostSNI(%q)", rule.Host), // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. RuleSyntax: "default", Service: serviceName, @@ -1063,10 +1063,10 @@ func buildRule(host string, pa netv1.HTTPIngressPath, config ingressConfig) stri switch pathType { case netv1.PathTypeExact: - rules = append(rules, fmt.Sprintf("Path(`%s`)", pa.Path)) + rules = append(rules, fmt.Sprintf("Path(%q)", pa.Path)) case netv1.PathTypePrefix: if ptr.Deref(config.UseRegex, false) { - rules = append(rules, fmt.Sprintf("PathRegexp(`^%s`)", pa.Path)) + rules = append(rules, fmt.Sprintf("PathRegexp(%q)", fmt.Sprintf("^%s", pa.Path))) } else { rules = append(rules, buildPrefixRule(pa.Path)) } @@ -1079,10 +1079,10 @@ func buildRule(host string, pa netv1.HTTPIngressPath, config ingressConfig) stri func buildHostRule(host string) string { if strings.HasPrefix(host, "*.") { host = strings.Replace(regexp.QuoteMeta(host), `\*\.`, `[a-zA-Z0-9-]+\.`, 1) - return fmt.Sprintf("HostRegexp(`^%s$`)", host) + return fmt.Sprintf("HostRegexp(%q)", fmt.Sprintf("^%s$", host)) } - return fmt.Sprintf("Host(`%s`)", host) + return fmt.Sprintf("Host(%q)", host) } // buildPrefixRule is a helper function to build a path prefix rule that matches path prefix split by `/`. @@ -1093,11 +1093,11 @@ func buildHostRule(host string) string { // Kubernetes Ingress API. func buildPrefixRule(path string) string { if path == "/" { - return "PathPrefix(`/`)" + return `PathPrefix("/")` } path = strings.TrimSuffix(path, "/") - return fmt.Sprintf("(Path(`%[1]s`) || PathPrefix(`%[1]s/`))", path) + return fmt.Sprintf("(Path(%q) || PathPrefix(%q))", path, fmt.Sprintf("%s/", path)) } func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *safe.Pool, eventsChan <-chan any) chan any {
pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go+17 −17 modified@@ -62,14 +62,14 @@ func TestLoadIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ "default-ingress-with-no-annotation-rule-0-path-0": { - Rule: "Host(`whoami.localhost`) && PathPrefix(`/`)", + Rule: `Host("whoami.localhost") && PathPrefix("/")`, RuleSyntax: "default", TLS: &dynamic.RouterTLSConfig{}, Service: "default-ingress-with-no-annotation-whoami-80", }, "default-ingress-with-no-annotation-rule-0-path-0-http": { EntryPoints: []string{"web"}, - Rule: "Host(`whoami.localhost`) && PathPrefix(`/`)", + Rule: `Host("whoami.localhost") && PathPrefix("/")`, RuleSyntax: "default", Middlewares: []string{"default-ingress-with-no-annotation-rule-0-path-0-redirect-scheme"}, Service: "noop@internal", @@ -131,7 +131,7 @@ func TestLoadIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ "default-ingress-with-basicauth-rule-0-path-0": { - Rule: "Host(`whoami.localhost`) && Path(`/basicauth`)", + Rule: `Host("whoami.localhost") && Path("/basicauth")`, RuleSyntax: "default", Middlewares: []string{"default-ingress-with-basicauth-rule-0-path-0-basic-auth"}, Service: "default-ingress-with-basicauth-whoami-80", @@ -186,7 +186,7 @@ func TestLoadIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ "default-ingress-with-forwardauth-rule-0-path-0": { - Rule: "Host(`whoami.localhost`) && Path(`/forwardauth`)", + Rule: `Host("whoami.localhost") && Path("/forwardauth")`, RuleSyntax: "default", Middlewares: []string{"default-ingress-with-forwardauth-rule-0-path-0-forward-auth"}, Service: "default-ingress-with-forwardauth-whoami-80", @@ -240,32 +240,32 @@ func TestLoadIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ "default-ingress-with-ssl-redirect-rule-0-path-0": { - Rule: "Host(`sslredirect.localhost`) && Path(`/`)", + Rule: `Host("sslredirect.localhost") && Path("/")`, RuleSyntax: "default", TLS: &dynamic.RouterTLSConfig{}, Service: "default-ingress-with-ssl-redirect-whoami-80", }, "default-ingress-with-ssl-redirect-rule-0-path-0-http": { EntryPoints: []string{"web"}, - Rule: "Host(`sslredirect.localhost`) && Path(`/`)", + Rule: `Host("sslredirect.localhost") && Path("/")`, RuleSyntax: "default", Middlewares: []string{"default-ingress-with-ssl-redirect-rule-0-path-0-redirect-scheme"}, Service: "noop@internal", }, "default-ingress-without-ssl-redirect-rule-0-path-0-http": { EntryPoints: []string{"web"}, - Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)", + Rule: `Host("withoutsslredirect.localhost") && Path("/")`, RuleSyntax: "default", Service: "default-ingress-without-ssl-redirect-whoami-80", }, "default-ingress-without-ssl-redirect-rule-0-path-0": { - Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)", + Rule: `Host("withoutsslredirect.localhost") && Path("/")`, RuleSyntax: "default", TLS: &dynamic.RouterTLSConfig{}, Service: "default-ingress-without-ssl-redirect-whoami-80", }, "default-ingress-with-force-ssl-redirect-rule-0-path-0": { - Rule: "Host(`forcesslredirect.localhost`) && Path(`/`)", + Rule: `Host("forcesslredirect.localhost") && Path("/")`, RuleSyntax: "default", Middlewares: []string{"default-ingress-with-force-ssl-redirect-rule-0-path-0-redirect-scheme"}, Service: "default-ingress-with-force-ssl-redirect-whoami-80", @@ -364,7 +364,7 @@ func TestLoadIngresses(t *testing.T) { TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ "default-ingress-with-ssl-passthrough-passthrough-whoami-localhost": { - Rule: "HostSNI(`passthrough.whoami.localhost`)", + Rule: `HostSNI("passthrough.whoami.localhost")`, RuleSyntax: "default", TLS: &dynamic.RouterTCPTLSConfig{ Passthrough: true, @@ -412,7 +412,7 @@ func TestLoadIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ "default-ingress-with-sticky-rule-0-path-0": { - Rule: "Host(`sticky.localhost`) && Path(`/`)", + Rule: `Host("sticky.localhost") && Path("/")`, RuleSyntax: "default", Service: "default-ingress-with-sticky-whoami-80", }, @@ -469,7 +469,7 @@ func TestLoadIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ "default-ingress-with-proxy-ssl-rule-0-path-0": { - Rule: "Host(`proxy-ssl.localhost`) && Path(`/`)", + Rule: `Host("proxy-ssl.localhost") && Path("/")`, RuleSyntax: "default", Service: "default-ingress-with-proxy-ssl-whoami-tls-443", }, @@ -521,7 +521,7 @@ func TestLoadIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ "default-ingress-with-cors-rule-0-path-0": { - Rule: "Host(`cors.localhost`) && Path(`/`)", + Rule: `Host("cors.localhost") && Path("/")`, RuleSyntax: "default", Middlewares: []string{"default-ingress-with-cors-rule-0-path-0-cors"}, Service: "default-ingress-with-cors-whoami-80", @@ -578,7 +578,7 @@ func TestLoadIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ "default-ingress-with-service-upstream-rule-0-path-0": { - Rule: "Host(`service-upstream.localhost`) && Path(`/`)", + Rule: `Host("service-upstream.localhost") && Path("/")`, RuleSyntax: "default", Service: "default-ingress-with-service-upstream-whoami-80", }, @@ -620,7 +620,7 @@ func TestLoadIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ "default-ingress-with-use-regex-rule-0-path-0": { - Rule: "Host(`use-regex.localhost`) && PathRegexp(`^/test(.*)`)", + Rule: `Host("use-regex.localhost") && PathRegexp("^/test(.*)")`, RuleSyntax: "default", Service: "default-ingress-with-use-regex-whoami-80", }, @@ -665,13 +665,13 @@ func TestLoadIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ "default-backend": { - Rule: "PathPrefix(`/`)", + Rule: `PathPrefix("/")`, RuleSyntax: "default", Priority: math.MinInt32, Service: "default-backend", }, "default-backend-tls": { - Rule: "PathPrefix(`/`)", + Rule: `PathPrefix("/")`, RuleSyntax: "default", Priority: math.MinInt32, TLS: &dynamic.RouterTLSConfig{},
pkg/provider/kubernetes/knative/kubernetes.go+3 −3 modified@@ -452,7 +452,7 @@ func buildRule(hosts []string, headers map[string]knativenetworkingv1alpha1.Head if len(hosts) > 0 { var hostRules []string for _, host := range hosts { - hostRules = append(hostRules, fmt.Sprintf("Host(`%v`)", host)) + hostRules = append(hostRules, fmt.Sprintf("Host(%q)", host)) } operands = append(operands, fmt.Sprintf("(%s)", strings.Join(hostRules, " || "))) } @@ -463,13 +463,13 @@ func buildRule(hosts []string, headers map[string]knativenetworkingv1alpha1.Head var headerRules []string for _, key := range headerKeys { - headerRules = append(headerRules, fmt.Sprintf("Header(`%s`,`%s`)", key, headers[key].Exact)) + headerRules = append(headerRules, fmt.Sprintf("Header(%q,%q)", key, headers[key].Exact)) } operands = append(operands, fmt.Sprintf("(%s)", strings.Join(headerRules, " && "))) } if len(path) > 0 { - operands = append(operands, fmt.Sprintf("PathPrefix(`%s`)", path)) + operands = append(operands, fmt.Sprintf("PathPrefix(%q)", path)) } return strings.Join(operands, " && ")
pkg/provider/kubernetes/knative/kubernetes_test.go+16 −16 modified@@ -55,7 +55,7 @@ func Test_loadConfiguration(t *testing.T) { "default-helloworld-go-rule-0-path-0": { EntryPoints: []string{"priv-http", "priv-https"}, Service: "default-helloworld-go-rule-0-path-0-wrr", - Rule: "(Host(`helloworld-go.default`) || Host(`helloworld-go.default.svc`) || Host(`helloworld-go.default.svc.cluster.local`))", + Rule: `(Host("helloworld-go.default") || Host("helloworld-go.default.svc") || Host("helloworld-go.default.svc.cluster.local"))`, Middlewares: []string{}, }, }, @@ -125,7 +125,7 @@ func Test_loadConfiguration(t *testing.T) { "default-helloworld-go-rule-0-path-0": { EntryPoints: []string{"http", "https"}, Service: "default-helloworld-go-rule-0-path-0-wrr", - Rule: "(Host(`helloworld-go.default`) || Host(`helloworld-go.default.svc`) || Host(`helloworld-go.default.svc.cluster.local`))", + Rule: `(Host("helloworld-go.default") || Host("helloworld-go.default.svc") || Host("helloworld-go.default.svc.cluster.local"))`, Middlewares: []string{}, }, }, @@ -195,13 +195,13 @@ func Test_loadConfiguration(t *testing.T) { "default-helloworld-go-rule-0-path-0": { EntryPoints: []string{"http", "https"}, Service: "default-helloworld-go-rule-0-path-0-wrr", - Rule: "(Host(`helloworld-go.default`) || Host(`helloworld-go.default.svc`) || Host(`helloworld-go.default.svc.cluster.local`))", + Rule: `(Host("helloworld-go.default") || Host("helloworld-go.default.svc") || Host("helloworld-go.default.svc.cluster.local"))`, Middlewares: []string{}, }, "default-helloworld-go-rule-0-path-0-tls": { EntryPoints: []string{"http", "https"}, Service: "default-helloworld-go-rule-0-path-0-wrr", - Rule: "(Host(`helloworld-go.default`) || Host(`helloworld-go.default.svc`) || Host(`helloworld-go.default.svc.cluster.local`))", + Rule: `(Host("helloworld-go.default") || Host("helloworld-go.default.svc") || Host("helloworld-go.default.svc.cluster.local"))`, Middlewares: []string{}, TLS: &dynamic.RouterTLSConfig{}, }, @@ -307,20 +307,20 @@ func Test_buildRule(t *testing.T) { { desc: "single host, no headers, no path", hosts: []string{"example.com"}, - want: "(Host(`example.com`))", + want: `(Host("example.com"))`, }, { desc: "multiple hosts, no headers, no path", hosts: []string{"example.com", "foo.com"}, - want: "(Host(`example.com`) || Host(`foo.com`))", + want: `(Host("example.com") || Host("foo.com"))`, }, { desc: "single host, single header, no path", hosts: []string{"example.com"}, headers: map[string]knativenetworkingv1alpha1.HeaderMatch{ "X-Header": {Exact: "value"}, }, - want: "(Host(`example.com`)) && (Header(`X-Header`,`value`))", + want: `(Host("example.com")) && (Header("X-Header","value"))`, }, { desc: "single host, multiple headers, no path", @@ -329,7 +329,7 @@ func Test_buildRule(t *testing.T) { "X-Header": {Exact: "value"}, "X-Header2": {Exact: "value2"}, }, - want: "(Host(`example.com`)) && (Header(`X-Header`,`value`) && Header(`X-Header2`,`value2`))", + want: `(Host("example.com")) && (Header("X-Header","value") && Header("X-Header2","value2"))`, }, { desc: "single host, multiple headers, with path", @@ -339,13 +339,13 @@ func Test_buildRule(t *testing.T) { "X-Header2": {Exact: "value2"}, }, path: "/foo", - want: "(Host(`example.com`)) && (Header(`X-Header`,`value`) && Header(`X-Header2`,`value2`)) && PathPrefix(`/foo`)", + want: `(Host("example.com")) && (Header("X-Header","value") && Header("X-Header2","value2")) && PathPrefix("/foo")`, }, { desc: "single host, no headers, with path", hosts: []string{"example.com"}, path: "/foo", - want: "(Host(`example.com`)) && PathPrefix(`/foo`)", + want: `(Host("example.com")) && PathPrefix("/foo")`, }, } @@ -370,7 +370,7 @@ func Test_mergeHTTPConfigs(t *testing.T) { configs: []*dynamic.HTTPConfiguration{ { Routers: map[string]*dynamic.Router{ - "router1": {Rule: "Host(`example.com`)"}, + "router1": {Rule: `Host("example.com")`}, }, Middlewares: map[string]*dynamic.Middleware{ "middleware1": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}}, @@ -387,7 +387,7 @@ func Test_mergeHTTPConfigs(t *testing.T) { }, want: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "router1": {Rule: "Host(`example.com`)"}, + "router1": {Rule: `Host("example.com")`}, }, Middlewares: map[string]*dynamic.Middleware{ "middleware1": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}}, @@ -402,7 +402,7 @@ func Test_mergeHTTPConfigs(t *testing.T) { configs: []*dynamic.HTTPConfiguration{ { Routers: map[string]*dynamic.Router{ - "router1": {Rule: "Host(`example.com`)"}, + "router1": {Rule: `Host("example.com")`}, }, Middlewares: map[string]*dynamic.Middleware{ "middleware1": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}}, @@ -413,7 +413,7 @@ func Test_mergeHTTPConfigs(t *testing.T) { }, { Routers: map[string]*dynamic.Router{ - "router2": {Rule: "PathPrefix(`/test`)"}, + "router2": {Rule: `PathPrefix("/test")`}, }, Middlewares: map[string]*dynamic.Middleware{ "middleware2": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}}, @@ -425,8 +425,8 @@ func Test_mergeHTTPConfigs(t *testing.T) { }, want: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "router1": {Rule: "Host(`example.com`)"}, - "router2": {Rule: "PathPrefix(`/test`)"}, + "router1": {Rule: `Host("example.com")`}, + "router2": {Rule: `PathPrefix("/test")`}, }, Middlewares: map[string]*dynamic.Middleware{ "middleware1": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}},
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
6- github.com/traefik/traefik/security/advisories/GHSA-67jx-r9pv-98rjnvdExploitPatchVendor AdvisoryWEB
- github.com/advisories/GHSA-67jx-r9pv-98rjghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-32695ghsaADVISORY
- github.com/traefik/traefik/commit/11d251415a6fd935025df5a9dda898e17e3097b2ghsaWEB
- github.com/traefik/traefik/releases/tag/v3.6.11nvdProductRelease NotesWEB
- github.com/traefik/traefik/releases/tag/v3.7.0-ea.2nvdProductRelease NotesWEB
News mentions
0No linked articles in our index yet.