From 2bc3fa7b4bfa627e6e9c7f66a483b4c826f39e88 Mon Sep 17 00:00:00 2001 From: Baptiste Mayelle Date: Tue, 2 Apr 2024 17:04:05 +0200 Subject: [PATCH] Reserve priority range for internal routers Co-authored-by: Romain --- docs/content/migration/v2.md | 17 ++++++++++++++ docs/content/routing/routers/index.md | 16 +++++++++++++ pkg/server/router/router.go | 11 +++++++++ pkg/server/router/router_test.go | 27 +++++++++++++++++++++ pkg/server/router/tcp/manager.go | 11 +++++++++ pkg/server/router/tcp/manager_test.go | 34 +++++++++++++++++++++++++++ 6 files changed, 116 insertions(+) diff --git a/docs/content/migration/v2.md b/docs/content/migration/v2.md index 556bad2d0..0f3114fc3 100644 --- a/docs/content/migration/v2.md +++ b/docs/content/migration/v2.md @@ -560,3 +560,20 @@ To enable these ciphers, please set the option `CipherSuites` in your [TLS confi > (https://go.dev/doc/go1.22#crypto/tls) To enable TLS 1.0, please set the option `MinVersion` to `VersionTLS10` in your [TLS configuration](https://doc.traefik.io/traefik/https/tls/#cipher-suites) or set the environment variable `GODEBUG=tls10server=1`. + +## v2.11.1 + +### Maximum Router Priority Value + +Before v2.11.1, the maximum user-defined router priority value is: + +- `MaxInt32` for 32-bit platforms, +- `MaxInt64` for 64-bit platforms. + +Please check out the [go documentation](https://pkg.go.dev/math#pkg-constants) for more information. + +In v2.11.1, Traefik reserves a range of priorities for its internal routers and now, +the maximum user-defined router priority value is: + +- `(MaxInt32 - 1000)` for 32-bit platforms, +- `(MaxInt64 - 1000)` for 64-bit platforms. diff --git a/docs/content/routing/routers/index.md b/docs/content/routing/routers/index.md index 6fedaa945..418f77201 100644 --- a/docs/content/routing/routers/index.md +++ b/docs/content/routing/routers/index.md @@ -293,6 +293,14 @@ To avoid path overlap, routes are sorted, by default, in descending order using A value of `0` for the priority is ignored: `priority = 0` means that the default rules length sorting is used. +??? warning "Maximum Value" + + Traefik reserves a range of priorities for its internal routers, + the maximum user-defined router priority value is: + + - `(MaxInt32 - 1000)` for 32-bit platforms, + - `(MaxInt64 - 1000)` for 64-bit platforms. + ??? info "How default priorities are computed" ```yaml tab="File (YAML)" @@ -897,6 +905,14 @@ The priority is directly equal to the length of the rule, and so the longest len A value of `0` for the priority is ignored: `priority = 0` means that the default rules length sorting is used. +??? warning "Maximum Value" + + Traefik reserves a range of priorities for its internal routers, + the maximum user-defined router priority value is: + + - `(MaxInt32 - 1000)` for 32-bit platforms, + - `(MaxInt64 - 1000)` for 64-bit platforms. + ??? info "How default priorities are computed" ```yaml tab="File (YAML)" diff --git a/pkg/server/router/router.go b/pkg/server/router/router.go index 547066ddc..0b88b0faf 100644 --- a/pkg/server/router/router.go +++ b/pkg/server/router/router.go @@ -4,7 +4,9 @@ import ( "context" "errors" "fmt" + "math" "net/http" + "strings" "github.com/containous/alice" "github.com/traefik/traefik/v2/pkg/config/runtime" @@ -21,6 +23,8 @@ import ( "github.com/traefik/traefik/v2/pkg/tls" ) +const maxUserPriority = math.MaxInt - 1000 + type middlewareBuilder interface { BuildChain(ctx context.Context, names []string) *alice.Chain } @@ -115,6 +119,13 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string ctxRouter := log.With(provider.AddInContext(ctx, routerName), log.Str(log.RouterName, routerName)) logger := log.FromContext(ctxRouter) + if routerConfig.Priority > maxUserPriority && !strings.HasSuffix(routerName, "@internal") { + err = fmt.Errorf("the router priority %d exceeds the max user-defined priority %d", routerConfig.Priority, maxUserPriority) + routerConfig.AddError(err, true) + logger.Error(err) + continue + } + handler, err := m.buildRouterHandler(ctxRouter, routerName, routerConfig) if err != nil { routerConfig.AddError(err, true) diff --git a/pkg/server/router/router_test.go b/pkg/server/router/router_test.go index 3cce3c4c4..d5fba5af1 100644 --- a/pkg/server/router/router_test.go +++ b/pkg/server/router/router_test.go @@ -3,6 +3,7 @@ package router import ( "context" "io" + "math" "net/http" "net/http/httptest" "strings" @@ -698,6 +699,32 @@ func TestRuntimeConfiguration(t *testing.T) { }, expectedError: 2, }, + { + desc: "Router priority exceeding max user-defined priority", + serviceConfig: map[string]*dynamic.Service{ + "foo-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1", + }, + }, + }, + }, + }, + middlewareConfig: map[string]*dynamic.Middleware{}, + routerConfig: map[string]*dynamic.Router{ + "bar": { + EntryPoints: []string{"web"}, + Service: "foo-service", + Rule: "Host(`foo.bar`)", + Priority: math.MaxInt, + TLS: &dynamic.RouterTLSConfig{}, + }, + }, + tlsOptions: map[string]tls.Options{}, + expectedError: 1, + }, { desc: "Router with broken tlsOption", serviceConfig: map[string]*dynamic.Service{ diff --git a/pkg/server/router/tcp/manager.go b/pkg/server/router/tcp/manager.go index abc5d1c64..45007fbd8 100644 --- a/pkg/server/router/tcp/manager.go +++ b/pkg/server/router/tcp/manager.go @@ -5,7 +5,9 @@ import ( "crypto/tls" "errors" "fmt" + "math" "net/http" + "strings" "github.com/traefik/traefik/v2/pkg/config/runtime" "github.com/traefik/traefik/v2/pkg/log" @@ -18,6 +20,8 @@ import ( traefiktls "github.com/traefik/traefik/v2/pkg/tls" ) +const maxUserPriority = math.MaxInt - 1000 + type middlewareBuilder interface { BuildChain(ctx context.Context, names []string) *tcp.Chain } @@ -291,6 +295,13 @@ func (m *Manager) addTCPHandlers(ctx context.Context, configs map[string]*runtim continue } + if routerConfig.Priority > maxUserPriority && !strings.HasSuffix(routerName, "@internal") { + routerErr := fmt.Errorf("the router priority %d exceeds the max user-defined priority %d", routerConfig.Priority, maxUserPriority) + routerConfig.AddError(routerErr, true) + logger.Error(routerErr) + continue + } + var handler tcp.Handler if routerConfig.TLS == nil || routerConfig.TLS.Passthrough { handler, err = m.buildTCPHandler(ctxRouter, routerConfig) diff --git a/pkg/server/router/tcp/manager_test.go b/pkg/server/router/tcp/manager_test.go index 219eb90d8..2ccbcb24b 100644 --- a/pkg/server/router/tcp/manager_test.go +++ b/pkg/server/router/tcp/manager_test.go @@ -3,6 +3,7 @@ package tcp import ( "context" "crypto/tls" + "math" "net/http" "net/http/httptest" "testing" @@ -270,6 +271,39 @@ func TestRuntimeConfiguration(t *testing.T) { }, expectedError: 2, }, + { + desc: "Router with priority exceeding the max user-defined priority", + tcpServiceConfig: map[string]*runtime.TCPServiceInfo{ + "foo-service": { + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Port: "8085", + Address: "127.0.0.1:8085", + }, + { + Address: "127.0.0.1:8086", + Port: "8086", + }, + }, + }, + }, + }, + }, + tcpRouterConfig: map[string]*runtime.TCPRouterInfo{ + "bar": { + TCPRouter: &dynamic.TCPRouter{ + EntryPoints: []string{"web"}, + Service: "foo-service", + Rule: "HostSNI(`foo.bar`)", + TLS: &dynamic.RouterTCPTLSConfig{}, + Priority: math.MaxInt, + }, + }, + }, + expectedError: 1, + }, { desc: "Router with HostSNI but no TLS", tcpServiceConfig: map[string]*runtime.TCPServiceInfo{