2019-03-14 08:30:04 +00:00
package tls
import (
2019-09-13 17:28:04 +00:00
"context"
2019-03-14 08:30:04 +00:00
"crypto/tls"
"crypto/x509"
2019-07-12 15:50:04 +00:00
"errors"
2019-03-14 08:30:04 +00:00
"fmt"
2022-09-13 18:34:08 +00:00
"strings"
2019-03-14 08:30:04 +00:00
"sync"
2022-09-13 18:34:08 +00:00
"github.com/go-acme/lego/v4/challenge/dns01"
2020-09-04 08:52:03 +00:00
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
2019-03-14 08:30:04 +00:00
"github.com/sirupsen/logrus"
2020-09-16 13:46:04 +00:00
"github.com/traefik/traefik/v2/pkg/log"
"github.com/traefik/traefik/v2/pkg/tls/generate"
"github.com/traefik/traefik/v2/pkg/types"
2019-03-14 08:30:04 +00:00
)
2021-06-14 08:06:05 +00:00
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"
)
2019-11-14 15:40:05 +00:00
// DefaultTLSOptions the default TLS options.
2021-08-20 16:20:06 +00:00
var DefaultTLSOptions = Options {
// ensure http2 enabled
ALPNProtocols : [ ] string { "h2" , "http/1.1" , tlsalpn01 . ACMETLS1Protocol } ,
2022-09-08 08:56:08 +00:00
MinVersion : "VersionTLS12" ,
CipherSuites : getCipherSuites ( ) ,
}
func getCipherSuites ( ) [ ] string {
gsc := tls . CipherSuites ( )
ciphers := make ( [ ] string , len ( gsc ) )
for idx , cs := range gsc {
ciphers [ idx ] = cs . Name
}
return ciphers
2021-08-20 16:20:06 +00:00
}
2019-11-14 15:40:05 +00:00
2020-05-11 10:06:07 +00:00
// Manager is the TLS option/store/configuration factory.
2019-03-14 08:30:04 +00:00
type Manager struct {
2021-06-14 08:06:05 +00:00
lock sync . RWMutex
2020-10-29 14:40:04 +00:00
storesConfig map [ string ] Store
stores map [ string ] * CertificateStore
configs map [ string ] Options
certs [ ] * CertAndStores
2019-03-14 08:30:04 +00:00
}
2020-05-11 10:06:07 +00:00
// NewManager creates a new Manager.
2019-03-14 08:30:04 +00:00
func NewManager ( ) * Manager {
2019-11-14 15:40:05 +00:00
return & Manager {
stores : map [ string ] * CertificateStore { } ,
configs : map [ string ] Options {
"default" : DefaultTLSOptions ,
} ,
}
2019-03-14 08:30:04 +00:00
}
2020-05-11 10:06:07 +00:00
// UpdateConfigs updates the TLS* configuration options.
2021-06-14 08:06:05 +00:00
// It initializes the default TLS store, and the TLS store for the ACME challenges.
2019-09-13 17:28:04 +00:00
func ( m * Manager ) UpdateConfigs ( ctx context . Context , stores map [ string ] Store , configs map [ string ] Options , certs [ ] * CertAndStores ) {
2019-03-14 08:30:04 +00:00
m . lock . Lock ( )
defer m . lock . Unlock ( )
m . configs = configs
m . storesConfig = stores
m . certs = certs
2021-06-14 08:06:05 +00:00
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 { }
}
2019-03-14 08:30:04 +00:00
storesCertificates := make ( map [ string ] map [ string ] * tls . Certificate )
for _ , conf := range certs {
if len ( conf . Stores ) == 0 {
if log . GetLevel ( ) >= logrus . DebugLevel {
2019-09-13 17:28:04 +00:00
log . FromContext ( ctx ) . Debugf ( "No store is defined to add the certificate %s, it will be added to the default store." ,
2019-03-14 08:30:04 +00:00
conf . Certificate . GetTruncatedCertificateName ( ) )
}
2022-09-13 18:34:08 +00:00
conf . Stores = [ ] string { DefaultTLSStoreName }
2019-03-14 08:30:04 +00:00
}
2022-09-13 18:34:08 +00:00
2019-03-14 08:30:04 +00:00
for _ , store := range conf . Stores {
2019-09-13 17:28:04 +00:00
ctxStore := log . With ( ctx , log . Str ( log . TLSStoreName , store ) )
2022-09-13 18:34:08 +00:00
if _ , ok := m . storesConfig [ store ] ; ! ok {
m . storesConfig [ store ] = Store { }
}
err := conf . Certificate . AppendCertificate ( storesCertificates , store )
if err != nil {
2019-09-13 17:28:04 +00:00
log . FromContext ( ctxStore ) . Errorf ( "Unable to append certificate %s to store: %v" , conf . Certificate . GetTruncatedCertificateName ( ) , err )
2019-03-14 08:30:04 +00:00
}
}
}
2022-09-13 18:34:08 +00:00
m . stores = make ( map [ string ] * CertificateStore )
for storeName , storeConfig := range m . storesConfig {
st := NewCertificateStore ( )
m . stores [ storeName ] = st
if certs , ok := storesCertificates [ storeName ] ; ok {
st . DynamicCerts . Set ( certs )
}
// a default cert for the ACME store does not make any sense, so generating one is a waste.
if storeName == tlsalpn01 . ACMETLS1Protocol {
continue
}
ctxStore := log . With ( ctx , log . Str ( log . TLSStoreName , storeName ) )
certificate , err := getDefaultCertificate ( ctxStore , storeConfig , st )
if err != nil {
log . FromContext ( ctxStore ) . Errorf ( "Error while creating certificate store: %v" , err )
2021-06-14 08:06:05 +00:00
}
2022-09-13 18:34:08 +00:00
st . DefaultCertificate = certificate
}
}
// sanitizeDomains sanitizes the domain definition Main and SANS,
// and returns them as a slice.
// This func apply the same sanitization as the ACME provider do before resolving certificates.
func sanitizeDomains ( domain types . Domain ) ( [ ] string , error ) {
domains := domain . ToStrArray ( )
if len ( domains ) == 0 {
return nil , errors . New ( "no domain was given" )
}
var cleanDomains [ ] string
for _ , domain := range domains {
canonicalDomain := types . CanonicalDomain ( domain )
cleanDomain := dns01 . UnFqdn ( canonicalDomain )
cleanDomains = append ( cleanDomains , cleanDomain )
2019-03-14 08:30:04 +00:00
}
2022-09-13 18:34:08 +00:00
return cleanDomains , nil
2019-03-14 08:30:04 +00:00
}
2020-05-11 10:06:07 +00:00
// Get gets the TLS configuration to use for a given store / configuration.
2020-07-07 12:42:03 +00:00
func ( m * Manager ) Get ( storeName , configName string ) ( * tls . Config , error ) {
2019-03-14 08:30:04 +00:00
m . lock . RLock ( )
defer m . lock . RUnlock ( )
2021-06-14 08:06:05 +00:00
sniStrict := false
2019-06-17 16:14:08 +00:00
config , ok := m . configs [ configName ]
2022-12-06 17:28:05 +00:00
if ! ok {
return nil , fmt . Errorf ( "unknown TLS options: %s" , configName )
2021-06-14 08:06:05 +00:00
}
2022-12-06 17:28:05 +00:00
sniStrict = config . SniStrict
tlsConfig , err := buildTLSConfig ( config )
2021-06-14 08:06:05 +00:00
if err != nil {
2022-12-06 17:28:05 +00:00
return nil , fmt . Errorf ( "building TLS config: %w" , err )
2019-06-17 16:14:08 +00:00
}
2019-03-14 08:30:04 +00:00
store := m . getStore ( storeName )
2021-06-14 08:06:05 +00:00
if store == nil {
err = fmt . Errorf ( "TLS store %s not found" , storeName )
}
2020-10-29 14:40:04 +00:00
acmeTLSStore := m . getStore ( tlsalpn01 . ACMETLS1Protocol )
2022-12-06 17:28:05 +00:00
if acmeTLSStore == nil && err == nil {
2021-06-14 08:06:05 +00:00
err = fmt . Errorf ( "ACME TLS store %s not found" , tlsalpn01 . ACMETLS1Protocol )
2019-03-14 08:30:04 +00:00
}
tlsConfig . GetCertificate = func ( clientHello * tls . ClientHelloInfo ) ( * tls . Certificate , error ) {
domainToCheck := types . CanonicalDomain ( clientHello . ServerName )
2020-10-29 14:40:04 +00:00
if isACMETLS ( clientHello ) {
certificate := acmeTLSStore . GetBestCertificate ( clientHello )
if certificate == nil {
2022-03-28 16:18:08 +00:00
log . WithoutContext ( ) . Debugf ( "TLS: no certificate for TLSALPN challenge: %s" , domainToCheck )
2022-12-06 17:28:05 +00:00
// We want the user to eventually get the (alertUnrecognizedName) "unrecognized name" error.
// Unfortunately, if we returned an error here,
// since we can't use the unexported error (errNoCertificates) that our caller (config.getCertificate in crypto/tls) uses as a sentinel,
// it would report an (alertInternalError) "internal error" instead of an alertUnrecognizedName.
// Which is why we return no error, and we let the caller detect that there's actually no certificate,
// and fall back into the flow that will report the desired error.
2022-03-28 16:18:08 +00:00
// https://cs.opensource.google/go/go/+/dev.boringcrypto.go1.17:src/crypto/tls/common.go;l=1058
return nil , nil
2019-03-14 08:30:04 +00:00
}
2020-10-29 14:40:04 +00:00
return certificate , nil
2019-03-14 08:30:04 +00:00
}
bestCertificate := store . GetBestCertificate ( clientHello )
if bestCertificate != nil {
return bestCertificate , nil
}
2021-06-14 08:06:05 +00:00
if sniStrict {
2022-03-28 16:18:08 +00:00
log . WithoutContext ( ) . Debugf ( "TLS: strict SNI enabled - No certificate found for domain: %q, closing connection" , domainToCheck )
// Same comment as above, as in the isACMETLS case.
return nil , nil
2019-03-14 08:30:04 +00:00
}
2022-05-19 15:12:08 +00:00
if store == nil {
log . WithoutContext ( ) . Errorf ( "TLS: No certificate store found with this name: %q, closing connection" , storeName )
// Same comment as above, as in the isACMETLS case.
return nil , nil
}
2019-03-14 08:30:04 +00:00
log . WithoutContext ( ) . Debugf ( "Serving default certificate for request: %q" , domainToCheck )
return store . DefaultCertificate , nil
}
2019-07-19 09:52:04 +00:00
return tlsConfig , err
2019-03-14 08:30:04 +00:00
}
2023-02-23 15:14:06 +00:00
// GetServerCertificates returns all certificates from the default store,
// as well as the user-defined default certificate (if it exists).
func ( m * Manager ) GetServerCertificates ( ) [ ] * x509 . Certificate {
2020-12-18 17:44:03 +00:00
var certificates [ ] * x509 . Certificate
2023-02-23 15:14:06 +00:00
// The default store is the only relevant, because it is the only one configurable.
defaultStore , ok := m . stores [ DefaultTLSStoreName ]
if ! ok || defaultStore == nil {
return certificates
}
2020-12-18 17:44:03 +00:00
// We iterate over all the certificates.
2023-02-23 15:14:06 +00:00
if defaultStore . DynamicCerts != nil && defaultStore . DynamicCerts . Get ( ) != nil {
for _ , cert := range defaultStore . DynamicCerts . Get ( ) . ( map [ string ] * tls . Certificate ) {
x509Cert , err := x509 . ParseCertificate ( cert . Certificate [ 0 ] )
if err != nil {
continue
2020-12-18 17:44:03 +00:00
}
2023-02-23 15:14:06 +00:00
certificates = append ( certificates , x509Cert )
2020-12-18 17:44:03 +00:00
}
}
2023-02-23 15:14:06 +00:00
if defaultStore . DefaultCertificate != nil {
x509Cert , err := x509 . ParseCertificate ( defaultStore . DefaultCertificate . Certificate [ 0 ] )
if err != nil {
return certificates
}
// Excluding the generated Traefik default certificate.
if x509Cert . Subject . CommonName == generate . DefaultDomain {
return certificates
}
certificates = append ( certificates , x509Cert )
}
2020-12-18 17:44:03 +00:00
return certificates
}
2021-06-14 08:06:05 +00:00
// getStore returns the store found for storeName, or nil otherwise.
2019-03-14 08:30:04 +00:00
func ( m * Manager ) getStore ( storeName string ) * CertificateStore {
2021-06-14 08:06:05 +00:00
st , ok := m . stores [ storeName ]
2019-03-14 08:30:04 +00:00
if ! ok {
2021-06-14 08:06:05 +00:00
return nil
2019-03-14 08:30:04 +00:00
}
2021-06-14 08:06:05 +00:00
return st
2019-03-14 08:30:04 +00:00
}
2020-05-11 10:06:07 +00:00
// GetStore gets the certificate store of a given name.
2019-03-14 08:30:04 +00:00
func ( m * Manager ) GetStore ( storeName string ) * CertificateStore {
m . lock . RLock ( )
defer m . lock . RUnlock ( )
return m . getStore ( storeName )
}
2022-09-13 18:34:08 +00:00
func getDefaultCertificate ( ctx context . Context , tlsStore Store , st * CertificateStore ) ( * tls . Certificate , error ) {
2019-03-14 08:30:04 +00:00
if tlsStore . DefaultCertificate != nil {
cert , err := buildDefaultCertificate ( tlsStore . DefaultCertificate )
if err != nil {
2022-09-13 18:34:08 +00:00
return nil , err
2019-03-14 08:30:04 +00:00
}
2021-06-14 08:06:05 +00:00
2022-09-13 18:34:08 +00:00
return cert , nil
2021-06-14 08:06:05 +00:00
}
2022-09-13 18:34:08 +00:00
defaultCert , err := generate . DefaultCertificate ( )
2021-06-14 08:06:05 +00:00
if err != nil {
2022-09-13 18:34:08 +00:00
return nil , err
2019-03-14 08:30:04 +00:00
}
2022-09-13 18:34:08 +00:00
if tlsStore . DefaultGeneratedCert != nil && tlsStore . DefaultGeneratedCert . Domain != nil && tlsStore . DefaultGeneratedCert . Resolver != "" {
domains , err := sanitizeDomains ( * tlsStore . DefaultGeneratedCert . Domain )
if err != nil {
return defaultCert , fmt . Errorf ( "falling back to the internal generated certificate because invalid domains: %w" , err )
}
defaultACMECert := st . GetCertificate ( domains )
if defaultACMECert == nil {
return defaultCert , fmt . Errorf ( "unable to find certificate for domains %q: falling back to the internal generated certificate" , strings . Join ( domains , "," ) )
}
return defaultACMECert , nil
}
log . FromContext ( ctx ) . Debug ( "No default certificate, fallback to the internal generated certificate" )
return defaultCert , nil
2019-03-14 08:30:04 +00:00
}
2020-05-11 10:06:07 +00:00
// creates a TLS config that allows terminating HTTPS for multiple domains using SNI.
2019-06-27 21:58:03 +00:00
func buildTLSConfig ( tlsOption Options ) ( * tls . Config , error ) {
2021-08-20 16:20:06 +00:00
conf := & tls . Config {
NextProtos : tlsOption . ALPNProtocols ,
}
2019-03-14 08:30:04 +00:00
2019-07-12 15:50:04 +00:00
if len ( tlsOption . ClientAuth . CAFiles ) > 0 {
2019-03-14 08:30:04 +00:00
pool := x509 . NewCertPool ( )
2019-07-12 15:50:04 +00:00
for _ , caFile := range tlsOption . ClientAuth . CAFiles {
2019-03-14 08:30:04 +00:00
data , err := caFile . Read ( )
if err != nil {
return nil , err
}
ok := pool . AppendCertsFromPEM ( data )
if ! ok {
2019-07-12 15:50:04 +00:00
if caFile . IsPath ( ) {
return nil , fmt . Errorf ( "invalid certificate(s) in %s" , caFile )
}
return nil , errors . New ( "invalid certificate(s) content" )
2019-03-14 08:30:04 +00:00
}
}
conf . ClientCAs = pool
2019-07-12 15:50:04 +00:00
conf . ClientAuth = tls . RequireAndVerifyClientCert
}
clientAuthType := tlsOption . ClientAuth . ClientAuthType
if len ( clientAuthType ) > 0 {
if conf . ClientCAs == nil && ( clientAuthType == "VerifyClientCertIfGiven" ||
clientAuthType == "RequireAndVerifyClientCert" ) {
return nil , fmt . Errorf ( "invalid clientAuthType: %s, CAFiles is required" , clientAuthType )
}
switch clientAuthType {
case "NoClientCert" :
conf . ClientAuth = tls . NoClientCert
case "RequestClientCert" :
conf . ClientAuth = tls . RequestClientCert
case "RequireAnyClientCert" :
conf . ClientAuth = tls . RequireAnyClientCert
case "VerifyClientCertIfGiven" :
2019-03-14 08:30:04 +00:00
conf . ClientAuth = tls . VerifyClientCertIfGiven
2019-07-12 15:50:04 +00:00
case "RequireAndVerifyClientCert" :
2019-03-14 08:30:04 +00:00
conf . ClientAuth = tls . RequireAndVerifyClientCert
2019-07-12 15:50:04 +00:00
default :
return nil , fmt . Errorf ( "unknown client auth type %q" , clientAuthType )
2019-03-14 08:30:04 +00:00
}
}
2019-11-03 14:54:04 +00:00
// Set the minimum TLS version if set in the config
2019-03-14 08:30:04 +00:00
if minConst , exists := MinVersion [ tlsOption . MinVersion ] ; exists {
conf . MinVersion = minConst
}
2019-10-29 11:58:05 +00:00
// Set the maximum TLS version if set in the config TOML
if maxConst , exists := MaxVersion [ tlsOption . MaxVersion ] ; exists {
conf . MaxVersion = maxConst
}
2019-11-03 14:54:04 +00:00
// Set the list of CipherSuites if set in the config
2019-03-14 08:30:04 +00:00
if tlsOption . CipherSuites != nil {
// if our list of CipherSuites is defined in the entryPoint config, we can re-initialize the suites list as empty
conf . CipherSuites = make ( [ ] uint16 , 0 )
for _ , cipher := range tlsOption . CipherSuites {
if cipherConst , exists := CipherSuites [ cipher ] ; exists {
conf . CipherSuites = append ( conf . CipherSuites , cipherConst )
} else {
// CipherSuite listed in the toml does not exist in our listed
return nil , fmt . Errorf ( "invalid CipherSuite: %s" , cipher )
}
}
}
2019-11-03 14:54:04 +00:00
// Set the list of CurvePreferences/CurveIDs if set in the config
if tlsOption . CurvePreferences != nil {
conf . CurvePreferences = make ( [ ] tls . CurveID , 0 )
// if our list of CurvePreferences/CurveIDs is defined in the config, we can re-initialize the list as empty
for _ , curve := range tlsOption . CurvePreferences {
if curveID , exists := CurveIDs [ curve ] ; exists {
conf . CurvePreferences = append ( conf . CurvePreferences , curveID )
} else {
// CurveID listed in the toml does not exist in our listed
return nil , fmt . Errorf ( "invalid CurveID in curvePreferences: %s" , curve )
}
}
}
2019-03-14 08:30:04 +00:00
return conf , nil
}
func buildDefaultCertificate ( defaultCertificate * Certificate ) ( * tls . Certificate , error ) {
certFile , err := defaultCertificate . CertFile . Read ( )
if err != nil {
2020-05-11 10:06:07 +00:00
return nil , fmt . Errorf ( "failed to get cert file content: %w" , err )
2019-03-14 08:30:04 +00:00
}
keyFile , err := defaultCertificate . KeyFile . Read ( )
if err != nil {
2020-05-11 10:06:07 +00:00
return nil , fmt . Errorf ( "failed to get key file content: %w" , err )
2019-03-14 08:30:04 +00:00
}
cert , err := tls . X509KeyPair ( certFile , keyFile )
if err != nil {
2020-05-11 10:06:07 +00:00
return nil , fmt . Errorf ( "failed to load X509 key pair: %w" , err )
2019-03-14 08:30:04 +00:00
}
return & cert , nil
}
2020-10-08 11:34:04 +00:00
func isACMETLS ( clientHello * tls . ClientHelloInfo ) bool {
for _ , proto := range clientHello . SupportedProtos {
if proto == tlsalpn01 . ACMETLS1Protocol {
return true
}
}
return false
}