2019-03-14 08:30:04 +00:00
package tcp
import (
"context"
2019-07-03 17:22:05 +00:00
"crypto/tls"
2019-05-16 08:58:06 +00:00
"fmt"
2019-03-14 08:30:04 +00:00
"net/http"
2019-03-15 08:42:03 +00:00
"github.com/containous/traefik/pkg/config"
"github.com/containous/traefik/pkg/log"
"github.com/containous/traefik/pkg/rules"
"github.com/containous/traefik/pkg/server/internal"
tcpservice "github.com/containous/traefik/pkg/server/service/tcp"
"github.com/containous/traefik/pkg/tcp"
2019-07-03 17:22:05 +00:00
traefiktls "github.com/containous/traefik/pkg/tls"
2019-03-14 08:30:04 +00:00
)
// NewManager Creates a new Manager
2019-05-16 08:58:06 +00:00
func NewManager ( conf * config . RuntimeConfiguration ,
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-06-17 16:14:08 +00:00
conf * config . RuntimeConfiguration
}
func ( m * Manager ) getTCPRouters ( ctx context . Context , entryPoints [ ] string ) map [ string ] map [ string ] * config . TCPRouterInfo {
if m . conf != nil {
return m . conf . GetTCPRoutersByEntrypoints ( ctx , entryPoints )
}
return make ( map [ string ] map [ string ] * config . TCPRouterInfo )
}
func ( m * Manager ) getHTTPRouters ( ctx context . Context , entryPoints [ ] string , tls bool ) map [ string ] map [ string ] * config . RouterInfo {
if m . conf != nil {
return m . conf . GetRoutersByEntrypoints ( ctx , entryPoints , tls )
}
return make ( map [ string ] map [ string ] * config . 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-06-17 16:14:08 +00:00
func ( m * Manager ) buildEntryPointHandler ( ctx context . Context , configs map [ string ] * config . TCPRouterInfo , configsHTTP map [ string ] * config . 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-21 15:18:05 +00:00
const defaultTLSConfigName = "default"
2019-06-17 16:14:08 +00:00
2019-06-21 15:18:05 +00:00
defaultTLSConf , err := m . tlsManager . Get ( "default" , defaultTLSConfigName )
2019-06-17 16:14:08 +00:00
if err != nil {
return nil , err
}
router . HTTPSHandler ( handlerHTTPS , defaultTLSConf )
2019-07-03 17:22:05 +00:00
type nameAndConfig struct {
routerName string // just so we have it as additional information when logging
TLSConfig * tls . Config
}
// 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 )
routerHTTPConfig . Err = routerErr . Error ( )
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 )
}
tlsConf , err := m . tlsManager . Get ( "default" , tlsOptionsName )
2019-06-17 16:14:08 +00:00
if err != nil {
routerHTTPConfig . Err = err . Error ( )
logger . Debug ( err )
continue
}
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
}
logger . Debugf ( "Adding route for %s with TLS options %s" , hostSNI , optionsName )
router . AddRouteHTTPTLS ( hostSNI , config )
} else {
routers := make ( [ ] string , 0 , len ( tlsConfigs ) )
for _ , v := range tlsConfigs {
// TODO: properly deal with critical errors VS non-critical errors
if configsHTTP [ v . routerName ] . Err != "" {
configsHTTP [ v . routerName ] . Err += "\n"
}
configsHTTP [ v . routerName ] . Err += fmt . Sprintf ( "found different TLS options for routers on the same host %v, so using the default TLS option instead" , hostSNI )
routers = append ( routers , v . routerName )
2019-06-17 16:14:08 +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 )
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 )
handler , err := m . serviceManager . BuildTCP ( ctxRouter , routerConfig . Service )
if err != nil {
2019-05-16 08:58:06 +00:00
routerConfig . Err = err . Error ( )
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 )
routerConfig . Err = routerErr . Error ( )
logger . Debug ( 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-06-21 15:18:05 +00:00
tlsConf , err := m . tlsManager . Get ( "default" , tlsOptionsName )
2019-06-17 16:14:08 +00:00
if err != nil {
routerConfig . Err = err . Error ( )
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
}