Merge branch v2.11 into v3.0
This commit is contained in:
commit
9f145dbc28
130 changed files with 245 additions and 322 deletions
|
@ -4,7 +4,9 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/alice"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
@ -21,6 +23,8 @@ import (
|
|||
"github.com/traefik/traefik/v3/pkg/tls"
|
||||
)
|
||||
|
||||
const maxUserPriority = math.MaxInt - 1000
|
||||
|
||||
type middlewareBuilder interface {
|
||||
BuildChain(ctx context.Context, names []string) *alice.Chain
|
||||
}
|
||||
|
@ -65,8 +69,6 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t
|
|||
entryPointHandlers := make(map[string]http.Handler)
|
||||
|
||||
for entryPointName, routers := range m.getHTTPRouters(rootCtx, entryPoints, tls) {
|
||||
entryPointName := entryPointName
|
||||
|
||||
logger := log.Ctx(rootCtx).With().Str(logs.EntryPointName, entryPointName).Logger()
|
||||
ctx := logger.WithContext(rootCtx)
|
||||
|
||||
|
@ -121,6 +123,13 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, entryPointName str
|
|||
routerConfig.Priority = httpmuxer.GetRulePriority(routerConfig.Rule)
|
||||
}
|
||||
|
||||
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(err).Send()
|
||||
continue
|
||||
}
|
||||
|
||||
handler, err := m.buildRouterHandler(ctxRouter, routerName, routerConfig)
|
||||
if err != nil {
|
||||
routerConfig.AddError(err, true)
|
||||
|
|
|
@ -3,6 +3,7 @@ package router
|
|||
import (
|
||||
"context"
|
||||
"io"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
|
@ -297,7 +298,6 @@ func TestRouterManager_Get(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -574,6 +574,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{
|
||||
|
@ -640,7 +666,6 @@ func TestRuntimeConfiguration(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@ import (
|
|||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
||||
|
@ -19,6 +21,8 @@ import (
|
|||
traefiktls "github.com/traefik/traefik/v3/pkg/tls"
|
||||
)
|
||||
|
||||
const maxUserPriority = math.MaxInt - 1000
|
||||
|
||||
type middlewareBuilder interface {
|
||||
BuildChain(ctx context.Context, names []string) *tcp.Chain
|
||||
}
|
||||
|
@ -74,8 +78,6 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string) m
|
|||
|
||||
entryPointHandlers := make(map[string]*Router)
|
||||
for _, entryPointName := range entryPoints {
|
||||
entryPointName := entryPointName
|
||||
|
||||
routers := entryPointsRouters[entryPointName]
|
||||
|
||||
logger := log.Ctx(rootCtx).With().Str(logs.EntryPointName, entryPointName).Logger()
|
||||
|
@ -296,6 +298,14 @@ func (m *Manager) addTCPHandlers(ctx context.Context, configs map[string]*runtim
|
|||
routerErr := fmt.Errorf("invalid rule: %q , has HostSNI matcher, but no TLS on router", routerConfig.Rule)
|
||||
routerConfig.AddError(routerErr, true)
|
||||
logger.Error().Err(routerErr).Send()
|
||||
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().Err(routerErr).Send()
|
||||
continue
|
||||
}
|
||||
|
||||
var handler tcp.Handler
|
||||
|
|
|
@ -3,6 +3,7 @@ package tcp
|
|||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
@ -265,11 +266,45 @@ func TestRuntimeConfiguration(t *testing.T) {
|
|||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
TLS: &dynamic.RouterTCPTLSConfig{},
|
||||
},
|
||||
},
|
||||
},
|
||||
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{
|
||||
|
@ -299,8 +334,6 @@ func TestRuntimeConfiguration(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -8,8 +8,10 @@ import (
|
|||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
|
||||
"github.com/rs/zerolog/log"
|
||||
tcpmuxer "github.com/traefik/traefik/v3/pkg/muxer/tcp"
|
||||
"github.com/traefik/traefik/v3/pkg/tcp"
|
||||
|
@ -157,6 +159,12 @@ func (r *Router) ServeTCP(conn tcp.WriteCloser) {
|
|||
return
|
||||
}
|
||||
|
||||
// Handling ACME-TLS/1 challenges.
|
||||
if slices.Contains(hello.protos, tlsalpn01.ACMETLS1Protocol) {
|
||||
r.acmeTLSALPNHandler().ServeTCP(r.GetConn(conn, hello.peeked))
|
||||
return
|
||||
}
|
||||
|
||||
// For real, the handler eventually used for HTTPS is (almost) always the same:
|
||||
// it is the httpsForwarder that is used for all HTTPS connections that match
|
||||
// (which is also incidentally the same used in the last block below for 404s).
|
||||
|
@ -201,6 +209,17 @@ func (r *Router) ServeTCP(conn tcp.WriteCloser) {
|
|||
conn.Close()
|
||||
}
|
||||
|
||||
// acmeTLSALPNHandler returns a special handler to solve ACME-TLS/1 challenges.
|
||||
func (r *Router) acmeTLSALPNHandler() tcp.Handler {
|
||||
if r.httpsTLSConfig == nil {
|
||||
return &brokenTLSRouter{}
|
||||
}
|
||||
|
||||
return tcp.HandlerFunc(func(conn tcp.WriteCloser) {
|
||||
_ = tls.Server(conn, r.httpsTLSConfig).Handshake()
|
||||
})
|
||||
}
|
||||
|
||||
// AddTCPRoute defines a handler for the given rule.
|
||||
func (r *Router) AddTCPRoute(rule string, priority int, target tcp.Handler) error {
|
||||
return r.muxerTCP.AddRoute(rule, "", priority, target)
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
|
@ -22,6 +23,8 @@ import (
|
|||
"github.com/traefik/traefik/v3/pkg/server/service/tcp"
|
||||
tcp2 "github.com/traefik/traefik/v3/pkg/tcp"
|
||||
traefiktls "github.com/traefik/traefik/v3/pkg/tls"
|
||||
"github.com/traefik/traefik/v3/pkg/tls/generate"
|
||||
"github.com/traefik/traefik/v3/pkg/types"
|
||||
)
|
||||
|
||||
type applyRouter func(conf *runtime.Configuration)
|
||||
|
@ -166,11 +169,16 @@ func Test_Routing(t *testing.T) {
|
|||
dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}})
|
||||
serviceManager := tcp.NewManager(conf, dialerManager)
|
||||
|
||||
certPEM, keyPEM, err := generate.KeyPair("foo.bar", time.Time{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Creates the tlsManager and defines the TLS 1.0 and 1.2 TLSOptions.
|
||||
tlsManager := traefiktls.NewManager()
|
||||
tlsManager.UpdateConfigs(
|
||||
context.Background(),
|
||||
map[string]traefiktls.Store{},
|
||||
map[string]traefiktls.Store{
|
||||
tlsalpn01.ACMETLS1Protocol: {},
|
||||
},
|
||||
map[string]traefiktls.Options{
|
||||
"default": {
|
||||
MinVersion: "VersionTLS10",
|
||||
|
@ -185,7 +193,10 @@ func Test_Routing(t *testing.T) {
|
|||
MaxVersion: "VersionTLS12",
|
||||
},
|
||||
},
|
||||
[]*traefiktls.CertAndStores{})
|
||||
[]*traefiktls.CertAndStores{{
|
||||
Certificate: traefiktls.Certificate{CertFile: types.FileOrContent(certPEM), KeyFile: types.FileOrContent(keyPEM)},
|
||||
Stores: []string{tlsalpn01.ACMETLS1Protocol},
|
||||
}})
|
||||
|
||||
middlewaresBuilder := tcpmiddleware.NewBuilder(conf.TCPMiddlewares)
|
||||
|
||||
|
@ -209,6 +220,10 @@ func Test_Routing(t *testing.T) {
|
|||
desc: "No routers",
|
||||
routers: []applyRouter{},
|
||||
checks: []checkCase{
|
||||
{
|
||||
desc: "ACME TLS Challenge",
|
||||
checkRouter: checkACMETLS,
|
||||
},
|
||||
{
|
||||
desc: "TCP with client sending first bytes should fail",
|
||||
checkRouter: checkTCPClientFirst,
|
||||
|
@ -246,6 +261,16 @@ func Test_Routing(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "TCP TLS passthrough does not catch ACME TLS",
|
||||
routers: []applyRouter{routerTCPTLSCatchAllPassthrough},
|
||||
checks: []checkCase{
|
||||
{
|
||||
desc: "ACME TLS Challenge",
|
||||
checkRouter: checkACMETLS,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Single TCP CatchAll router",
|
||||
routers: []applyRouter{routerTCPCatchAll},
|
||||
|
@ -556,8 +581,6 @@ func Test_Routing(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -694,6 +717,21 @@ func routerTCPTLSCatchAll(conf *runtime.Configuration) {
|
|||
}
|
||||
}
|
||||
|
||||
// routerTCPTLSCatchAllPassthrough a TCP TLS CatchAll Passthrough - HostSNI(`*`) router with TLS 1.0 config.
|
||||
func routerTCPTLSCatchAllPassthrough(conf *runtime.Configuration) {
|
||||
conf.TCPRouters["tcp-tls-catchall-passthrough"] = &runtime.TCPRouterInfo{
|
||||
TCPRouter: &dynamic.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "tcp",
|
||||
Rule: "HostSNI(`*`)",
|
||||
TLS: &dynamic.RouterTCPTLSConfig{
|
||||
Options: "tls12",
|
||||
Passthrough: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// routerTCPTLS configures a TCP TLS - HostSNI(`foo.bar`) router with TLS 1.2 config.
|
||||
func routerTCPTLS(conf *runtime.Configuration) {
|
||||
conf.TCPRouters["tcp-tls"] = &runtime.TCPRouterInfo{
|
||||
|
@ -736,6 +774,33 @@ func routerHTTPS(conf *runtime.Configuration) {
|
|||
}
|
||||
}
|
||||
|
||||
// checkACMETLS simulates a ACME TLS Challenge client connection.
|
||||
// It returns an error if TLS handshake fails.
|
||||
func checkACMETLS(addr string, _ time.Duration) (err error) {
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "foo.bar",
|
||||
MinVersion: tls.VersionTLS10,
|
||||
NextProtos: []string{tlsalpn01.ACMETLS1Protocol},
|
||||
}
|
||||
conn, err := tls.Dial("tcp", addr, tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
closeErr := conn.Close()
|
||||
if closeErr != nil && err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
}()
|
||||
|
||||
if conn.ConnectionState().Version != tls.VersionTLS10 {
|
||||
return fmt.Errorf("wrong TLS version. wanted %X, got %X", tls.VersionTLS10, conn.ConnectionState().Version)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkTCPClientFirst simulates a TCP client sending first bytes first.
|
||||
// It returns an error if it doesn't receive the expected response.
|
||||
func checkTCPClientFirst(addr string, timeout time.Duration) (err error) {
|
||||
|
|
|
@ -43,8 +43,6 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string) m
|
|||
|
||||
entryPointHandlers := make(map[string]udp.Handler)
|
||||
for _, entryPointName := range entryPoints {
|
||||
entryPointName := entryPointName
|
||||
|
||||
routers := entryPointsRouters[entryPointName]
|
||||
|
||||
logger := log.Ctx(rootCtx).With().Str(logs.EntryPointName, entryPointName).Logger()
|
||||
|
|
|
@ -106,8 +106,6 @@ func TestRuntimeConfiguration(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue