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"
2019-08-03 01:58:23 +00:00
"github.com/containous/traefik/v2/pkg/config/runtime"
"github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/rules"
"github.com/containous/traefik/v2/pkg/server/internal"
tcpservice "github.com/containous/traefik/v2/pkg/server/service/tcp"
"github.com/containous/traefik/v2/pkg/tcp"
traefiktls "github.com/containous/traefik/v2/pkg/tls"
2019-03-14 08:30:04 +00:00
)
2019-11-14 15:40:05 +00:00
const (
defaultTLSConfigName = "default"
defaultTLSStoreName = "default"
)
2019-03-14 08:30:04 +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 ,
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 {
serviceManager : serviceManager ,
httpHandlers : httpHandlers ,
httpsHandlers : httpsHandlers ,
2019-06-17 16:14:08 +00:00
tlsManager : tlsManager ,
conf : conf ,
2019-03-14 08:30:04 +00:00
}
}
// Manager is a route/router manager
type Manager struct {
serviceManager * tcpservice . Manager
httpHandlers map [ string ] http . Handler
httpsHandlers map [ string ] http . Handler
2019-07-03 17:22:05 +00:00
tlsManager * traefiktls . Manager
2019-07-15 15:04:04 +00:00
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
}
// BuildHandlers builds the handlers for the given entrypoints
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
}
2019-07-15 15:04:04 +00:00
func ( m * Manager ) buildEntryPointHandler ( ctx context . Context , configs map [ string ] * runtime . TCPRouterInfo , configsHTTP map [ string ] * runtime . RouterInfo , handlerHTTP http . Handler , 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
2019-11-14 15:40:05 +00:00
defaultTLSConf , err := m . tlsManager . Get ( defaultTLSStoreName , 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
}
router . HTTPSHandler ( handlerHTTPS , defaultTLSConf )
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 { }
2019-06-17 16:14:08 +00:00
for routerHTTPName , routerHTTPConfig := range configsHTTP {
2019-06-21 15:18:05 +00:00
if len ( routerHTTPConfig . TLS . Options ) == 0 || routerHTTPConfig . TLS . Options == defaultTLSConfigName {
2019-06-17 16:14:08 +00:00
continue
}
ctxRouter := log . With ( internal . AddProviderInContext ( ctx , routerHTTPName ) , log . Str ( log . RouterName , routerHTTPName ) )
logger := log . FromContext ( ctxRouter )
domains , err := rules . ParseDomains ( routerHTTPConfig . Rule )
if err != nil {
routerErr := fmt . Errorf ( "invalid rule %s, error: %v" , 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 {
if routerHTTPConfig . TLS != nil {
2019-06-21 15:18:05 +00:00
tlsOptionsName := routerHTTPConfig . TLS . Options
if tlsOptionsName != defaultTLSConfigName {
tlsOptionsName = internal . GetQualifiedName ( ctxRouter , routerHTTPConfig . TLS . Options )
}
2019-11-14 15:40:05 +00:00
tlsConf , err := m . tlsManager . Get ( defaultTLSStoreName , tlsOptionsName )
2019-06-17 16:14:08 +00:00
if err != nil {
2019-07-15 15:04:04 +00:00
routerHTTPConfig . AddError ( err , true )
2019-06-17 16:14:08 +00:00
logger . Debug ( err )
continue
}
2019-11-14 15:40:05 +00:00
2019-07-03 17:22:05 +00:00
if tlsOptionsForHostSNI [ domain ] == nil {
tlsOptionsForHostSNI [ domain ] = make ( map [ string ] nameAndConfig )
}
tlsOptionsForHostSNI [ domain ] [ routerHTTPConfig . TLS . Options ] = nameAndConfig {
routerName : routerHTTPName ,
TLSConfig : tlsConf ,
}
}
}
}
2019-06-17 16:14:08 +00:00
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 {
2019-05-09 12:30:06 +00:00
ctxRouter := log . With ( internal . AddProviderInContext ( 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
}
2019-03-14 08:30:04 +00:00
handler , err := m . serviceManager . BuildTCP ( ctxRouter , routerConfig . Service )
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 :
if routerConfig . TLS . Passthrough {
router . AddRoute ( domain , handler )
} else {
2019-06-21 15:18:05 +00:00
tlsOptionsName := routerConfig . TLS . Options
if len ( tlsOptionsName ) == 0 {
tlsOptionsName = defaultTLSConfigName
}
if tlsOptionsName != defaultTLSConfigName {
tlsOptionsName = internal . GetQualifiedName ( ctxRouter , tlsOptionsName )
2019-06-17 16:14:08 +00:00
}
2019-11-14 15:40:05 +00:00
tlsConf , err := m . tlsManager . Get ( defaultTLSStoreName , tlsOptionsName )
2019-06-17 16:14:08 +00:00
if err != nil {
2019-07-19 14:42:04 +00:00
routerConfig . AddError ( err , true )
2019-06-17 16:14:08 +00:00
logger . Debug ( err )
continue
}
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
}