tls Manager: do not build a default certificate for ACME challenges store

Co-authored-by: Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
Co-authored-by: Romain <rtribotte@users.noreply.github.com>
This commit is contained in:
Richard Kojedzinszky 2021-06-14 10:06:05 +02:00 committed by GitHub
parent fc9f41b955
commit f15d05b22f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 86 additions and 51 deletions

View file

@ -383,7 +383,6 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
ctxRouter := log.With(ctx, log.Str(log.RouterName, routerName), log.Str(log.Rule, route.Rule)) ctxRouter := log.With(ctx, log.Str(log.RouterName, routerName), log.Str(log.Rule, route.Rule))
logger := log.FromContext(ctxRouter) logger := log.FromContext(ctxRouter)
tlsStore := "default"
if len(route.TLS.Domains) > 0 { if len(route.TLS.Domains) > 0 {
for _, domain := range route.TLS.Domains { for _, domain := range route.TLS.Domains {
if domain.Main != dns01.UnFqdn(domain.Main) { if domain.Main != dns01.UnFqdn(domain.Main) {
@ -400,7 +399,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
for i := 0; i < len(domains); i++ { for i := 0; i < len(domains); i++ {
domain := domains[i] domain := domains[i]
safe.Go(func() { safe.Go(func() {
if _, err := p.resolveCertificate(ctx, domain, tlsStore); err != nil { if _, err := p.resolveCertificate(ctx, domain, traefiktls.DefaultTLSStoreName); err != nil {
log.WithoutContext().WithField(log.ProviderName, p.ResolverName+".acme"). log.WithoutContext().WithField(log.ProviderName, p.ResolverName+".acme").
Errorf("Unable to obtain ACME certificate for domains %q : %v", strings.Join(domain.ToStrArray(), ","), err) Errorf("Unable to obtain ACME certificate for domains %q : %v", strings.Join(domain.ToStrArray(), ","), err)
} }
@ -412,7 +411,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
logger.Errorf("Error parsing domains in provider ACME: %v", err) logger.Errorf("Error parsing domains in provider ACME: %v", err)
continue continue
} }
p.resolveDomains(ctxRouter, domains, tlsStore) p.resolveDomains(ctxRouter, domains, traefiktls.DefaultTLSStoreName)
} }
} }
} }
@ -424,13 +423,12 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
ctxRouter := log.With(ctx, log.Str(log.RouterName, routerName), log.Str(log.Rule, route.Rule)) ctxRouter := log.With(ctx, log.Str(log.RouterName, routerName), log.Str(log.Rule, route.Rule))
tlsStore := "default"
if len(route.TLS.Domains) > 0 { if len(route.TLS.Domains) > 0 {
domains := deleteUnnecessaryDomains(ctxRouter, route.TLS.Domains) domains := deleteUnnecessaryDomains(ctxRouter, route.TLS.Domains)
for i := 0; i < len(domains); i++ { for i := 0; i < len(domains); i++ {
domain := domains[i] domain := domains[i]
safe.Go(func() { safe.Go(func() {
if _, err := p.resolveCertificate(ctx, domain, tlsStore); err != nil { if _, err := p.resolveCertificate(ctx, domain, traefiktls.DefaultTLSStoreName); err != nil {
log.WithoutContext().WithField(log.ProviderName, p.ResolverName+".acme"). log.WithoutContext().WithField(log.ProviderName, p.ResolverName+".acme").
Errorf("Unable to obtain ACME certificate for domains %q : %v", strings.Join(domain.ToStrArray(), ","), err) Errorf("Unable to obtain ACME certificate for domains %q : %v", strings.Join(domain.ToStrArray(), ","), err)
} }
@ -442,7 +440,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
log.FromContext(ctxRouter).Errorf("Error parsing domains in provider ACME: %v", err) log.FromContext(ctxRouter).Errorf("Error parsing domains in provider ACME: %v", err)
continue continue
} }
p.resolveDomains(ctxRouter, domains, tlsStore) p.resolveDomains(ctxRouter, domains, traefiktls.DefaultTLSStoreName)
} }
} }
case <-ctxPool.Done(): case <-ctxPool.Done():

View file

@ -691,7 +691,7 @@ func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options
id := makeID(tlsOption.Namespace, tlsOption.Name) id := makeID(tlsOption.Namespace, tlsOption.Name)
// If the name is default, we override the default config. // If the name is default, we override the default config.
if tlsOption.Name == "default" { if tlsOption.Name == tls.DefaultTLSConfigName {
id = tlsOption.Name id = tlsOption.Name
nsDefault = append(nsDefault, tlsOption.Namespace) nsDefault = append(nsDefault, tlsOption.Namespace)
} }
@ -710,7 +710,7 @@ func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options
} }
if len(nsDefault) > 1 { if len(nsDefault) > 1 {
delete(tlsOptions, "default") delete(tlsOptions, tls.DefaultTLSConfigName)
log.FromContext(ctx).Errorf("Default TLS Options defined in multiple namespaces: %v", nsDefault) log.FromContext(ctx).Errorf("Default TLS Options defined in multiple namespaces: %v", nsDefault)
} }
@ -750,7 +750,7 @@ func buildTLSStores(ctx context.Context, client Client) map[string]tls.Store {
id := makeID(tlsStore.Namespace, tlsStore.Name) id := makeID(tlsStore.Namespace, tlsStore.Name)
// If the name is default, we override the default config. // If the name is default, we override the default config.
if tlsStore.Name == "default" { if tlsStore.Name == tls.DefaultTLSStoreName {
id = tlsStore.Name id = tlsStore.Name
nsDefault = append(nsDefault, tlsStore.Namespace) nsDefault = append(nsDefault, tlsStore.Namespace)
} }
@ -763,7 +763,7 @@ func buildTLSStores(ctx context.Context, client Client) map[string]tls.Store {
} }
if len(nsDefault) > 1 { if len(nsDefault) > 1 {
delete(tlsStores, "default") delete(tlsStores, tls.DefaultTLSStoreName)
log.FromContext(ctx).Errorf("Default TLS Stores defined in multiple namespaces: %v", nsDefault) log.FromContext(ctx).Errorf("Default TLS Stores defined in multiple namespaces: %v", nsDefault)
} }

View file

@ -91,7 +91,7 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint
} }
for key, store := range configuration.TLS.Stores { for key, store := range configuration.TLS.Stores {
if key != "default" { if key != tls.DefaultTLSStoreName {
key = provider.MakeQualifiedName(pvd, key) key = provider.MakeQualifiedName(pvd, key)
} else { } else {
defaultTLSStoreProviders = append(defaultTLSStoreProviders, pvd) defaultTLSStoreProviders = append(defaultTLSStoreProviders, pvd)
@ -113,16 +113,16 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint
if len(defaultTLSStoreProviders) > 1 { if len(defaultTLSStoreProviders) > 1 {
log.WithoutContext().Errorf("Default TLS Stores defined multiple times in %v", defaultTLSOptionProviders) log.WithoutContext().Errorf("Default TLS Stores defined multiple times in %v", defaultTLSOptionProviders)
delete(conf.TLS.Stores, "default") delete(conf.TLS.Stores, tls.DefaultTLSStoreName)
} }
if len(defaultTLSOptionProviders) == 0 { if len(defaultTLSOptionProviders) == 0 {
conf.TLS.Options["default"] = tls.DefaultTLSOptions conf.TLS.Options[tls.DefaultTLSConfigName] = tls.DefaultTLSOptions
} else if len(defaultTLSOptionProviders) > 1 { } else if len(defaultTLSOptionProviders) > 1 {
log.WithoutContext().Errorf("Default TLS Options defined multiple times in %v", defaultTLSOptionProviders) log.WithoutContext().Errorf("Default TLS Options defined multiple times in %v", defaultTLSOptionProviders)
// We do not set an empty tls.TLS{} as above so that we actually get a "cascading failure" later on, // We do not set an empty tls.TLS{} as above so that we actually get a "cascading failure" later on,
// i.e. routers depending on this missing TLS option will fail to initialize as well. // i.e. routers depending on this missing TLS option will fail to initialize as well.
delete(conf.TLS.Options, "default") delete(conf.TLS.Options, tls.DefaultTLSConfigName)
} }
return conf return conf

View file

@ -18,11 +18,6 @@ import (
traefiktls "github.com/traefik/traefik/v2/pkg/tls" traefiktls "github.com/traefik/traefik/v2/pkg/tls"
) )
const (
defaultTLSConfigName = "default"
defaultTLSStoreName = "default"
)
type middlewareBuilder interface { type middlewareBuilder interface {
BuildChain(ctx context.Context, names []string) *tcp.Chain BuildChain(ctx context.Context, names []string) *tcp.Chain
} }
@ -103,7 +98,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
router := &tcp.Router{} router := &tcp.Router{}
router.HTTPHandler(handlerHTTP) router.HTTPHandler(handlerHTTP)
defaultTLSConf, err := m.tlsManager.Get(defaultTLSStoreName, defaultTLSConfigName) defaultTLSConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, traefiktls.DefaultTLSConfigName)
if err != nil { if err != nil {
log.FromContext(ctx).Errorf("Error during the build of the default TLS configuration: %v", err) log.FromContext(ctx).Errorf("Error during the build of the default TLS configuration: %v", err)
} }
@ -123,8 +118,8 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
ctxRouter := log.With(provider.AddInContext(ctx, routerHTTPName), log.Str(log.RouterName, routerHTTPName)) ctxRouter := log.With(provider.AddInContext(ctx, routerHTTPName), log.Str(log.RouterName, routerHTTPName))
logger := log.FromContext(ctxRouter) logger := log.FromContext(ctxRouter)
tlsOptionsName := defaultTLSConfigName tlsOptionsName := traefiktls.DefaultTLSConfigName
if len(routerHTTPConfig.TLS.Options) > 0 && routerHTTPConfig.TLS.Options != defaultTLSConfigName { if len(routerHTTPConfig.TLS.Options) > 0 && routerHTTPConfig.TLS.Options != traefiktls.DefaultTLSConfigName {
tlsOptionsName = provider.GetQualifiedName(ctxRouter, routerHTTPConfig.TLS.Options) tlsOptionsName = provider.GetQualifiedName(ctxRouter, routerHTTPConfig.TLS.Options)
} }
@ -141,7 +136,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
} }
for _, domain := range domains { for _, domain := range domains {
tlsConf, err := m.tlsManager.Get(defaultTLSStoreName, tlsOptionsName) tlsConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, tlsOptionsName)
if err != nil { if err != nil {
routerHTTPConfig.AddError(err, true) routerHTTPConfig.AddError(err, true)
logger.Debug(err) logger.Debug(err)
@ -159,7 +154,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
if name, ok := tlsOptionsForHost[domain]; ok && name != tlsOptionsName { if name, ok := tlsOptionsForHost[domain]; ok && name != tlsOptionsName {
// Different tlsOptions on the same domain fallback to default // Different tlsOptions on the same domain fallback to default
tlsOptionsForHost[domain] = defaultTLSConfigName tlsOptionsForHost[domain] = traefiktls.DefaultTLSConfigName
} else { } else {
tlsOptionsForHost[domain] = tlsOptionsName tlsOptionsForHost[domain] = tlsOptionsName
} }
@ -280,14 +275,14 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
tlsOptionsName := routerConfig.TLS.Options tlsOptionsName := routerConfig.TLS.Options
if len(tlsOptionsName) == 0 { if len(tlsOptionsName) == 0 {
tlsOptionsName = defaultTLSConfigName tlsOptionsName = traefiktls.DefaultTLSConfigName
} }
if tlsOptionsName != defaultTLSConfigName { if tlsOptionsName != traefiktls.DefaultTLSConfigName {
tlsOptionsName = provider.GetQualifiedName(ctxRouter, tlsOptionsName) tlsOptionsName = provider.GetQualifiedName(ctxRouter, tlsOptionsName)
} }
tlsConf, err := m.tlsManager.Get(defaultTLSStoreName, tlsOptionsName) tlsConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, tlsOptionsName)
if err != nil { if err != nil {
routerConfig.AddError(err, true) routerConfig.AddError(err, true)
logger.Debug(err) logger.Debug(err)
@ -338,5 +333,5 @@ func findTLSOptionName(tlsOptionsForHost map[string]string, host string) string
return tlsOptions return tlsOptions
} }
return defaultTLSConfigName return traefiktls.DefaultTLSConfigName
} }

View file

@ -69,7 +69,10 @@ func (c CertificateStore) GetAllDomains() []string {
} }
// GetBestCertificate returns the best match certificate, and caches the response. // GetBestCertificate returns the best match certificate, and caches the response.
func (c CertificateStore) GetBestCertificate(clientHello *tls.ClientHelloInfo) *tls.Certificate { func (c *CertificateStore) GetBestCertificate(clientHello *tls.ClientHelloInfo) *tls.Certificate {
if c == nil {
return nil
}
domainToCheck := strings.ToLower(strings.TrimSpace(clientHello.ServerName)) domainToCheck := strings.ToLower(strings.TrimSpace(clientHello.ServerName))
if len(domainToCheck) == 0 { if len(domainToCheck) == 0 {
// If no ServerName is provided, Check for local IP address matches // If no ServerName is provided, Check for local IP address matches

View file

@ -15,16 +15,24 @@ import (
"github.com/traefik/traefik/v2/pkg/types" "github.com/traefik/traefik/v2/pkg/types"
) )
const (
// DefaultTLSConfigName is the name of the default set of options for configuring TLS.
DefaultTLSConfigName = "default"
// DefaultTLSStoreName is the name of the default store of TLS certificates.
// Note that it actually is the only usable one for now.
DefaultTLSStoreName = "default"
)
// DefaultTLSOptions the default TLS options. // DefaultTLSOptions the default TLS options.
var DefaultTLSOptions = Options{} var DefaultTLSOptions = Options{}
// Manager is the TLS option/store/configuration factory. // Manager is the TLS option/store/configuration factory.
type Manager struct { type Manager struct {
lock sync.RWMutex
storesConfig map[string]Store storesConfig map[string]Store
stores map[string]*CertificateStore stores map[string]*CertificateStore
configs map[string]Options configs map[string]Options
certs []*CertAndStores certs []*CertAndStores
lock sync.RWMutex
} }
// NewManager creates a new Manager. // NewManager creates a new Manager.
@ -38,6 +46,7 @@ func NewManager() *Manager {
} }
// UpdateConfigs updates the TLS* configuration options. // UpdateConfigs updates the TLS* configuration options.
// It initializes the default TLS store, and the TLS store for the ACME challenges.
func (m *Manager) UpdateConfigs(ctx context.Context, stores map[string]Store, configs map[string]Options, certs []*CertAndStores) { func (m *Manager) UpdateConfigs(ctx context.Context, stores map[string]Store, configs map[string]Options, certs []*CertAndStores) {
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock() defer m.lock.Unlock()
@ -46,10 +55,22 @@ func (m *Manager) UpdateConfigs(ctx context.Context, stores map[string]Store, co
m.storesConfig = stores m.storesConfig = stores
m.certs = certs m.certs = certs
if m.storesConfig == nil {
m.storesConfig = make(map[string]Store)
}
if _, ok := m.storesConfig[DefaultTLSStoreName]; !ok {
m.storesConfig[DefaultTLSStoreName] = Store{}
}
if _, ok := m.storesConfig[tlsalpn01.ACMETLS1Protocol]; !ok {
m.storesConfig[tlsalpn01.ACMETLS1Protocol] = Store{}
}
m.stores = make(map[string]*CertificateStore) m.stores = make(map[string]*CertificateStore)
for storeName, storeConfig := range m.storesConfig { for storeName, storeConfig := range m.storesConfig {
ctxStore := log.With(ctx, log.Str(log.TLSStoreName, storeName)) ctxStore := log.With(ctx, log.Str(log.TLSStoreName, storeName))
store, err := buildCertificateStore(ctxStore, storeConfig) store, err := buildCertificateStore(ctxStore, storeConfig, storeName)
if err != nil { if err != nil {
log.FromContext(ctxStore).Errorf("Error while creating certificate store: %v", err) log.FromContext(ctxStore).Errorf("Error while creating certificate store: %v", err)
continue continue
@ -75,7 +96,12 @@ func (m *Manager) UpdateConfigs(ctx context.Context, stores map[string]Store, co
} }
for storeName, certs := range storesCertificates { for storeName, certs := range storesCertificates {
m.getStore(storeName).DynamicCerts.Set(certs) st, ok := m.stores[storeName]
if !ok {
st, _ = buildCertificateStore(context.Background(), Store{}, storeName)
m.stores[storeName] = st
}
st.DynamicCerts.Set(certs)
} }
} }
@ -87,20 +113,25 @@ func (m *Manager) Get(storeName, configName string) (*tls.Config, error) {
var tlsConfig *tls.Config var tlsConfig *tls.Config
var err error var err error
sniStrict := false
config, ok := m.configs[configName] config, ok := m.configs[configName]
if !ok { if ok {
sniStrict = config.SniStrict
tlsConfig, err = buildTLSConfig(config)
} else {
err = fmt.Errorf("unknown TLS options: %s", configName) err = fmt.Errorf("unknown TLS options: %s", configName)
}
if err != nil {
tlsConfig = &tls.Config{} tlsConfig = &tls.Config{}
} }
store := m.getStore(storeName) store := m.getStore(storeName)
if store == nil {
err = fmt.Errorf("TLS store %s not found", storeName)
}
acmeTLSStore := m.getStore(tlsalpn01.ACMETLS1Protocol) acmeTLSStore := m.getStore(tlsalpn01.ACMETLS1Protocol)
if acmeTLSStore == nil {
if err == nil { err = fmt.Errorf("ACME TLS store %s not found", tlsalpn01.ACMETLS1Protocol)
tlsConfig, err = buildTLSConfig(config)
if err != nil {
tlsConfig = &tls.Config{}
}
} }
tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
@ -120,7 +151,7 @@ func (m *Manager) Get(storeName, configName string) (*tls.Config, error) {
return bestCertificate, nil return bestCertificate, nil
} }
if m.configs[configName].SniStrict { if sniStrict {
return nil, fmt.Errorf("strict SNI enabled - No certificate found for domain: %q, closing connection", domainToCheck) return nil, fmt.Errorf("strict SNI enabled - No certificate found for domain: %q, closing connection", domainToCheck)
} }
@ -152,12 +183,13 @@ func (m *Manager) GetCertificates() []*x509.Certificate {
return certificates return certificates
} }
// getStore returns the store found for storeName, or nil otherwise.
func (m *Manager) getStore(storeName string) *CertificateStore { func (m *Manager) getStore(storeName string) *CertificateStore {
_, ok := m.stores[storeName] st, ok := m.stores[storeName]
if !ok { if !ok {
m.stores[storeName], _ = buildCertificateStore(context.Background(), Store{}) return nil
} }
return m.stores[storeName] return st
} }
// GetStore gets the certificate store of a given name. // GetStore gets the certificate store of a given name.
@ -168,7 +200,7 @@ func (m *Manager) GetStore(storeName string) *CertificateStore {
return m.getStore(storeName) return m.getStore(storeName)
} }
func buildCertificateStore(ctx context.Context, tlsStore Store) (*CertificateStore, error) { func buildCertificateStore(ctx context.Context, tlsStore Store, storename string) (*CertificateStore, error) {
certificateStore := NewCertificateStore() certificateStore := NewCertificateStore()
certificateStore.DynamicCerts.Set(make(map[string]*tls.Certificate)) certificateStore.DynamicCerts.Set(make(map[string]*tls.Certificate))
@ -178,14 +210,21 @@ func buildCertificateStore(ctx context.Context, tlsStore Store) (*CertificateSto
return certificateStore, err return certificateStore, err
} }
certificateStore.DefaultCertificate = cert certificateStore.DefaultCertificate = cert
} else { return certificateStore, nil
log.FromContext(ctx).Debug("No default certificate, generating one")
cert, err := generate.DefaultCertificate()
if err != nil {
return certificateStore, err
}
certificateStore.DefaultCertificate = cert
} }
// a default cert for the ACME store does not make any sense, so generating one
// is a waste.
if storename == tlsalpn01.ACMETLS1Protocol {
return certificateStore, nil
}
log.FromContext(ctx).Debug("No default certificate, generating one")
cert, err := generate.DefaultCertificate()
if err != nil {
return certificateStore, err
}
certificateStore.DefaultCertificate = cert
return certificateStore, nil return certificateStore, nil
} }