Remove acme provider dependency in server

This commit is contained in:
SALLEYRON Julien 2018-04-24 22:40:04 +02:00 committed by Traefiker Bot
parent 9daae9c705
commit b4e3bca6fa
7 changed files with 124 additions and 101 deletions

View file

@ -24,7 +24,6 @@ import (
"github.com/containous/traefik/configuration/router"
"github.com/containous/traefik/job"
"github.com/containous/traefik/log"
"github.com/containous/traefik/provider/acme"
"github.com/containous/traefik/provider/ecs"
"github.com/containous/traefik/provider/kubernetes"
"github.com/containous/traefik/safe"
@ -174,28 +173,47 @@ func runCmd(globalConfiguration *configuration.GlobalConfiguration, configFile s
stats(globalConfiguration)
log.Debugf("Global configuration loaded %s", string(jsonConf))
if acme.IsEnabled() {
store := acme.NewLocalStore(acme.Get().Storage)
acme.Get().Store = &store
providerAggregator := configuration.NewProviderAggregator(globalConfiguration)
acmeprovider := globalConfiguration.InitACMEProvider()
if acmeprovider != nil {
providerAggregator.AddProvider(acmeprovider)
}
entryPoints := map[string]server.EntryPoint{}
for entryPointName, config := range globalConfiguration.EntryPoints {
internalRouter := router.NewInternalRouterAggregator(*globalConfiguration, entryPointName)
if acme.IsEnabled() && acme.Get().HTTPChallenge != nil && acme.Get().HTTPChallenge.EntryPoint == entryPointName {
internalRouter.AddRouter(acme.Get())
}
entryPoints[entryPointName] = server.EntryPoint{
InternalRouter: internalRouter,
entryPoint := server.EntryPoint{
Configuration: config,
}
internalRouter := router.NewInternalRouterAggregator(*globalConfiguration, entryPointName)
if acmeprovider != nil {
if acmeprovider.HTTPChallenge != nil && acmeprovider.HTTPChallenge.EntryPoint == entryPointName {
internalRouter.AddRouter(acmeprovider)
}
svr := server.NewServer(*globalConfiguration, configuration.NewProviderAggregator(globalConfiguration), entryPoints)
if acme.IsEnabled() && acme.Get().OnHostRule {
acme.Get().SetConfigListenerChan(make(chan types.Configuration))
svr.AddListener(acme.Get().ListenConfiguration)
if acmeprovider.EntryPoint == entryPointName && acmeprovider.OnDemand {
entryPoint.OnDemandListener = acmeprovider.ListenRequest
}
entryPoint.CertificateStore = &traefiktls.CertificateStore{
DynamicCerts: &safe.Safe{},
StaticCerts: &safe.Safe{},
}
acmeprovider.SetCertificateStore(*entryPoint.CertificateStore)
}
entryPoint.InternalRouter = internalRouter
entryPoints[entryPointName] = entryPoint
}
svr := server.NewServer(*globalConfiguration, providerAggregator, entryPoints)
if acmeprovider != nil && acmeprovider.OnHostRule {
acmeprovider.SetConfigListenerChan(make(chan types.Configuration))
svr.AddListener(acmeprovider.ListenConfiguration)
}
ctx := cmd.ContextWithSignal(context.Background())

View file

@ -370,11 +370,17 @@ func (gc *GlobalConfiguration) initACMEProvider() {
if gc.ACME.OnDemand {
log.Warn("ACME.OnDemand is deprecated")
}
}
}
// InitACMEProvider create an acme provider from the ACME part of globalConfiguration
func (gc *GlobalConfiguration) InitACMEProvider() *acmeprovider.Provider {
if gc.ACME != nil {
// TODO: Remove when Provider ACME will replace totally ACME
// If provider file, use Provider ACME instead of ACME
if gc.Cluster == nil {
acmeprovider.Get().Configuration = &acmeprovider.Configuration{
provider := &acmeprovider.Provider{}
provider.Configuration = &acmeprovider.Configuration{
OnHostRule: gc.ACME.OnHostRule,
OnDemand: gc.ACME.OnDemand,
Email: gc.ACME.Email,
@ -386,9 +392,15 @@ func (gc *GlobalConfiguration) initACMEProvider() {
CAServer: gc.ACME.CAServer,
EntryPoint: gc.ACME.EntryPoint,
}
store := acmeprovider.NewLocalStore(provider.Storage)
provider.Store = &store
acme.ConvertToNewFormat(provider.Storage)
gc.ACME = nil
return provider
}
}
return nil
}
// ValidateConfiguration validate that configuration is coherent
@ -401,14 +413,6 @@ func (gc *GlobalConfiguration) ValidateConfiguration() {
log.Fatalf("Entrypoint %q has no TLS configuration for ACME configuration", gc.ACME.EntryPoint)
}
}
} else if acmeprovider.IsEnabled() {
if _, ok := gc.EntryPoints[acmeprovider.Get().EntryPoint]; !ok {
log.Fatalf("Unknown entrypoint %q for provider ACME configuration", acmeprovider.Get().EntryPoint)
} else {
if gc.EntryPoints[acmeprovider.Get().EntryPoint].TLS == nil {
log.Fatalf("Entrypoint %q has no TLS configuration for provider ACME configuration", acmeprovider.Get().EntryPoint)
}
}
}
}

View file

@ -4,21 +4,20 @@ import (
"encoding/json"
"reflect"
"github.com/containous/traefik/acme"
"github.com/containous/traefik/log"
"github.com/containous/traefik/provider"
acmeprovider "github.com/containous/traefik/provider/acme"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
)
type providerAggregator struct {
// ProviderAggregator aggregate providers
type ProviderAggregator struct {
providers []provider.Provider
}
// NewProviderAggregator return an aggregate of all the providers configured in GlobalConfiguration
func NewProviderAggregator(gc *GlobalConfiguration) provider.Provider {
provider := providerAggregator{}
func NewProviderAggregator(gc *GlobalConfiguration) ProviderAggregator {
provider := ProviderAggregator{}
if gc.Docker != nil {
provider.providers = append(provider.providers, gc.Docker)
}
@ -67,17 +66,16 @@ func NewProviderAggregator(gc *GlobalConfiguration) provider.Provider {
if gc.ServiceFabric != nil {
provider.providers = append(provider.providers, gc.ServiceFabric)
}
if acmeprovider.IsEnabled() {
provider.providers = append(provider.providers, acmeprovider.Get())
acme.ConvertToNewFormat(acmeprovider.Get().Storage)
}
if len(provider.providers) == 1 {
return provider.providers[0]
}
return provider
}
func (p providerAggregator) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
// AddProvider add a provider in the providers map
func (p *ProviderAggregator) AddProvider(provider provider.Provider) {
p.providers = append(p.providers, provider)
}
// Provide call the provide method of every providers
func (p ProviderAggregator) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
for _, p := range p.providers {
providerType := reflect.TypeOf(p)
jsonConf, err := json.Marshal(p)

View file

@ -20,7 +20,7 @@ import (
"github.com/containous/traefik/log"
"github.com/containous/traefik/rules"
"github.com/containous/traefik/safe"
traefikTLS "github.com/containous/traefik/tls"
traefiktls "github.com/containous/traefik/tls"
"github.com/containous/traefik/types"
"github.com/pkg/errors"
acme "github.com/xenolf/lego/acmev2"
@ -30,7 +30,6 @@ import (
var (
// OSCPMustStaple enables OSCP stapling as from https://github.com/xenolf/lego/issues/270
OSCPMustStaple = false
provider = &Provider{}
)
// Configuration holds ACME configuration provided by users
@ -56,8 +55,7 @@ type Provider struct {
client *acme.Client
certsChan chan *Certificate
configurationChan chan<- types.ConfigMessage
dynamicCerts *safe.Safe
staticCerts map[string]*tls.Certificate
certificateStore traefiktls.CertificateStore
clientMutex sync.Mutex
configFromListenerChan chan types.Configuration
pool *safe.Pool
@ -81,16 +79,6 @@ type HTTPChallenge struct {
EntryPoint string `description:"HTTP challenge EntryPoint"`
}
// Get returns the provider instance
func Get() *Provider {
return provider
}
// IsEnabled returns true if the provider instance and its configuration are not nil, otherwise false
func IsEnabled() bool {
return provider != nil && provider.Configuration != nil
}
// SetConfigListenerChan initializes the configFromListenerChan
func (p *Provider) SetConfigListenerChan(configFromListenerChan chan types.Configuration) {
p.configFromListenerChan = configFromListenerChan
@ -196,14 +184,9 @@ func (p *Provider) watchNewDomains() {
})
}
// SetDynamicCertificates allow to initialize dynamicCerts map
func (p *Provider) SetDynamicCertificates(safe *safe.Safe) {
p.dynamicCerts = safe
}
// SetStaticCertificates allow to initialize staticCerts map
func (p *Provider) SetStaticCertificates(staticCerts map[string]*tls.Certificate) {
p.staticCerts = staticCerts
// SetCertificateStore allow to initialize certificate store
func (p *Provider) SetCertificateStore(certificateStore traefiktls.CertificateStore) {
p.certificateStore = certificateStore
}
func (p *Provider) resolveCertificate(domain types.Domain, domainFromConfigurationFile bool) (*acme.CertificateResource, error) {
@ -424,13 +407,13 @@ func (p *Provider) refreshCertificates() {
Configuration: &types.Configuration{
Backends: map[string]*types.Backend{},
Frontends: map[string]*types.Frontend{},
TLS: []*traefikTLS.Configuration{},
TLS: []*traefiktls.Configuration{},
},
}
for _, cert := range p.certificates {
certificate := &traefikTLS.Certificate{CertFile: traefikTLS.FileOrContent(cert.Certificate), KeyFile: traefikTLS.FileOrContent(cert.Key)}
config.Configuration.TLS = append(config.Configuration.TLS, &traefikTLS.Configuration{Certificate: certificate, EntryPoints: []string{p.EntryPoint}})
certificate := &traefiktls.Certificate{CertFile: traefiktls.FileOrContent(cert.Certificate), KeyFile: traefiktls.FileOrContent(cert.Key)}
config.Configuration.TLS = append(config.Configuration.TLS, &traefiktls.Configuration{Certificate: certificate, EntryPoints: []string{p.EntryPoint}})
}
p.configurationChan <- config
}
@ -507,33 +490,23 @@ func (p *Provider) AddRoutes(router *mux.Router) {
// from static and dynamic provided certificates
func (p *Provider) getUncheckedDomains(domainsToCheck []string, checkConfigurationDomains bool) []string {
log.Debugf("Looking for provided certificate(s) to validate %q...", domainsToCheck)
var allCerts []string
var allDomains []string
// Get static certificates
for domains := range p.staticCerts {
allCerts = append(allCerts, domains)
}
// Get dynamic certificates
if p.dynamicCerts != nil && p.dynamicCerts.Get() != nil {
for domains := range p.dynamicCerts.Get().(map[string]*tls.Certificate) {
allCerts = append(allCerts, domains)
}
}
allDomains = p.certificateStore.GetAllDomains()
// Get ACME certificates
for _, certificate := range p.certificates {
allCerts = append(allCerts, strings.Join(certificate.Domain.ToStrArray(), ","))
allDomains = append(allDomains, strings.Join(certificate.Domain.ToStrArray(), ","))
}
// Get Configuration Domains
if checkConfigurationDomains {
for i := 0; i < len(p.Domains); i++ {
allCerts = append(allCerts, strings.Join(p.Domains[i].ToStrArray(), ","))
allDomains = append(allDomains, strings.Join(p.Domains[i].ToStrArray(), ","))
}
}
return searchUncheckedDomains(domainsToCheck, allCerts)
return searchUncheckedDomains(domainsToCheck, allDomains)
}
func searchUncheckedDomains(domainsToCheck []string, existentDomains []string) []string {

View file

@ -5,6 +5,7 @@ import (
"testing"
"github.com/containous/traefik/safe"
traefiktls "github.com/containous/traefik/tls"
"github.com/containous/traefik/types"
"github.com/stretchr/testify/assert"
)
@ -25,7 +26,7 @@ func TestGetUncheckedCertificates(t *testing.T) {
testCases := []struct {
desc string
dynamicCerts *safe.Safe
staticCerts map[string]*tls.Certificate
staticCerts *safe.Safe
acmeCertificates []*Certificate
domains []string
expectedDomains []string
@ -44,7 +45,7 @@ func TestGetUncheckedCertificates(t *testing.T) {
{
desc: "wildcard already exists in static certificates",
domains: []string{"*.traefik.wtf"},
staticCerts: wildcardMap,
staticCerts: wildcardSafe,
expectedDomains: nil,
},
{
@ -71,7 +72,7 @@ func TestGetUncheckedCertificates(t *testing.T) {
{
desc: "domain CN already exists in static certificates and SANs to generate",
domains: []string{"traefik.wtf", "foo.traefik.wtf"},
staticCerts: domainMap,
staticCerts: domainSafe,
expectedDomains: []string{"foo.traefik.wtf"},
},
{
@ -93,7 +94,7 @@ func TestGetUncheckedCertificates(t *testing.T) {
{
desc: "domain already exists in static certificates",
domains: []string{"traefik.wtf"},
staticCerts: domainMap,
staticCerts: domainSafe,
expectedDomains: nil,
},
{
@ -115,7 +116,7 @@ func TestGetUncheckedCertificates(t *testing.T) {
{
desc: "domain matched by wildcard in static certificates",
domains: []string{"who.traefik.wtf", "foo.traefik.wtf"},
staticCerts: wildcardMap,
staticCerts: wildcardSafe,
expectedDomains: nil,
},
{
@ -146,8 +147,10 @@ func TestGetUncheckedCertificates(t *testing.T) {
t.Parallel()
acmeProvider := Provider{
dynamicCerts: test.dynamicCerts,
staticCerts: test.staticCerts,
certificateStore: traefiktls.CertificateStore{
DynamicCerts: test.dynamicCerts,
StaticCerts: test.staticCerts,
},
certificates: test.acmeCertificates,
}

View file

@ -35,7 +35,6 @@ import (
"github.com/containous/traefik/middlewares/redirect"
"github.com/containous/traefik/middlewares/tracing"
"github.com/containous/traefik/provider"
"github.com/containous/traefik/provider/acme"
"github.com/containous/traefik/rules"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/server/cookie"
@ -83,6 +82,8 @@ type Server struct {
type EntryPoint struct {
InternalRouter types.InternalRouter
Configuration *configuration.EntryPoint
OnDemandListener func(string) (*tls.Certificate, error)
CertificateStore *traefiktls.CertificateStore
}
type serverEntryPoints map[string]*serverEntryPoint
@ -502,11 +503,6 @@ func (s *Server) AddListener(listener func(types.Configuration)) {
s.configurationListeners = append(s.configurationListeners, listener)
}
// SetOnDemandListener adds a new listener function used when a request is caught
func (s *serverEntryPoint) SetOnDemandListener(listener func(string) (*tls.Certificate, error)) {
s.onDemandListener = listener
}
// loadHTTPSConfiguration add/delete HTTPS certificate managed dynamically
func (s *Server) loadHTTPSConfiguration(configurations types.Configurations, defaultEntryPoints configuration.DefaultEntryPoints) (map[string]map[string]*tls.Certificate, error) {
newEPCertificates := make(map[string]map[string]*tls.Certificate)
@ -693,14 +689,8 @@ func (s *Server) createTLSConfig(entryPointName string, tlsOption *traefiktls.TL
// in each certificate and populates the config.NameToCertificate map.
config.BuildNameToCertificate()
if acme.IsEnabled() {
if entryPointName == acme.Get().EntryPoint {
acme.Get().SetStaticCertificates(config.NameToCertificate)
acme.Get().SetDynamicCertificates(&s.serverEntryPoints[entryPointName].certs)
if acme.Get().OnDemand {
s.serverEntryPoints[entryPointName].SetOnDemandListener(acme.Get().ListenRequest)
}
}
if s.entryPoints[entryPointName].CertificateStore != nil {
s.entryPoints[entryPointName].CertificateStore.StaticCerts.Set(config.NameToCertificate)
}
// Set the minimum TLS version if set in the config TOML
@ -839,9 +829,13 @@ func buildServerTimeouts(globalConfig configuration.GlobalConfiguration) (readTi
func (s *Server) buildEntryPoints() map[string]*serverEntryPoint {
serverEntryPoints := make(map[string]*serverEntryPoint)
for entryPointName := range s.entryPoints {
for entryPointName, entryPoint := range s.entryPoints {
serverEntryPoints[entryPointName] = &serverEntryPoint{
httpRouter: middlewares.NewHandlerSwitcher(s.buildDefaultHTTPRouter()),
onDemandListener: entryPoint.OnDemandListener,
}
if entryPoint.CertificateStore != nil {
serverEntryPoints[entryPointName].certs = *entryPoint.CertificateStore.DynamicCerts
}
}
return serverEntryPoints

33
tls/certificate_store.go Normal file
View file

@ -0,0 +1,33 @@
package tls
import (
"crypto/tls"
"github.com/containous/traefik/safe"
)
// CertificateStore store for dynamic and static certificates
type CertificateStore struct {
DynamicCerts *safe.Safe
StaticCerts *safe.Safe
}
// GetAllDomains return a slice with all the certificate domain
func (c CertificateStore) GetAllDomains() []string {
var allCerts []string
// Get static certificates
if c.StaticCerts != nil && c.StaticCerts.Get() != nil {
for domains := range c.StaticCerts.Get().(map[string]*tls.Certificate) {
allCerts = append(allCerts, domains)
}
}
// Get dynamic certificates
if c.DynamicCerts != nil && c.DynamicCerts.Get() != nil {
for domains := range c.DynamicCerts.Get().(map[string]*tls.Certificate) {
allCerts = append(allCerts, domains)
}
}
return allCerts
}