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:
parent
fc9f41b955
commit
f15d05b22f
6 changed files with 86 additions and 51 deletions
|
@ -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))
|
||||
logger := log.FromContext(ctxRouter)
|
||||
|
||||
tlsStore := "default"
|
||||
if len(route.TLS.Domains) > 0 {
|
||||
for _, domain := range route.TLS.Domains {
|
||||
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++ {
|
||||
domain := domains[i]
|
||||
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").
|
||||
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)
|
||||
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))
|
||||
|
||||
tlsStore := "default"
|
||||
if len(route.TLS.Domains) > 0 {
|
||||
domains := deleteUnnecessaryDomains(ctxRouter, route.TLS.Domains)
|
||||
for i := 0; i < len(domains); i++ {
|
||||
domain := domains[i]
|
||||
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").
|
||||
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)
|
||||
continue
|
||||
}
|
||||
p.resolveDomains(ctxRouter, domains, tlsStore)
|
||||
p.resolveDomains(ctxRouter, domains, traefiktls.DefaultTLSStoreName)
|
||||
}
|
||||
}
|
||||
case <-ctxPool.Done():
|
||||
|
|
|
@ -691,7 +691,7 @@ func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options
|
|||
|
||||
id := makeID(tlsOption.Namespace, tlsOption.Name)
|
||||
// If the name is default, we override the default config.
|
||||
if tlsOption.Name == "default" {
|
||||
if tlsOption.Name == tls.DefaultTLSConfigName {
|
||||
id = tlsOption.Name
|
||||
nsDefault = append(nsDefault, tlsOption.Namespace)
|
||||
}
|
||||
|
@ -710,7 +710,7 @@ func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options
|
|||
}
|
||||
|
||||
if len(nsDefault) > 1 {
|
||||
delete(tlsOptions, "default")
|
||||
delete(tlsOptions, tls.DefaultTLSConfigName)
|
||||
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)
|
||||
// If the name is default, we override the default config.
|
||||
if tlsStore.Name == "default" {
|
||||
if tlsStore.Name == tls.DefaultTLSStoreName {
|
||||
id = tlsStore.Name
|
||||
nsDefault = append(nsDefault, tlsStore.Namespace)
|
||||
}
|
||||
|
@ -763,7 +763,7 @@ func buildTLSStores(ctx context.Context, client Client) map[string]tls.Store {
|
|||
}
|
||||
|
||||
if len(nsDefault) > 1 {
|
||||
delete(tlsStores, "default")
|
||||
delete(tlsStores, tls.DefaultTLSStoreName)
|
||||
log.FromContext(ctx).Errorf("Default TLS Stores defined in multiple namespaces: %v", nsDefault)
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint
|
|||
}
|
||||
|
||||
for key, store := range configuration.TLS.Stores {
|
||||
if key != "default" {
|
||||
if key != tls.DefaultTLSStoreName {
|
||||
key = provider.MakeQualifiedName(pvd, key)
|
||||
} else {
|
||||
defaultTLSStoreProviders = append(defaultTLSStoreProviders, pvd)
|
||||
|
@ -113,16 +113,16 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint
|
|||
|
||||
if len(defaultTLSStoreProviders) > 1 {
|
||||
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 {
|
||||
conf.TLS.Options["default"] = tls.DefaultTLSOptions
|
||||
conf.TLS.Options[tls.DefaultTLSConfigName] = tls.DefaultTLSOptions
|
||||
} else if len(defaultTLSOptionProviders) > 1 {
|
||||
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,
|
||||
// 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
|
||||
|
|
|
@ -18,11 +18,6 @@ import (
|
|||
traefiktls "github.com/traefik/traefik/v2/pkg/tls"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTLSConfigName = "default"
|
||||
defaultTLSStoreName = "default"
|
||||
)
|
||||
|
||||
type middlewareBuilder interface {
|
||||
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.HTTPHandler(handlerHTTP)
|
||||
|
||||
defaultTLSConf, err := m.tlsManager.Get(defaultTLSStoreName, defaultTLSConfigName)
|
||||
defaultTLSConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, traefiktls.DefaultTLSConfigName)
|
||||
if err != nil {
|
||||
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))
|
||||
logger := log.FromContext(ctxRouter)
|
||||
|
||||
tlsOptionsName := defaultTLSConfigName
|
||||
if len(routerHTTPConfig.TLS.Options) > 0 && routerHTTPConfig.TLS.Options != defaultTLSConfigName {
|
||||
tlsOptionsName := traefiktls.DefaultTLSConfigName
|
||||
if len(routerHTTPConfig.TLS.Options) > 0 && routerHTTPConfig.TLS.Options != traefiktls.DefaultTLSConfigName {
|
||||
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 {
|
||||
tlsConf, err := m.tlsManager.Get(defaultTLSStoreName, tlsOptionsName)
|
||||
tlsConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, tlsOptionsName)
|
||||
if err != nil {
|
||||
routerHTTPConfig.AddError(err, true)
|
||||
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 {
|
||||
// Different tlsOptions on the same domain fallback to default
|
||||
tlsOptionsForHost[domain] = defaultTLSConfigName
|
||||
tlsOptionsForHost[domain] = traefiktls.DefaultTLSConfigName
|
||||
} else {
|
||||
tlsOptionsForHost[domain] = tlsOptionsName
|
||||
}
|
||||
|
@ -280,14 +275,14 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
|||
tlsOptionsName := routerConfig.TLS.Options
|
||||
|
||||
if len(tlsOptionsName) == 0 {
|
||||
tlsOptionsName = defaultTLSConfigName
|
||||
tlsOptionsName = traefiktls.DefaultTLSConfigName
|
||||
}
|
||||
|
||||
if tlsOptionsName != defaultTLSConfigName {
|
||||
if tlsOptionsName != traefiktls.DefaultTLSConfigName {
|
||||
tlsOptionsName = provider.GetQualifiedName(ctxRouter, tlsOptionsName)
|
||||
}
|
||||
|
||||
tlsConf, err := m.tlsManager.Get(defaultTLSStoreName, tlsOptionsName)
|
||||
tlsConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, tlsOptionsName)
|
||||
if err != nil {
|
||||
routerConfig.AddError(err, true)
|
||||
logger.Debug(err)
|
||||
|
@ -338,5 +333,5 @@ func findTLSOptionName(tlsOptionsForHost map[string]string, host string) string
|
|||
return tlsOptions
|
||||
}
|
||||
|
||||
return defaultTLSConfigName
|
||||
return traefiktls.DefaultTLSConfigName
|
||||
}
|
||||
|
|
|
@ -69,7 +69,10 @@ func (c CertificateStore) GetAllDomains() []string {
|
|||
}
|
||||
|
||||
// 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))
|
||||
if len(domainToCheck) == 0 {
|
||||
// If no ServerName is provided, Check for local IP address matches
|
||||
|
|
|
@ -15,16 +15,24 @@ import (
|
|||
"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.
|
||||
var DefaultTLSOptions = Options{}
|
||||
|
||||
// Manager is the TLS option/store/configuration factory.
|
||||
type Manager struct {
|
||||
lock sync.RWMutex
|
||||
storesConfig map[string]Store
|
||||
stores map[string]*CertificateStore
|
||||
configs map[string]Options
|
||||
certs []*CertAndStores
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// NewManager creates a new Manager.
|
||||
|
@ -38,6 +46,7 @@ func NewManager() *Manager {
|
|||
}
|
||||
|
||||
// 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) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
@ -46,10 +55,22 @@ func (m *Manager) UpdateConfigs(ctx context.Context, stores map[string]Store, co
|
|||
m.storesConfig = stores
|
||||
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)
|
||||
for storeName, storeConfig := range m.storesConfig {
|
||||
ctxStore := log.With(ctx, log.Str(log.TLSStoreName, storeName))
|
||||
store, err := buildCertificateStore(ctxStore, storeConfig)
|
||||
store, err := buildCertificateStore(ctxStore, storeConfig, storeName)
|
||||
if err != nil {
|
||||
log.FromContext(ctxStore).Errorf("Error while creating certificate store: %v", err)
|
||||
continue
|
||||
|
@ -75,7 +96,12 @@ func (m *Manager) UpdateConfigs(ctx context.Context, stores map[string]Store, co
|
|||
}
|
||||
|
||||
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 err error
|
||||
|
||||
sniStrict := false
|
||||
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)
|
||||
}
|
||||
if err != nil {
|
||||
tlsConfig = &tls.Config{}
|
||||
}
|
||||
|
||||
store := m.getStore(storeName)
|
||||
if store == nil {
|
||||
err = fmt.Errorf("TLS store %s not found", storeName)
|
||||
}
|
||||
acmeTLSStore := m.getStore(tlsalpn01.ACMETLS1Protocol)
|
||||
|
||||
if err == nil {
|
||||
tlsConfig, err = buildTLSConfig(config)
|
||||
if err != nil {
|
||||
tlsConfig = &tls.Config{}
|
||||
}
|
||||
if acmeTLSStore == nil {
|
||||
err = fmt.Errorf("ACME TLS store %s not found", tlsalpn01.ACMETLS1Protocol)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if m.configs[configName].SniStrict {
|
||||
if sniStrict {
|
||||
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
|
||||
}
|
||||
|
||||
// getStore returns the store found for storeName, or nil otherwise.
|
||||
func (m *Manager) getStore(storeName string) *CertificateStore {
|
||||
_, ok := m.stores[storeName]
|
||||
st, ok := m.stores[storeName]
|
||||
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.
|
||||
|
@ -168,7 +200,7 @@ func (m *Manager) GetStore(storeName string) *CertificateStore {
|
|||
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.DynamicCerts.Set(make(map[string]*tls.Certificate))
|
||||
|
||||
|
@ -178,14 +210,21 @@ func buildCertificateStore(ctx context.Context, tlsStore Store) (*CertificateSto
|
|||
return certificateStore, err
|
||||
}
|
||||
certificateStore.DefaultCertificate = cert
|
||||
} else {
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue