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"
2020-07-20 16:32:03 +00:00
"net"
2019-03-14 08:30:04 +00:00
"net/http"
2020-07-17 13:38:04 +00:00
"strings"
2019-03-14 08:30:04 +00:00
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"
2020-01-27 09:40:05 +00:00
"github.com/containous/traefik/v2/pkg/server/provider"
2019-08-03 01:58:23 +00:00
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"
)
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 ,
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
}
}
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 {
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
}
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
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
}
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 {
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
}
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 )
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 {
if routerHTTPConfig . TLS != nil {
2019-06-21 15:18:05 +00:00
tlsOptionsName := routerHTTPConfig . TLS . Options
if tlsOptionsName != defaultTLSConfigName {
2020-01-27 09:40:05 +00:00
tlsOptionsName = provider . GetQualifiedName ( ctxRouter , routerHTTPConfig . TLS . Options )
2019-06-21 15:18:05 +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-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
2020-07-20 16:32:03 +00:00
// domain is already in lower case thanks to the domain parsing
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 ,
}
2020-07-17 13:38:04 +00:00
2020-07-20 16:32:03 +00:00
if _ , ok := tlsOptionsForHost [ domain ] ; ok {
2020-07-17 13:38:04 +00:00
// Multiple tlsOptions fallback to default
2020-07-20 16:32:03 +00:00
tlsOptionsForHost [ domain ] = "default"
2020-07-17 13:38:04 +00:00
} else {
2020-07-20 16:32:03 +00:00
tlsOptionsForHost [ domain ] = routerHTTPConfig . TLS . Options
2020-07-17 13:38:04 +00:00
}
2019-07-03 17:22:05 +00:00
}
}
}
2019-06-17 16:14:08 +00:00
2020-07-17 13:38:04 +00:00
sniCheck := http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
2020-07-20 16:32:03 +00:00
if req . TLS == nil {
handlerHTTPS . ServeHTTP ( rw , req )
return
}
host , _ , err := net . SplitHostPort ( req . Host )
if err != nil {
host = req . Host
}
host = strings . TrimSpace ( host )
serverName := strings . TrimSpace ( req . TLS . ServerName )
// Domain Fronting
if ! strings . EqualFold ( host , serverName ) {
tlsOptionSNI := findTLSOptionName ( tlsOptionsForHost , serverName )
tlsOptionHeader := findTLSOptionName ( tlsOptionsForHost , host )
2020-07-17 13:38:04 +00:00
if tlsOptionHeader != tlsOptionSNI {
2020-07-20 16:32:03 +00:00
log . WithoutContext ( ) .
WithField ( "host" , host ) .
WithField ( "req.Host" , req . Host ) .
WithField ( "req.TLS.ServerName" , req . TLS . ServerName ) .
Debugf ( "TLS options difference: SNI=%s, Header:%s" , tlsOptionSNI , tlsOptionHeader )
2020-07-17 13:38:04 +00:00
http . Error ( rw , http . StatusText ( http . StatusMisdirectedRequest ) , http . StatusMisdirectedRequest )
return
}
}
2020-07-20 16:32:03 +00:00
2020-07-17 13:38:04 +00:00
handlerHTTPS . ServeHTTP ( rw , req )
} )
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
}
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 {
2020-01-27 09:40:05 +00:00
tlsOptionsName = provider . 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
}
2020-07-17 13:38:04 +00:00
func findTLSOptionName ( tlsOptionsForHost map [ string ] string , host string ) string {
tlsOptions , ok := tlsOptionsForHost [ host ]
if ok {
return tlsOptions
}
tlsOptions , ok = tlsOptionsForHost [ strings . ToLower ( host ) ]
if ok {
return tlsOptions
}
return "default"
}