2019-03-14 08:30:04 +00:00
package tcp
import (
"context"
2019-07-03 17:22:05 +00:00
"crypto/tls"
2019-07-19 14:42:04 +00:00
"errors"
2019-05-16 08:58:06 +00:00
"fmt"
2019-03-14 08:30:04 +00:00
"net/http"
2020-09-16 13:46:04 +00:00
"github.com/traefik/traefik/v2/pkg/config/runtime"
"github.com/traefik/traefik/v2/pkg/log"
2022-02-14 16:18:08 +00:00
"github.com/traefik/traefik/v2/pkg/middlewares/snicheck"
2020-09-16 13:46:04 +00:00
"github.com/traefik/traefik/v2/pkg/rules"
"github.com/traefik/traefik/v2/pkg/server/provider"
tcpservice "github.com/traefik/traefik/v2/pkg/server/service/tcp"
"github.com/traefik/traefik/v2/pkg/tcp"
traefiktls "github.com/traefik/traefik/v2/pkg/tls"
2019-03-14 08:30:04 +00:00
)
2021-06-11 13:30:05 +00:00
type middlewareBuilder interface {
BuildChain ( ctx context . Context , names [ ] string ) * tcp . Chain
}
2020-05-11 10:06:07 +00:00
// NewManager Creates a new Manager.
2019-07-15 15:04:04 +00:00
func NewManager ( conf * runtime . Configuration ,
2019-03-14 08:30:04 +00:00
serviceManager * tcpservice . Manager ,
2021-06-11 13:30:05 +00:00
middlewaresBuilder middlewareBuilder ,
2019-03-14 08:30:04 +00:00
httpHandlers map [ string ] http . Handler ,
httpsHandlers map [ string ] http . Handler ,
2019-07-03 17:22:05 +00:00
tlsManager * traefiktls . Manager ,
2019-03-14 08:30:04 +00:00
) * Manager {
return & Manager {
2021-06-11 13:30:05 +00:00
serviceManager : serviceManager ,
middlewaresBuilder : middlewaresBuilder ,
httpHandlers : httpHandlers ,
httpsHandlers : httpsHandlers ,
tlsManager : tlsManager ,
conf : conf ,
2019-03-14 08:30:04 +00:00
}
}
2020-05-11 10:06:07 +00:00
// Manager is a route/router manager.
2019-03-14 08:30:04 +00:00
type Manager struct {
2021-06-11 13:30:05 +00:00
serviceManager * tcpservice . Manager
middlewaresBuilder middlewareBuilder
httpHandlers map [ string ] http . Handler
httpsHandlers map [ string ] http . Handler
tlsManager * traefiktls . Manager
conf * runtime . Configuration
2019-06-17 16:14:08 +00:00
}
2019-07-15 15:04:04 +00:00
func ( m * Manager ) getTCPRouters ( ctx context . Context , entryPoints [ ] string ) map [ string ] map [ string ] * runtime . TCPRouterInfo {
2019-06-17 16:14:08 +00:00
if m . conf != nil {
2019-07-15 15:04:04 +00:00
return m . conf . GetTCPRoutersByEntryPoints ( ctx , entryPoints )
2019-06-17 16:14:08 +00:00
}
2019-07-15 15:04:04 +00:00
return make ( map [ string ] map [ string ] * runtime . TCPRouterInfo )
2019-06-17 16:14:08 +00:00
}
2019-07-15 15:04:04 +00:00
func ( m * Manager ) getHTTPRouters ( ctx context . Context , entryPoints [ ] string , tls bool ) map [ string ] map [ string ] * runtime . RouterInfo {
2019-06-17 16:14:08 +00:00
if m . conf != nil {
2019-07-15 15:04:04 +00:00
return m . conf . GetRoutersByEntryPoints ( ctx , entryPoints , tls )
2019-06-17 16:14:08 +00:00
}
2019-07-15 15:04:04 +00:00
return make ( map [ string ] map [ string ] * runtime . RouterInfo )
2019-03-14 08:30:04 +00:00
}
2020-05-11 10:06:07 +00:00
// BuildHandlers builds the handlers for the given entrypoints.
2019-03-14 08:30:04 +00:00
func ( m * Manager ) BuildHandlers ( rootCtx context . Context , entryPoints [ ] string ) map [ string ] * tcp . Router {
2019-06-17 16:14:08 +00:00
entryPointsRouters := m . getTCPRouters ( rootCtx , entryPoints )
entryPointsRoutersHTTP := m . getHTTPRouters ( rootCtx , entryPoints , true )
2019-03-14 08:30:04 +00:00
entryPointHandlers := make ( map [ string ] * tcp . Router )
for _ , entryPointName := range entryPoints {
entryPointName := entryPointName
routers := entryPointsRouters [ entryPointName ]
ctx := log . With ( rootCtx , log . Str ( log . EntryPointName , entryPointName ) )
2019-06-17 16:14:08 +00:00
handler , err := m . buildEntryPointHandler ( ctx , routers , entryPointsRoutersHTTP [ entryPointName ] , m . httpHandlers [ entryPointName ] , m . httpsHandlers [ entryPointName ] )
2019-03-14 08:30:04 +00:00
if err != nil {
log . FromContext ( ctx ) . Error ( err )
continue
}
entryPointHandlers [ entryPointName ] = handler
}
return entryPointHandlers
}
2019-07-19 09:52:04 +00:00
type nameAndConfig struct {
routerName string // just so we have it as additional information when logging
TLSConfig * tls . Config
}
2020-07-07 12:42:03 +00:00
func ( m * Manager ) buildEntryPointHandler ( ctx context . Context , configs map [ string ] * runtime . TCPRouterInfo , configsHTTP map [ string ] * runtime . RouterInfo , handlerHTTP , handlerHTTPS http . Handler ) ( * tcp . Router , error ) {
2019-03-14 08:30:04 +00:00
router := & tcp . Router { }
router . HTTPHandler ( handlerHTTP )
2019-06-17 16:14:08 +00:00
2021-06-14 08:06:05 +00:00
defaultTLSConf , err := m . tlsManager . Get ( traefiktls . DefaultTLSStoreName , traefiktls . DefaultTLSConfigName )
2019-06-17 16:14:08 +00:00
if err != nil {
2019-07-19 09:52:04 +00:00
log . FromContext ( ctx ) . Errorf ( "Error during the build of the default TLS configuration: %v" , err )
2019-06-17 16:14:08 +00:00
}
2019-11-14 15:40:05 +00:00
if len ( configsHTTP ) > 0 {
router . AddRouteHTTPTLS ( "*" , defaultTLSConf )
}
2019-07-03 17:22:05 +00:00
// Keyed by domain, then by options reference.
tlsOptionsForHostSNI := map [ string ] map [ string ] nameAndConfig { }
2020-07-17 13:38:04 +00:00
tlsOptionsForHost := map [ string ] string { }
2019-06-17 16:14:08 +00:00
for routerHTTPName , routerHTTPConfig := range configsHTTP {
2020-12-09 13:16:03 +00:00
if routerHTTPConfig . TLS == nil {
2019-06-17 16:14:08 +00:00
continue
}
2020-01-27 09:40:05 +00:00
ctxRouter := log . With ( provider . AddInContext ( ctx , routerHTTPName ) , log . Str ( log . RouterName , routerHTTPName ) )
2019-06-17 16:14:08 +00:00
logger := log . FromContext ( ctxRouter )
2021-06-14 08:06:05 +00:00
tlsOptionsName := traefiktls . DefaultTLSConfigName
if len ( routerHTTPConfig . TLS . Options ) > 0 && routerHTTPConfig . TLS . Options != traefiktls . DefaultTLSConfigName {
2020-12-09 13:16:03 +00:00
tlsOptionsName = provider . GetQualifiedName ( ctxRouter , routerHTTPConfig . TLS . Options )
}
2019-06-17 16:14:08 +00:00
domains , err := rules . ParseDomains ( routerHTTPConfig . Rule )
if err != nil {
2020-05-11 10:06:07 +00:00
routerErr := fmt . Errorf ( "invalid rule %s, error: %w" , routerHTTPConfig . Rule , err )
2019-07-15 15:04:04 +00:00
routerHTTPConfig . AddError ( routerErr , true )
2019-06-17 16:14:08 +00:00
logger . Debug ( routerErr )
continue
}
if len ( domains ) == 0 {
2019-07-03 17:22:05 +00:00
logger . Warnf ( "No domain found in rule %v, the TLS options applied for this router will depend on the hostSNI of each request" , routerHTTPConfig . Rule )
2019-06-17 16:14:08 +00:00
}
for _ , domain := range domains {
2021-06-14 08:06:05 +00:00
tlsConf , err := m . tlsManager . Get ( traefiktls . DefaultTLSStoreName , tlsOptionsName )
2020-12-09 13:16:03 +00:00
if err != nil {
routerHTTPConfig . AddError ( err , true )
logger . Debug ( err )
continue
}
2019-11-14 15:40:05 +00:00
2020-12-09 13:16:03 +00:00
// domain is already in lower case thanks to the domain parsing
if tlsOptionsForHostSNI [ domain ] == nil {
tlsOptionsForHostSNI [ domain ] = make ( map [ string ] nameAndConfig )
}
tlsOptionsForHostSNI [ domain ] [ tlsOptionsName ] = nameAndConfig {
routerName : routerHTTPName ,
TLSConfig : tlsConf ,
}
2020-07-17 13:38:04 +00:00
2020-12-09 13:16:03 +00:00
if name , ok := tlsOptionsForHost [ domain ] ; ok && name != tlsOptionsName {
// Different tlsOptions on the same domain fallback to default
2021-06-14 08:06:05 +00:00
tlsOptionsForHost [ domain ] = traefiktls . DefaultTLSConfigName
2020-12-09 13:16:03 +00:00
} else {
tlsOptionsForHost [ domain ] = tlsOptionsName
2019-07-03 17:22:05 +00:00
}
}
}
2019-06-17 16:14:08 +00:00
2022-02-14 16:18:08 +00:00
sniCheck := snicheck . New ( tlsOptionsForHost , handlerHTTPS )
2020-07-17 13:38:04 +00:00
router . HTTPSHandler ( sniCheck , defaultTLSConf )
2019-07-03 17:22:05 +00:00
logger := log . FromContext ( ctx )
for hostSNI , tlsConfigs := range tlsOptionsForHostSNI {
if len ( tlsConfigs ) == 1 {
var optionsName string
var config * tls . Config
for k , v := range tlsConfigs {
optionsName = k
config = v . TLSConfig
break
}
2019-11-14 15:40:05 +00:00
2019-07-03 17:22:05 +00:00
logger . Debugf ( "Adding route for %s with TLS options %s" , hostSNI , optionsName )
2019-11-14 15:40:05 +00:00
2019-07-03 17:22:05 +00:00
router . AddRouteHTTPTLS ( hostSNI , config )
} else {
routers := make ( [ ] string , 0 , len ( tlsConfigs ) )
for _ , v := range tlsConfigs {
2019-07-19 09:52:04 +00:00
configsHTTP [ v . routerName ] . AddError ( fmt . Errorf ( "found different TLS options for routers on the same host %v, so using the default TLS options instead" , hostSNI ) , false )
2019-07-03 17:22:05 +00:00
routers = append ( routers , v . routerName )
2019-06-17 16:14:08 +00:00
}
2019-11-14 15:40:05 +00:00
2019-07-03 17:22:05 +00:00
logger . Warnf ( "Found different TLS options for routers on the same host %v, so using the default TLS options instead for these routers: %#v" , hostSNI , routers )
2019-11-14 15:40:05 +00:00
2019-07-03 17:22:05 +00:00
router . AddRouteHTTPTLS ( hostSNI , defaultTLSConf )
2019-06-17 16:14:08 +00:00
}
}
2019-05-09 12:30:06 +00:00
2019-03-14 08:30:04 +00:00
for routerName , routerConfig := range configs {
2020-01-27 09:40:05 +00:00
ctxRouter := log . With ( provider . AddInContext ( ctx , routerName ) , log . Str ( log . RouterName , routerName ) )
2019-03-14 08:30:04 +00:00
logger := log . FromContext ( ctxRouter )
2019-07-19 14:42:04 +00:00
if routerConfig . Service == "" {
err := errors . New ( "the service is missing on the router" )
routerConfig . AddError ( err , true )
logger . Error ( err )
continue
}
2019-09-05 11:42:04 +00:00
if routerConfig . Rule == "" {
err := errors . New ( "router has no rule" )
routerConfig . AddError ( err , true )
logger . Error ( err )
continue
}
2021-06-11 13:30:05 +00:00
handler , err := m . buildTCPHandler ( ctxRouter , routerConfig )
2019-03-14 08:30:04 +00:00
if err != nil {
2019-07-19 14:42:04 +00:00
routerConfig . AddError ( err , true )
2019-03-14 08:30:04 +00:00
logger . Error ( err )
continue
}
domains , err := rules . ParseHostSNI ( routerConfig . Rule )
if err != nil {
2019-05-16 08:58:06 +00:00
routerErr := fmt . Errorf ( "unknown rule %s" , routerConfig . Rule )
2019-07-19 14:42:04 +00:00
routerConfig . AddError ( routerErr , true )
2019-09-05 11:42:04 +00:00
logger . Error ( routerErr )
2019-03-14 08:30:04 +00:00
continue
}
2019-05-09 12:30:06 +00:00
2019-03-14 08:30:04 +00:00
for _ , domain := range domains {
2019-05-16 08:58:06 +00:00
logger . Debugf ( "Adding route %s on TCP" , domain )
2019-03-14 08:30:04 +00:00
switch {
case routerConfig . TLS != nil :
2021-03-22 20:16:04 +00:00
if ! rules . IsASCII ( domain ) {
asciiError := fmt . Errorf ( "invalid domain name value %q, non-ASCII characters are not allowed" , domain )
routerConfig . AddError ( asciiError , true )
logger . Debug ( asciiError )
continue
}
2019-03-14 08:30:04 +00:00
if routerConfig . TLS . Passthrough {
router . AddRoute ( domain , handler )
2021-03-22 20:16:04 +00:00
continue
}
2019-06-21 15:18:05 +00:00
2021-03-22 20:16:04 +00:00
tlsOptionsName := routerConfig . TLS . Options
2019-06-21 15:18:05 +00:00
2021-03-22 20:16:04 +00:00
if len ( tlsOptionsName ) == 0 {
2021-06-14 08:06:05 +00:00
tlsOptionsName = traefiktls . DefaultTLSConfigName
2021-03-22 20:16:04 +00:00
}
2019-06-17 16:14:08 +00:00
2021-06-14 08:06:05 +00:00
if tlsOptionsName != traefiktls . DefaultTLSConfigName {
2021-03-22 20:16:04 +00:00
tlsOptionsName = provider . GetQualifiedName ( ctxRouter , tlsOptionsName )
}
2019-06-17 16:14:08 +00:00
2021-06-14 08:06:05 +00:00
tlsConf , err := m . tlsManager . Get ( traefiktls . DefaultTLSStoreName , tlsOptionsName )
2021-03-22 20:16:04 +00:00
if err != nil {
routerConfig . AddError ( err , true )
logger . Debug ( err )
continue
2019-03-14 08:30:04 +00:00
}
2021-03-22 20:16:04 +00:00
router . AddRouteTLS ( domain , handler , tlsConf )
2019-03-14 08:30:04 +00:00
case domain == "*" :
router . AddCatchAllNoTLS ( handler )
default :
logger . Warn ( "TCP Router ignored, cannot specify a Host rule without TLS" )
}
}
}
return router , nil
}
2020-07-17 13:38:04 +00:00
2021-06-11 13:30:05 +00:00
func ( m * Manager ) buildTCPHandler ( ctx context . Context , router * runtime . TCPRouterInfo ) ( tcp . Handler , error ) {
var qualifiedNames [ ] string
for _ , name := range router . Middlewares {
qualifiedNames = append ( qualifiedNames , provider . GetQualifiedName ( ctx , name ) )
}
router . Middlewares = qualifiedNames
if router . Service == "" {
return nil , errors . New ( "the service is missing on the router" )
}
sHandler , err := m . serviceManager . BuildTCP ( ctx , router . Service )
if err != nil {
return nil , err
}
mHandler := m . middlewaresBuilder . BuildChain ( ctx , router . Middlewares )
return tcp . NewChain ( ) . Extend ( * mHandler ) . Then ( sHandler )
}