acme: new HTTP and TLS challenges implementations.
This commit is contained in:
parent
49cdb67ddc
commit
05333b9579
13 changed files with 398 additions and 254 deletions
|
@ -1,6 +1,7 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/server/provider"
|
||||
|
@ -77,7 +78,13 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint
|
|||
}
|
||||
|
||||
if configuration.TLS != nil {
|
||||
conf.TLS.Certificates = append(conf.TLS.Certificates, configuration.TLS.Certificates...)
|
||||
for _, cert := range configuration.TLS.Certificates {
|
||||
if containsACMETLS1(cert.Stores) && pvd != "tlsalpn.acme" {
|
||||
continue
|
||||
}
|
||||
|
||||
conf.TLS.Certificates = append(conf.TLS.Certificates, cert)
|
||||
}
|
||||
|
||||
for key, store := range configuration.TLS.Stores {
|
||||
if key != "default" {
|
||||
|
@ -160,3 +167,13 @@ func applyModel(cfg dynamic.Configuration) dynamic.Configuration {
|
|||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func containsACMETLS1(stores []string) bool {
|
||||
for _, store := range stores {
|
||||
if store == tlsalpn01.ACMETLS1Protocol {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package server
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/tls"
|
||||
|
@ -122,6 +123,55 @@ func Test_mergeConfiguration(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func Test_mergeConfiguration_tlsCertificates(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
given dynamic.Configurations
|
||||
expected []*tls.CertAndStores
|
||||
}{
|
||||
{
|
||||
desc: "Skip temp certificates from another provider than tlsalpn",
|
||||
given: dynamic.Configurations{
|
||||
"provider-1": &dynamic.Configuration{
|
||||
TLS: &dynamic.TLSConfiguration{
|
||||
Certificates: []*tls.CertAndStores{
|
||||
{Certificate: tls.Certificate{}, Stores: []string{tlsalpn01.ACMETLS1Protocol}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "Allows tlsalpn provider to give certificates",
|
||||
given: dynamic.Configurations{
|
||||
"tlsalpn.acme": &dynamic.Configuration{
|
||||
TLS: &dynamic.TLSConfiguration{
|
||||
Certificates: []*tls.CertAndStores{{
|
||||
Certificate: tls.Certificate{CertFile: "foo", KeyFile: "bar"},
|
||||
Stores: []string{tlsalpn01.ACMETLS1Protocol},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []*tls.CertAndStores{{
|
||||
Certificate: tls.Certificate{CertFile: "foo", KeyFile: "bar"},
|
||||
Stores: []string{tlsalpn01.ACMETLS1Protocol},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := mergeConfiguration(test.given, []string{"defaultEP"})
|
||||
assert.Equal(t, test.expected, actual.TLS.Certificates)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_mergeConfiguration_tlsOptions(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
|
|
@ -50,7 +50,7 @@ func TestReuseService(t *testing.T) {
|
|||
|
||||
roundTripperManager := service.NewRoundTripperManager()
|
||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager)
|
||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager, nil)
|
||||
tlsManager := tls.NewManager()
|
||||
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(staticConfig, metrics.NewVoidRegistry(), nil), nil)
|
||||
|
@ -186,7 +186,7 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
|||
|
||||
roundTripperManager := service.NewRoundTripperManager()
|
||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager)
|
||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager, nil)
|
||||
tlsManager := tls.NewManager()
|
||||
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(staticConfig, metrics.NewVoidRegistry(), nil), nil)
|
||||
|
@ -227,7 +227,7 @@ func TestInternalServices(t *testing.T) {
|
|||
|
||||
roundTripperManager := service.NewRoundTripperManager()
|
||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager)
|
||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager, nil)
|
||||
tlsManager := tls.NewManager()
|
||||
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(staticConfig, metrics.NewVoidRegistry(), nil), nil)
|
||||
|
|
|
@ -6,8 +6,6 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
||||
)
|
||||
|
||||
type serviceManager interface {
|
||||
|
@ -22,22 +20,19 @@ type InternalHandlers struct {
|
|||
rest http.Handler
|
||||
prometheus http.Handler
|
||||
ping http.Handler
|
||||
acmeHTTP http.Handler
|
||||
serviceManager
|
||||
}
|
||||
|
||||
// NewInternalHandlers creates a new InternalHandlers.
|
||||
func NewInternalHandlers(api func(configuration *runtime.Configuration) http.Handler, configuration *runtime.Configuration, rest, metricsHandler, pingHandler, dashboard http.Handler, next serviceManager) *InternalHandlers {
|
||||
var apiHandler http.Handler
|
||||
if api != nil {
|
||||
apiHandler = api(configuration)
|
||||
}
|
||||
|
||||
func NewInternalHandlers(next serviceManager, apiHandler, rest, metricsHandler, pingHandler, dashboard, acmeHTTP http.Handler) *InternalHandlers {
|
||||
return &InternalHandlers{
|
||||
api: apiHandler,
|
||||
dashboard: dashboard,
|
||||
rest: rest,
|
||||
prometheus: metricsHandler,
|
||||
ping: pingHandler,
|
||||
acmeHTTP: acmeHTTP,
|
||||
serviceManager: next,
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +58,12 @@ func (m *InternalHandlers) get(serviceName string) (http.Handler, error) {
|
|||
rw.WriteHeader(http.StatusTeapot)
|
||||
}), nil
|
||||
|
||||
case "acme-http@internal":
|
||||
if m.acmeHTTP == nil {
|
||||
return nil, errors.New("HTTP challenge is not enabled")
|
||||
}
|
||||
return m.acmeHTTP, nil
|
||||
|
||||
case "api@internal":
|
||||
if m.api == nil {
|
||||
return nil, errors.New("api is not enabled")
|
||||
|
|
|
@ -21,16 +21,18 @@ type ManagerFactory struct {
|
|||
dashboardHandler http.Handler
|
||||
metricsHandler http.Handler
|
||||
pingHandler http.Handler
|
||||
acmeHTTPHandler http.Handler
|
||||
|
||||
routinesPool *safe.Pool
|
||||
}
|
||||
|
||||
// NewManagerFactory creates a new ManagerFactory.
|
||||
func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *safe.Pool, metricsRegistry metrics.Registry, roundTripperManager *RoundTripperManager) *ManagerFactory {
|
||||
func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *safe.Pool, metricsRegistry metrics.Registry, roundTripperManager *RoundTripperManager, acmeHTTPHandler http.Handler) *ManagerFactory {
|
||||
factory := &ManagerFactory{
|
||||
metricsRegistry: metricsRegistry,
|
||||
routinesPool: routinesPool,
|
||||
roundTripperManager: roundTripperManager,
|
||||
acmeHTTPHandler: acmeHTTPHandler,
|
||||
}
|
||||
|
||||
if staticConfiguration.API != nil {
|
||||
|
@ -62,5 +64,11 @@ func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *s
|
|||
// Build creates a service manager.
|
||||
func (f *ManagerFactory) Build(configuration *runtime.Configuration) *InternalHandlers {
|
||||
svcManager := NewManager(configuration.Services, f.metricsRegistry, f.routinesPool, f.roundTripperManager)
|
||||
return NewInternalHandlers(f.api, configuration, f.restHandler, f.metricsHandler, f.pingHandler, f.dashboardHandler, svcManager)
|
||||
|
||||
var apiHandler http.Handler
|
||||
if f.api != nil {
|
||||
apiHandler = f.api(configuration)
|
||||
}
|
||||
|
||||
return NewInternalHandlers(svcManager, apiHandler, f.restHandler, f.metricsHandler, f.pingHandler, f.dashboardHandler, f.acmeHTTPHandler)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue