1
0
Fork 0

Support SPIFFE mTLS between Traefik and Backend servers

This commit is contained in:
Julien Levesy 2022-10-14 17:16:08 +02:00 committed by GitHub
parent 33f0aed5ea
commit b39ce8cc58
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 736 additions and 24 deletions

View file

@ -11,6 +11,10 @@ import (
"sync"
"time"
"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/v2/pkg/config/dynamic"
"github.com/traefik/traefik/v2/pkg/log"
traefiktls "github.com/traefik/traefik/v2/pkg/tls"
@ -26,11 +30,18 @@ func (t *h2cTransportWrapper) RoundTrip(req *http.Request) (*http.Response, erro
return t.Transport.RoundTrip(req)
}
// SpiffeX509Source allows to retrieve a x509 SVID and bundle.
type SpiffeX509Source interface {
x509svid.Source
x509bundle.Source
}
// NewRoundTripperManager creates a new RoundTripperManager.
func NewRoundTripperManager() *RoundTripperManager {
func NewRoundTripperManager(spiffeX509Source SpiffeX509Source) *RoundTripperManager {
return &RoundTripperManager{
roundTrippers: make(map[string]http.RoundTripper),
configs: make(map[string]*dynamic.ServersTransport),
roundTrippers: make(map[string]http.RoundTripper),
configs: make(map[string]*dynamic.ServersTransport),
spiffeX509Source: spiffeX509Source,
}
}
@ -39,6 +50,8 @@ type RoundTripperManager struct {
rtLock sync.RWMutex
roundTrippers map[string]http.RoundTripper
configs map[string]*dynamic.ServersTransport
spiffeX509Source SpiffeX509Source
}
// Update updates the roundtrippers configurations.
@ -59,7 +72,7 @@ func (r *RoundTripperManager) Update(newConfigs map[string]*dynamic.ServersTrans
}
var err error
r.roundTrippers[configName], err = createRoundTripper(newConfig)
r.roundTrippers[configName], err = r.createRoundTripper(newConfig)
if err != nil {
log.WithoutContext().Errorf("Could not configure HTTP Transport %s, fallback on default transport: %v", configName, err)
r.roundTrippers[configName] = http.DefaultTransport
@ -72,7 +85,7 @@ func (r *RoundTripperManager) Update(newConfigs map[string]*dynamic.ServersTrans
}
var err error
r.roundTrippers[newConfigName], err = createRoundTripper(newConfig)
r.roundTrippers[newConfigName], err = r.createRoundTripper(newConfig)
if err != nil {
log.WithoutContext().Errorf("Could not configure HTTP Transport %s, fallback on default transport: %v", newConfigName, err)
r.roundTrippers[newConfigName] = http.DefaultTransport
@ -102,7 +115,7 @@ func (r *RoundTripperManager) Get(name string) (http.RoundTripper, error) {
// For the settings that can't be configured in Traefik it uses the default http.Transport settings.
// An exception to this is the MaxIdleConns setting as we only provide the option MaxIdleConnsPerHost in Traefik at this point in time.
// Setting this value to the default of 100 could lead to confusing behavior and backwards compatibility issues.
func createRoundTripper(cfg *dynamic.ServersTransport) (http.RoundTripper, error) {
func (r *RoundTripperManager) createRoundTripper(cfg *dynamic.ServersTransport) (http.RoundTripper, error) {
if cfg == nil {
return nil, errors.New("no transport configuration given")
}
@ -132,7 +145,24 @@ func createRoundTripper(cfg *dynamic.ServersTransport) (http.RoundTripper, error
transport.IdleConnTimeout = time.Duration(cfg.ForwardingTimeouts.IdleConnTimeout)
}
if cfg.Spiffe != nil {
if r.spiffeX509Source == nil {
return nil, errors.New("SPIFFE is enabled for this transport, but not configured")
}
spiffeAuthorizer, err := buildSpiffeAuthorizer(cfg.Spiffe)
if err != nil {
return nil, fmt.Errorf("unable to build SPIFFE authorizer: %w", err)
}
transport.TLSClientConfig = tlsconfig.MTLSClientConfig(r.spiffeX509Source, r.spiffeX509Source, spiffeAuthorizer)
}
if cfg.InsecureSkipVerify || len(cfg.RootCAs) > 0 || len(cfg.ServerName) > 0 || len(cfg.Certificates) > 0 || cfg.PeerCertURI != "" {
if transport.TLSClientConfig != nil {
return nil, errors.New("TLS and SPIFFE configuration cannot be defined at the same time")
}
transport.TLSClientConfig = &tls.Config{
ServerName: cfg.ServerName,
InsecureSkipVerify: cfg.InsecureSkipVerify,
@ -173,3 +203,31 @@ func createRootCACertPool(rootCAs []traefiktls.FileOrContent) *x509.CertPool {
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
}
}