mirror of
				https://github.com/traefik/traefik.git
				synced 2025-10-31 08:21:27 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			207 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			207 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package tcp
 | |
| 
 | |
| import (
 | |
| 	"crypto/tls"
 | |
| 	"crypto/x509"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"net"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/rs/zerolog/log"
 | |
| 	"github.com/spiffe/go-spiffe/v2/bundle/x509bundle"
 | |
| 	"github.com/spiffe/go-spiffe/v2/spiffeid"
 | |
| 	"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
 | |
| 	"github.com/spiffe/go-spiffe/v2/svid/x509svid"
 | |
| 	"github.com/traefik/traefik/v3/pkg/config/dynamic"
 | |
| 	traefiktls "github.com/traefik/traefik/v3/pkg/tls"
 | |
| 	"github.com/traefik/traefik/v3/pkg/types"
 | |
| 	"golang.org/x/net/proxy"
 | |
| )
 | |
| 
 | |
| type Dialer interface {
 | |
| 	proxy.Dialer
 | |
| 
 | |
| 	TerminationDelay() time.Duration
 | |
| }
 | |
| 
 | |
| type tcpDialer struct {
 | |
| 	proxy.Dialer
 | |
| 	terminationDelay time.Duration
 | |
| }
 | |
| 
 | |
| func (d tcpDialer) TerminationDelay() time.Duration {
 | |
| 	return d.terminationDelay
 | |
| }
 | |
| 
 | |
| // SpiffeX509Source allows to retrieve a x509 SVID and bundle.
 | |
| type SpiffeX509Source interface {
 | |
| 	x509svid.Source
 | |
| 	x509bundle.Source
 | |
| }
 | |
| 
 | |
| // DialerManager handles dialer for the reverse proxy.
 | |
| type DialerManager struct {
 | |
| 	rtLock           sync.RWMutex
 | |
| 	dialers          map[string]Dialer
 | |
| 	dialersTLS       map[string]Dialer
 | |
| 	spiffeX509Source SpiffeX509Source
 | |
| }
 | |
| 
 | |
| // NewDialerManager creates a new DialerManager.
 | |
| func NewDialerManager(spiffeX509Source SpiffeX509Source) *DialerManager {
 | |
| 	return &DialerManager{
 | |
| 		dialers:          make(map[string]Dialer),
 | |
| 		dialersTLS:       make(map[string]Dialer),
 | |
| 		spiffeX509Source: spiffeX509Source,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Update updates the dialers configurations.
 | |
| func (d *DialerManager) Update(configs map[string]*dynamic.TCPServersTransport) {
 | |
| 	d.rtLock.Lock()
 | |
| 	defer d.rtLock.Unlock()
 | |
| 
 | |
| 	d.dialers = make(map[string]Dialer)
 | |
| 	d.dialersTLS = make(map[string]Dialer)
 | |
| 	for configName, config := range configs {
 | |
| 		if err := d.createDialers(configName, config); err != nil {
 | |
| 			log.Debug().
 | |
| 				Str("dialer", configName).
 | |
| 				Err(err).
 | |
| 				Msg("Create TCP Dialer")
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Get gets a dialer by name.
 | |
| func (d *DialerManager) Get(name string, tls bool) (Dialer, error) {
 | |
| 	if len(name) == 0 {
 | |
| 		name = "default@internal"
 | |
| 	}
 | |
| 
 | |
| 	d.rtLock.RLock()
 | |
| 	defer d.rtLock.RUnlock()
 | |
| 
 | |
| 	if tls {
 | |
| 		if rt, ok := d.dialersTLS[name]; ok {
 | |
| 			return rt, nil
 | |
| 		}
 | |
| 
 | |
| 		return nil, fmt.Errorf("TCP dialer not found %s", name)
 | |
| 	}
 | |
| 
 | |
| 	if rt, ok := d.dialers[name]; ok {
 | |
| 		return rt, nil
 | |
| 	}
 | |
| 
 | |
| 	return nil, fmt.Errorf("TCP dialer not found %s", name)
 | |
| }
 | |
| 
 | |
| // createDialers creates the dialers according to the TCPServersTransport configuration.
 | |
| func (d *DialerManager) createDialers(name string, cfg *dynamic.TCPServersTransport) error {
 | |
| 	if cfg == nil {
 | |
| 		return errors.New("no transport configuration given")
 | |
| 	}
 | |
| 
 | |
| 	dialer := &net.Dialer{
 | |
| 		Timeout:   time.Duration(cfg.DialTimeout),
 | |
| 		KeepAlive: time.Duration(cfg.DialKeepAlive),
 | |
| 	}
 | |
| 
 | |
| 	var tlsConfig *tls.Config
 | |
| 
 | |
| 	if cfg.TLS != nil {
 | |
| 		if cfg.TLS.Spiffe != nil {
 | |
| 			if d.spiffeX509Source == nil {
 | |
| 				return errors.New("SPIFFE is enabled for this transport, but not configured")
 | |
| 			}
 | |
| 
 | |
| 			authorizer, err := buildSpiffeAuthorizer(cfg.TLS.Spiffe)
 | |
| 			if err != nil {
 | |
| 				return fmt.Errorf("unable to build SPIFFE authorizer: %w", err)
 | |
| 			}
 | |
| 
 | |
| 			tlsConfig = tlsconfig.MTLSClientConfig(d.spiffeX509Source, d.spiffeX509Source, authorizer)
 | |
| 		}
 | |
| 
 | |
| 		if cfg.TLS.InsecureSkipVerify || len(cfg.TLS.RootCAs) > 0 || len(cfg.TLS.ServerName) > 0 || len(cfg.TLS.Certificates) > 0 || cfg.TLS.PeerCertURI != "" {
 | |
| 			if tlsConfig != nil {
 | |
| 				return errors.New("TLS and SPIFFE configuration cannot be defined at the same time")
 | |
| 			}
 | |
| 
 | |
| 			tlsConfig = &tls.Config{
 | |
| 				ServerName:         cfg.TLS.ServerName,
 | |
| 				InsecureSkipVerify: cfg.TLS.InsecureSkipVerify,
 | |
| 				RootCAs:            createRootCACertPool(cfg.TLS.RootCAs),
 | |
| 				Certificates:       cfg.TLS.Certificates.GetCertificates(),
 | |
| 			}
 | |
| 
 | |
| 			if cfg.TLS.PeerCertURI != "" {
 | |
| 				tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
 | |
| 					return traefiktls.VerifyPeerCertificate(cfg.TLS.PeerCertURI, tlsConfig, rawCerts)
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	tlsDialer := &tls.Dialer{
 | |
| 		NetDialer: dialer,
 | |
| 		Config:    tlsConfig,
 | |
| 	}
 | |
| 
 | |
| 	d.dialers[name] = tcpDialer{dialer, time.Duration(cfg.TerminationDelay)}
 | |
| 	d.dialersTLS[name] = tcpDialer{tlsDialer, time.Duration(cfg.TerminationDelay)}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func createRootCACertPool(rootCAs []types.FileOrContent) *x509.CertPool {
 | |
| 	if len(rootCAs) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	roots := x509.NewCertPool()
 | |
| 
 | |
| 	for _, cert := range rootCAs {
 | |
| 		certContent, err := cert.Read()
 | |
| 		if err != nil {
 | |
| 			log.Err(err).Msg("Error while read RootCAs")
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		roots.AppendCertsFromPEM(certContent)
 | |
| 	}
 | |
| 
 | |
| 	return roots
 | |
| }
 | |
| 
 | |
| func buildSpiffeAuthorizer(cfg *dynamic.Spiffe) (tlsconfig.Authorizer, error) {
 | |
| 	switch {
 | |
| 	case len(cfg.IDs) > 0:
 | |
| 		spiffeIDs := make([]spiffeid.ID, 0, len(cfg.IDs))
 | |
| 		for _, rawID := range cfg.IDs {
 | |
| 			id, err := spiffeid.FromString(rawID)
 | |
| 			if err != nil {
 | |
| 				return nil, fmt.Errorf("invalid SPIFFE ID: %w", err)
 | |
| 			}
 | |
| 
 | |
| 			spiffeIDs = append(spiffeIDs, id)
 | |
| 		}
 | |
| 
 | |
| 		return tlsconfig.AuthorizeOneOf(spiffeIDs...), nil
 | |
| 
 | |
| 	case cfg.TrustDomain != "":
 | |
| 		trustDomain, err := spiffeid.TrustDomainFromString(cfg.TrustDomain)
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("invalid SPIFFE trust domain: %w", err)
 | |
| 		}
 | |
| 
 | |
| 		return tlsconfig.AuthorizeMemberOf(trustDomain), nil
 | |
| 
 | |
| 	default:
 | |
| 		return tlsconfig.AuthorizeAny(), nil
 | |
| 	}
 | |
| }
 |