Add support for custom CA certificates by certificate resolver
This commit is contained in:
parent
e222d5cb2f
commit
ac1dad3d14
6 changed files with 210 additions and 4 deletions
|
@ -6,9 +6,13 @@ import (
|
|||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -43,6 +47,10 @@ type Configuration struct {
|
|||
EAB *EAB `description:"External Account Binding to use." json:"eab,omitempty" toml:"eab,omitempty" yaml:"eab,omitempty"`
|
||||
CertificatesDuration int `description:"Certificates' duration in hours." json:"certificatesDuration,omitempty" toml:"certificatesDuration,omitempty" yaml:"certificatesDuration,omitempty" export:"true"`
|
||||
|
||||
CACertificates []string `description:"Specify the paths to PEM encoded CA Certificates that can be used to authenticate an ACME server with an HTTPS certificate not issued by a CA in the system-wide trusted root list." json:"caCertificates,omitempty" toml:"caCertificates,omitempty" yaml:"caCertificates,omitempty"`
|
||||
CASystemCertPool bool `description:"Define if the certificates pool must use a copy of the system cert pool." json:"caSystemCertPool,omitempty" toml:"caSystemCertPool,omitempty" yaml:"caSystemCertPool,omitempty" export:"true"`
|
||||
CAServerName string `description:"Specify the CA server name that can be used to authenticate an ACME server with an HTTPS certificate not issued by a CA in the system-wide trusted root list." json:"caServerName,omitempty" toml:"caServerName,omitempty" yaml:"caServerName,omitempty" export:"true"`
|
||||
|
||||
DNSChallenge *DNSChallenge `description:"Activate DNS-01 Challenge." json:"dnsChallenge,omitempty" toml:"dnsChallenge,omitempty" yaml:"dnsChallenge,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
HTTPChallenge *HTTPChallenge `description:"Activate HTTP-01 Challenge." json:"httpChallenge,omitempty" toml:"httpChallenge,omitempty" yaml:"httpChallenge,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
TLSChallenge *TLSChallenge `description:"Activate TLS-ALPN-01 Challenge." json:"tlsChallenge,omitempty" toml:"tlsChallenge,omitempty" yaml:"tlsChallenge,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
|
@ -261,6 +269,11 @@ func (p *Provider) getClient() (*lego.Client, error) {
|
|||
config.Certificate.KeyType = GetKeyType(ctx, p.KeyType)
|
||||
config.UserAgent = fmt.Sprintf("containous-traefik/%s", version.Version)
|
||||
|
||||
config.HTTPClient, err = p.createHTTPClient()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating HTTP client: %w", err)
|
||||
}
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -340,6 +353,64 @@ func (p *Provider) getClient() (*lego.Client, error) {
|
|||
return p.client, nil
|
||||
}
|
||||
|
||||
func (p *Provider) createHTTPClient() (*http.Client, error) {
|
||||
tlsConfig, err := p.createClientTLSConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating client TLS config: %w", err)
|
||||
}
|
||||
|
||||
return &http.Client{
|
||||
Timeout: 2 * time.Minute,
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
TLSHandshakeTimeout: 30 * time.Second,
|
||||
ResponseHeaderTimeout: 30 * time.Second,
|
||||
TLSClientConfig: tlsConfig,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) createClientTLSConfig() (*tls.Config, error) {
|
||||
if len(p.CACertificates) > 0 || p.CAServerName != "" {
|
||||
certPool, err := lego.CreateCertPool(p.CACertificates, p.CASystemCertPool)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating cert pool with custom certificates: %w", err)
|
||||
}
|
||||
|
||||
return &tls.Config{
|
||||
ServerName: p.CAServerName,
|
||||
RootCAs: certPool,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Compatibility layer with the lego.
|
||||
// https://github.com/go-acme/lego/blob/834a9089f143e3407b3f5c8b93a0e285ba231fe2/lego/client_config.go#L24-L34
|
||||
// https://github.com/go-acme/lego/blob/834a9089f143e3407b3f5c8b93a0e285ba231fe2/lego/client_config.go#L97-L113
|
||||
|
||||
serverName := os.Getenv("LEGO_CA_SERVER_NAME")
|
||||
customCACertsPath := os.Getenv("LEGO_CA_CERTIFICATES")
|
||||
|
||||
if customCACertsPath == "" && serverName == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
useSystemCertPool, _ := strconv.ParseBool(os.Getenv("LEGO_CA_SYSTEM_CERT_POOL"))
|
||||
|
||||
certPool, err := lego.CreateCertPool(strings.Split(customCACertsPath, string(os.PathListSeparator)), useSystemCertPool)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating cert pool: %w", err)
|
||||
}
|
||||
|
||||
return &tls.Config{
|
||||
ServerName: serverName,
|
||||
RootCAs: certPool,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) initAccount(ctx context.Context) (*Account, error) {
|
||||
if p.account == nil || len(p.account.Email) == 0 {
|
||||
var err error
|
||||
|
@ -424,8 +495,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
|
|||
|
||||
if len(route.TLS.Domains) > 0 {
|
||||
domains := deleteUnnecessaryDomains(ctxRouter, route.TLS.Domains)
|
||||
for i := range len(domains) {
|
||||
domain := domains[i]
|
||||
for _, domain := range domains {
|
||||
safe.Go(func() {
|
||||
dom, cert, err := p.resolveCertificate(ctx, domain, traefiktls.DefaultTLSStoreName)
|
||||
if err != nil {
|
||||
|
@ -461,8 +531,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
|
|||
|
||||
if len(route.TLS.Domains) > 0 {
|
||||
domains := deleteUnnecessaryDomains(ctxRouter, route.TLS.Domains)
|
||||
for i := range len(domains) {
|
||||
domain := domains[i]
|
||||
for _, domain := range domains {
|
||||
safe.Go(func() {
|
||||
dom, cert, err := p.resolveCertificate(ctx, domain, traefiktls.DefaultTLSStoreName)
|
||||
if err != nil {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue