2016-03-21 10:10:18 +00:00
package acme
import (
2016-08-16 15:26:10 +00:00
"context"
2016-03-21 10:10:18 +00:00
"crypto/tls"
"errors"
"fmt"
2016-12-30 08:21:13 +00:00
"io/ioutil"
fmtlog "log"
2018-01-15 15:04:05 +00:00
"net"
"net/http"
2016-12-30 08:21:13 +00:00
"os"
2018-03-26 12:12:03 +00:00
"reflect"
2016-12-30 08:21:13 +00:00
"strings"
"time"
2016-10-14 14:04:09 +00:00
"github.com/BurntSushi/ty/fun"
2016-09-23 16:27:01 +00:00
"github.com/cenk/backoff"
2018-01-15 15:04:05 +00:00
"github.com/containous/flaeg"
"github.com/containous/mux"
2016-08-18 12:20:11 +00:00
"github.com/containous/staert"
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/log"
2018-03-05 19:54:04 +00:00
acmeprovider "github.com/containous/traefik/provider/acme"
2016-08-18 12:20:11 +00:00
"github.com/containous/traefik/safe"
2017-11-09 11:16:03 +00:00
"github.com/containous/traefik/tls/generate"
2016-10-14 14:04:09 +00:00
"github.com/containous/traefik/types"
2016-12-09 13:37:39 +00:00
"github.com/eapache/channels"
2018-04-06 15:04:03 +00:00
acme "github.com/xenolf/lego/acmev2"
2016-10-14 00:33:01 +00:00
"github.com/xenolf/lego/providers/dns"
2016-03-21 10:10:18 +00:00
)
2016-10-14 00:33:01 +00:00
var (
// OSCPMustStaple enables OSCP stapling as from https://github.com/xenolf/lego/issues/270
OSCPMustStaple = false
)
2016-03-21 10:10:18 +00:00
// ACME allows to connect to lets encrypt and retrieve certs
2018-03-06 13:50:03 +00:00
// Deprecated Please use provider/acme/Provider
2016-03-21 10:10:18 +00:00
type ACME struct {
2018-03-05 19:54:04 +00:00
Email string ` description:"Email address used for registration" `
Domains [ ] types . Domain ` description:"SANs (alternative domains) to each main domain using format: --acme.domains='main.com,san1.com,san2.com' --acme.domains='main.net,san1.net,san2.net'" `
Storage string ` description:"File or key used for certificates storage." `
2018-05-14 07:52:03 +00:00
StorageFile string // Deprecated
OnDemand bool ` description:"(Deprecated) Enable on demand certificate generation. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate." ` //deprecated
2018-03-05 19:54:04 +00:00
OnHostRule bool ` description:"Enable certificate generation on frontends Host rules." `
CAServer string ` description:"CA server to use." `
EntryPoint string ` description:"Entrypoint to proxy acme challenge to." `
2018-05-16 09:44:03 +00:00
KeyType string ` description:"KeyType used for generating certificate private key. Allow value 'EC256', 'EC384', 'RSA2048', 'RSA4096', 'RSA8192'. Default to 'RSA4096'" `
2018-04-11 15:16:07 +00:00
DNSChallenge * acmeprovider . DNSChallenge ` description:"Activate DNS-01 Challenge" `
2018-03-05 19:54:04 +00:00
HTTPChallenge * acmeprovider . HTTPChallenge ` description:"Activate HTTP-01 Challenge" `
2018-05-14 07:52:03 +00:00
DNSProvider string ` description:"(Deprecated) Activate DNS-01 Challenge" ` // Deprecated
DelayDontCheckDNS flaeg . Duration ` description:"(Deprecated) Assume DNS propagates after a delay in seconds rather than finding and querying nameservers." ` // Deprecated
2018-03-05 19:54:04 +00:00
ACMELogging bool ` description:"Enable debug logging of ACME actions." `
2018-05-17 10:32:03 +00:00
OverrideCertificates bool ` description:"Enable to override certificates in key-value store when using storeconfig" `
2018-01-15 15:04:05 +00:00
client * acme . Client
defaultCertificate * tls . Certificate
store cluster . Store
challengeHTTPProvider * challengeHTTPProvider
checkOnDemandDomain func ( domain string ) bool
jobs * channels . InfiniteChannel
TLSConfig * tls . Config ` description:"TLS config in case wildcard certs are used" `
dynamicCerts * safe . Safe
}
2016-08-16 17:13:18 +00:00
func ( a * ACME ) init ( ) error {
2016-10-14 00:33:01 +00:00
if a . ACMELogging {
acme . Logger = fmtlog . New ( os . Stderr , "legolog: " , fmtlog . LstdFlags )
} else {
acme . Logger = fmtlog . New ( ioutil . Discard , "" , 0 )
}
2016-08-16 17:13:18 +00:00
// no certificates in TLS config, so we add a default one
2017-11-09 11:16:03 +00:00
cert , err := generate . DefaultCertificate ( )
2016-08-16 17:13:18 +00:00
if err != nil {
return err
}
a . defaultCertificate = cert
2018-01-15 15:04:05 +00:00
2016-12-09 13:37:39 +00:00
a . jobs = channels . NewInfiniteChannel ( )
2016-08-16 17:13:18 +00:00
return nil
}
2016-03-21 10:10:18 +00:00
2018-01-15 15:04:05 +00:00
// AddRoutes add routes on internal router
func ( a * ACME ) AddRoutes ( router * mux . Router ) {
router . Methods ( http . MethodGet ) .
Path ( acme . HTTP01ChallengePath ( "{token}" ) ) .
Handler ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
if a . challengeHTTPProvider == nil {
rw . WriteHeader ( http . StatusNotFound )
return
}
vars := mux . Vars ( req )
if token , ok := vars [ "token" ] ; ok {
domain , _ , err := net . SplitHostPort ( req . Host )
if err != nil {
log . Debugf ( "Unable to split host and port: %v. Fallback to request host." , err )
domain = req . Host
}
tokenValue := a . challengeHTTPProvider . getTokenValue ( token , domain )
if len ( tokenValue ) > 0 {
rw . WriteHeader ( http . StatusOK )
rw . Write ( tokenValue )
return
}
}
rw . WriteHeader ( http . StatusNotFound )
} ) )
}
2016-08-18 12:20:11 +00:00
// CreateClusterConfig creates a tls.config using ACME configuration in cluster mode
2017-11-09 11:16:03 +00:00
func ( a * ACME ) CreateClusterConfig ( leadership * cluster . Leadership , tlsConfig * tls . Config , certs * safe . Safe , checkOnDemandDomain func ( domain string ) bool ) error {
2016-08-16 17:13:18 +00:00
err := a . init ( )
if err != nil {
return err
2016-03-21 10:10:18 +00:00
}
2016-08-18 12:20:11 +00:00
if len ( a . Storage ) == 0 {
return errors . New ( "Empty Store, please provide a key for certs storage" )
2016-08-16 17:13:18 +00:00
}
2016-08-18 12:20:11 +00:00
a . checkOnDemandDomain = checkOnDemandDomain
2017-11-09 11:16:03 +00:00
a . dynamicCerts = certs
2016-08-16 17:13:18 +00:00
tlsConfig . Certificates = append ( tlsConfig . Certificates , * a . defaultCertificate )
2016-08-18 12:20:11 +00:00
tlsConfig . GetCertificate = a . getCertificate
2017-01-04 21:23:18 +00:00
a . TLSConfig = tlsConfig
2016-09-29 13:36:52 +00:00
listener := func ( object cluster . Object ) error {
account := object . ( * Account )
account . Init ( )
if ! leadership . IsLeader ( ) {
a . client , err = a . buildACMEClient ( account )
if err != nil {
log . Errorf ( "Error building ACME client %+v: %s" , object , err . Error ( ) )
}
}
return nil
}
2016-08-18 12:20:11 +00:00
datastore , err := cluster . NewDataStore (
2016-11-16 08:56:52 +00:00
leadership . Pool . Ctx ( ) ,
2016-08-18 12:20:11 +00:00
staert . KvSource {
Store : leadership . Store ,
2016-09-29 13:36:52 +00:00
Prefix : a . Storage ,
2016-08-18 12:20:11 +00:00
} ,
2016-11-16 08:56:52 +00:00
& Account { } ,
2016-09-29 13:36:52 +00:00
listener )
2016-08-18 12:20:11 +00:00
if err != nil {
return err
}
a . store = datastore
ticker := time . NewTicker ( 24 * time . Hour )
leadership . Pool . AddGoCtx ( func ( ctx context . Context ) {
2017-05-26 15:03:14 +00:00
log . Info ( "Starting ACME renew job..." )
defer log . Info ( "Stopped ACME renew job..." )
2016-10-27 14:09:28 +00:00
for {
select {
case <- ctx . Done ( ) :
return
case <- ticker . C :
2016-12-09 13:37:39 +00:00
a . renewCertificates ( )
2016-08-18 12:20:11 +00:00
}
}
} )
2018-01-15 15:04:05 +00:00
leadership . AddListener ( a . leadershipListener )
return nil
}
func ( a * ACME ) leadershipListener ( elected bool ) error {
if elected {
_ , err := a . store . Load ( )
if err != nil {
return err
}
2018-03-26 12:12:03 +00:00
2018-01-15 15:04:05 +00:00
transaction , object , err := a . store . Begin ( )
if err != nil {
return err
}
2018-03-26 12:12:03 +00:00
2018-01-15 15:04:05 +00:00
account := object . ( * Account )
account . Init ( )
2018-03-26 12:12:03 +00:00
2018-01-15 15:04:05 +00:00
var needRegister bool
if account == nil || len ( account . Email ) == 0 {
2018-03-26 12:12:03 +00:00
domainsCerts := DomainsCertificates { Certs : [ ] * DomainsCertificate { } }
if account != nil {
domainsCerts = account . DomainsCertificate
}
2018-05-16 09:44:03 +00:00
account , err = NewAccount ( a . Email , domainsCerts . Certs , a . KeyType )
2016-08-18 12:20:11 +00:00
if err != nil {
return err
}
2018-03-26 12:12:03 +00:00
2018-01-15 15:04:05 +00:00
needRegister = true
}
2018-03-26 12:12:03 +00:00
2018-01-15 15:04:05 +00:00
a . client , err = a . buildACMEClient ( account )
if err != nil {
return err
}
if needRegister {
// New users will need to register; be sure to save it
log . Debug ( "Register..." )
2018-03-26 12:12:03 +00:00
reg , err := a . client . Register ( true )
2016-08-18 12:20:11 +00:00
if err != nil {
return err
}
2018-03-26 12:12:03 +00:00
2018-01-15 15:04:05 +00:00
account . Registration = reg
2016-08-18 12:20:11 +00:00
}
2018-03-26 12:12:03 +00:00
2018-01-15 15:04:05 +00:00
err = transaction . Commit ( account )
if err != nil {
return err
}
a . retrieveCertificates ( )
a . renewCertificates ( )
a . runJobs ( )
}
2016-08-16 17:13:18 +00:00
return nil
}
2016-03-21 10:10:18 +00:00
2016-08-18 12:20:11 +00:00
func ( a * ACME ) getCertificate ( clientHello * tls . ClientHelloInfo ) ( * tls . Certificate , error ) {
2016-10-14 14:04:09 +00:00
domain := types . CanonicalDomain ( clientHello . ServerName )
2016-08-18 12:20:11 +00:00
account := a . store . Get ( ) . ( * Account )
2017-06-19 11:22:41 +00:00
2018-02-26 10:38:03 +00:00
if providedCertificate := a . getProvidedCertificate ( domain ) ; providedCertificate != nil {
2017-06-19 11:22:41 +00:00
return providedCertificate , nil
2017-01-04 21:23:18 +00:00
}
2017-06-19 11:22:41 +00:00
2016-10-14 14:04:09 +00:00
if domainCert , ok := account . DomainsCertificate . getCertificateForDomain ( domain ) ; ok {
log . Debugf ( "ACME got domain cert %s" , domain )
2016-08-18 12:20:11 +00:00
return domainCert . tlsCert , nil
}
if a . OnDemand {
2016-10-14 14:04:09 +00:00
if a . checkOnDemandDomain != nil && ! a . checkOnDemandDomain ( domain ) {
2016-08-18 12:20:11 +00:00
return nil , nil
}
return a . loadCertificateOnDemand ( clientHello )
}
2018-01-10 14:20:04 +00:00
log . Debugf ( "No certificate found or generated for %s" , domain )
2016-08-18 12:20:11 +00:00
return nil , nil
}
func ( a * ACME ) retrieveCertificates ( ) {
2016-12-09 13:37:39 +00:00
a . jobs . In ( ) <- func ( ) {
2017-05-26 15:03:14 +00:00
log . Info ( "Retrieving ACME certificates..." )
2018-03-26 12:12:03 +00:00
a . deleteUnnecessaryDomains ( )
for i := 0 ; i < len ( a . Domains ) ; i ++ {
domain := a . Domains [ i ]
2016-12-09 13:37:39 +00:00
// check if cert isn't already loaded
account := a . store . Get ( ) . ( * Account )
if _ , exists := account . DomainsCertificate . exists ( domain ) ; ! exists {
2018-03-26 12:12:03 +00:00
var domains [ ] string
2016-12-09 13:37:39 +00:00
domains = append ( domains , domain . Main )
domains = append ( domains , domain . SANs ... )
2018-03-26 12:12:03 +00:00
domains , err := a . getValidDomains ( domains , true )
if err != nil {
log . Errorf ( "Error validating ACME certificate for domain %q: %s" , domains , err )
continue
}
2016-12-09 13:37:39 +00:00
certificateResource , err := a . getDomainsCertificates ( domains )
if err != nil {
2018-03-26 12:12:03 +00:00
log . Errorf ( "Error getting ACME certificate for domain %q: %s" , domains , err )
2016-12-09 13:37:39 +00:00
continue
}
2018-03-26 12:12:03 +00:00
2016-12-09 13:37:39 +00:00
transaction , object , err := a . store . Begin ( )
if err != nil {
2018-03-26 12:12:03 +00:00
log . Errorf ( "Error creating ACME store transaction from domain %q: %s" , domain , err )
2016-12-09 13:37:39 +00:00
continue
}
2018-03-26 12:12:03 +00:00
2016-12-09 13:37:39 +00:00
account = object . ( * Account )
_ , err = account . DomainsCertificate . addCertificateForDomains ( certificateResource , domain )
if err != nil {
2018-03-26 12:12:03 +00:00
log . Errorf ( "Error adding ACME certificate for domain %q: %s" , domains , err )
2016-12-09 13:37:39 +00:00
continue
}
2016-08-18 12:20:11 +00:00
2016-12-09 13:37:39 +00:00
if err = transaction . Commit ( account ) ; err != nil {
2018-03-26 12:12:03 +00:00
log . Errorf ( "Error Saving ACME account %+v: %s" , account , err )
2016-12-09 13:37:39 +00:00
continue
}
2016-03-22 00:32:02 +00:00
}
}
2018-03-26 12:12:03 +00:00
2017-05-26 15:03:14 +00:00
log . Info ( "Retrieved ACME certificates" )
2016-03-22 00:32:02 +00:00
}
}
2016-12-09 13:37:39 +00:00
func ( a * ACME ) renewCertificates ( ) {
a . jobs . In ( ) <- func ( ) {
2017-12-05 14:42:03 +00:00
log . Info ( "Testing certificate renew..." )
2016-12-09 13:37:39 +00:00
account := a . store . Get ( ) . ( * Account )
for _ , certificateResource := range account . DomainsCertificate . Certs {
if certificateResource . needRenew ( ) {
2017-12-05 14:42:03 +00:00
log . Infof ( "Renewing certificate from LE : %+v" , certificateResource . Domains )
renewedACMECert , err := a . renewACMECertificate ( certificateResource )
2016-12-09 13:37:39 +00:00
if err != nil {
2017-12-05 14:42:03 +00:00
log . Errorf ( "Error renewing certificate from LE: %v" , err )
2016-12-09 13:37:39 +00:00
continue
}
2017-12-05 14:42:03 +00:00
operation := func ( ) error {
2017-12-27 13:22:02 +00:00
return a . storeRenewedCertificate ( certificateResource , renewedACMECert )
2016-12-09 13:37:39 +00:00
}
2017-12-05 14:42:03 +00:00
notify := func ( err error , time time . Duration ) {
log . Warnf ( "Renewed certificate storage error: %v, retrying in %s" , err , time )
2016-12-09 13:37:39 +00:00
}
2017-12-05 14:42:03 +00:00
ebo := backoff . NewExponentialBackOff ( )
ebo . MaxElapsedTime = 60 * time . Second
err = backoff . RetryNotify ( safe . OperationWithRecover ( operation ) , ebo , notify )
2016-12-09 13:37:39 +00:00
if err != nil {
2017-12-05 14:42:03 +00:00
log . Errorf ( "Datastore cannot sync: %v" , err )
2016-12-09 13:37:39 +00:00
continue
}
2016-03-21 10:10:18 +00:00
}
}
}
}
2017-12-05 14:42:03 +00:00
func ( a * ACME ) renewACMECertificate ( certificateResource * DomainsCertificate ) ( * Certificate , error ) {
renewedCert , err := a . client . RenewCertificate ( acme . CertificateResource {
Domain : certificateResource . Certificate . Domain ,
CertURL : certificateResource . Certificate . CertURL ,
CertStableURL : certificateResource . Certificate . CertStableURL ,
PrivateKey : certificateResource . Certificate . PrivateKey ,
Certificate : certificateResource . Certificate . Certificate ,
} , true , OSCPMustStaple )
if err != nil {
return nil , err
}
log . Infof ( "Renewed certificate from LE: %+v" , certificateResource . Domains )
return & Certificate {
Domain : renewedCert . Domain ,
CertURL : renewedCert . CertURL ,
CertStableURL : renewedCert . CertStableURL ,
PrivateKey : renewedCert . PrivateKey ,
Certificate : renewedCert . Certificate ,
} , nil
}
2017-12-27 13:22:02 +00:00
func ( a * ACME ) storeRenewedCertificate ( certificateResource * DomainsCertificate , renewedACMECert * Certificate ) error {
2017-12-05 14:42:03 +00:00
transaction , object , err := a . store . Begin ( )
if err != nil {
return fmt . Errorf ( "error during transaction initialization for renewing certificate: %v" , err )
}
log . Infof ( "Renewing certificate in data store : %+v " , certificateResource . Domains )
2017-12-27 13:22:02 +00:00
account := object . ( * Account )
2017-12-05 14:42:03 +00:00
err = account . DomainsCertificate . renewCertificates ( renewedACMECert , certificateResource . Domains )
if err != nil {
return fmt . Errorf ( "error renewing certificate in datastore: %v " , err )
}
log . Infof ( "Commit certificate renewed in data store : %+v" , certificateResource . Domains )
if err = transaction . Commit ( account ) ; err != nil {
return fmt . Errorf ( "error saving ACME account %+v: %v" , account , err )
}
oldAccount := a . store . Get ( ) . ( * Account )
for _ , oldCertificateResource := range oldAccount . DomainsCertificate . Certs {
if oldCertificateResource . Domains . Main == certificateResource . Domains . Main && strings . Join ( oldCertificateResource . Domains . SANs , "," ) == strings . Join ( certificateResource . Domains . SANs , "," ) && certificateResource . Certificate != renewedACMECert {
return fmt . Errorf ( "renewed certificate not stored: %+v" , certificateResource . Domains )
}
}
log . Infof ( "Certificate successfully renewed in data store: %+v" , certificateResource . Domains )
return nil
}
2018-01-15 15:04:05 +00:00
func dnsOverrideDelay ( delay flaeg . Duration ) error {
2016-10-14 00:33:01 +00:00
var err error
if delay > 0 {
2018-01-15 15:04:05 +00:00
log . Debugf ( "Delaying %d rather than validating DNS propagation" , delay )
2016-10-14 00:33:01 +00:00
acme . PreCheckDNS = func ( _ , _ string ) ( bool , error ) {
2018-01-15 15:04:05 +00:00
time . Sleep ( time . Duration ( delay ) )
2016-10-14 00:33:01 +00:00
return true , nil
}
} else if delay < 0 {
2018-01-15 15:04:05 +00:00
err = fmt . Errorf ( "invalid negative DelayBeforeCheck: %d" , delay )
2016-10-14 00:33:01 +00:00
}
return err
}
2016-08-18 12:20:11 +00:00
func ( a * ACME ) buildACMEClient ( account * Account ) ( * acme . Client , error ) {
2017-05-26 15:03:14 +00:00
log . Debug ( "Building ACME client..." )
2018-03-26 12:12:03 +00:00
caServer := "https://acme-v02.api.letsencrypt.org/directory"
2016-03-21 10:10:18 +00:00
if len ( a . CAServer ) > 0 {
caServer = a . CAServer
}
2018-05-16 09:44:03 +00:00
client , err := acme . NewClient ( caServer , account , account . KeyType )
2016-08-18 12:20:11 +00:00
if err != nil {
return nil , err
}
2016-10-14 00:33:01 +00:00
2018-01-15 15:04:05 +00:00
if a . DNSChallenge != nil && len ( a . DNSChallenge . Provider ) > 0 {
log . Debugf ( "Using DNS Challenge provider: %s" , a . DNSChallenge . Provider )
2016-10-14 00:33:01 +00:00
2018-01-15 15:04:05 +00:00
err = dnsOverrideDelay ( a . DNSChallenge . DelayBeforeCheck )
2016-10-14 00:33:01 +00:00
if err != nil {
return nil , err
}
var provider acme . ChallengeProvider
2018-01-15 15:04:05 +00:00
provider , err = dns . NewDNSChallengeProviderByName ( a . DNSChallenge . Provider )
2016-10-14 00:33:01 +00:00
if err != nil {
return nil , err
}
2018-03-26 12:12:03 +00:00
client . ExcludeChallenges ( [ ] acme . Challenge { acme . HTTP01 } )
2016-10-14 00:33:01 +00:00
err = client . SetChallengeProvider ( acme . DNS01 , provider )
2018-01-15 15:04:05 +00:00
} else if a . HTTPChallenge != nil && len ( a . HTTPChallenge . EntryPoint ) > 0 {
2018-03-05 19:54:04 +00:00
log . Debug ( "Using HTTP Challenge provider." )
2018-03-26 12:12:03 +00:00
client . ExcludeChallenges ( [ ] acme . Challenge { acme . DNS01 } )
2018-01-15 15:04:05 +00:00
a . challengeHTTPProvider = & challengeHTTPProvider { store : a . store }
err = client . SetChallengeProvider ( acme . HTTP01 , a . challengeHTTPProvider )
2016-10-14 00:33:01 +00:00
} else {
2018-03-06 13:50:03 +00:00
return nil , errors . New ( "ACME challenge not specified, please select HTTP or DNS Challenge" )
2016-10-14 00:33:01 +00:00
}
2016-03-21 10:10:18 +00:00
if err != nil {
return nil , err
}
return client , nil
}
2016-08-05 18:42:45 +00:00
func ( a * ACME ) loadCertificateOnDemand ( clientHello * tls . ClientHelloInfo ) ( * tls . Certificate , error ) {
2016-10-14 14:04:09 +00:00
domain := types . CanonicalDomain ( clientHello . ServerName )
2016-08-18 12:20:11 +00:00
account := a . store . Get ( ) . ( * Account )
2016-10-14 14:04:09 +00:00
if certificateResource , ok := account . DomainsCertificate . getCertificateForDomain ( domain ) ; ok {
2016-03-21 10:10:18 +00:00
return certificateResource . tlsCert , nil
}
2016-10-14 14:04:09 +00:00
certificate , err := a . getDomainsCertificates ( [ ] string { domain } )
2016-03-21 10:10:18 +00:00
if err != nil {
return nil , err
}
2016-10-14 14:04:09 +00:00
log . Debugf ( "Got certificate on demand for domain %s" , domain )
2016-08-18 12:20:11 +00:00
transaction , object , err := a . store . Begin ( )
2016-03-21 10:10:18 +00:00
if err != nil {
return nil , err
}
2016-08-18 12:20:11 +00:00
account = object . ( * Account )
2018-03-05 19:54:04 +00:00
cert , err := account . DomainsCertificate . addCertificateForDomains ( certificate , types . Domain { Main : domain } )
2016-08-18 12:20:11 +00:00
if err != nil {
return nil , err
}
if err = transaction . Commit ( account ) ; err != nil {
2016-03-21 10:10:18 +00:00
return nil , err
}
return cert . tlsCert , nil
}
2016-08-05 18:42:45 +00:00
// LoadCertificateForDomains loads certificates from ACME for given domains
func ( a * ACME ) LoadCertificateForDomains ( domains [ ] string ) {
2016-12-09 13:37:39 +00:00
a . jobs . In ( ) <- func ( ) {
2017-06-19 11:22:41 +00:00
log . Debugf ( "LoadCertificateForDomains %v..." , domains )
2018-03-26 12:12:03 +00:00
domains , err := a . getValidDomains ( domains , false )
if err != nil {
log . Errorf ( "Error getting valid domain: %v" , err )
2017-06-19 11:22:41 +00:00
return
}
2016-09-23 16:27:01 +00:00
operation := func ( ) error {
if a . client == nil {
2017-06-27 12:42:12 +00:00
return errors . New ( "ACME client still not built" )
2016-09-23 16:27:01 +00:00
}
return nil
}
notify := func ( err error , time time . Duration ) {
log . Errorf ( "Error getting ACME client: %v, retrying in %s" , err , time )
}
ebo := backoff . NewExponentialBackOff ( )
ebo . MaxElapsedTime = 30 * time . Second
2018-03-26 12:12:03 +00:00
err = backoff . RetryNotify ( safe . OperationWithRecover ( operation ) , ebo , notify )
2016-09-23 16:27:01 +00:00
if err != nil {
log . Errorf ( "Error getting ACME client: %v" , err )
return
}
2016-08-18 12:20:11 +00:00
account := a . store . Get ( ) . ( * Account )
2018-02-26 10:38:03 +00:00
// Check provided certificates
uncheckedDomains := a . getUncheckedDomains ( domains , account )
if len ( uncheckedDomains ) == 0 {
2016-08-05 18:42:45 +00:00
return
}
2018-02-26 10:38:03 +00:00
certificate , err := a . getDomainsCertificates ( uncheckedDomains )
2016-08-05 18:42:45 +00:00
if err != nil {
2018-02-26 10:38:03 +00:00
log . Errorf ( "Error getting ACME certificates %+v : %v" , uncheckedDomains , err )
2016-08-05 18:42:45 +00:00
return
}
2018-02-26 10:38:03 +00:00
log . Debugf ( "Got certificate for domains %+v" , uncheckedDomains )
2016-08-18 12:20:11 +00:00
transaction , object , err := a . store . Begin ( )
if err != nil {
2018-02-26 10:38:03 +00:00
log . Errorf ( "Error creating transaction %+v : %v" , uncheckedDomains , err )
2016-08-18 12:20:11 +00:00
return
}
2018-03-05 19:54:04 +00:00
var domain types . Domain
2018-02-26 10:38:03 +00:00
if len ( uncheckedDomains ) > 1 {
2018-03-05 19:54:04 +00:00
domain = types . Domain { Main : uncheckedDomains [ 0 ] , SANs : uncheckedDomains [ 1 : ] }
2018-02-26 10:38:03 +00:00
} else {
2018-03-05 19:54:04 +00:00
domain = types . Domain { Main : uncheckedDomains [ 0 ] }
2018-02-26 10:38:03 +00:00
}
2016-08-18 12:20:11 +00:00
account = object . ( * Account )
_ , err = account . DomainsCertificate . addCertificateForDomains ( certificate , domain )
2016-08-05 18:42:45 +00:00
if err != nil {
2018-02-26 10:38:03 +00:00
log . Errorf ( "Error adding ACME certificates %+v : %v" , uncheckedDomains , err )
2016-08-05 18:42:45 +00:00
return
}
2016-08-18 12:20:11 +00:00
if err = transaction . Commit ( account ) ; err != nil {
log . Errorf ( "Error Saving ACME account %+v: %v" , account , err )
2016-08-05 18:42:45 +00:00
return
}
2016-12-09 13:37:39 +00:00
}
2016-08-05 18:42:45 +00:00
}
2017-06-19 11:22:41 +00:00
// Get provided certificate which check a domains list (Main and SANs)
2017-11-09 11:16:03 +00:00
// from static and dynamic provided certificates
2018-02-26 10:38:03 +00:00
func ( a * ACME ) getProvidedCertificate ( domains string ) * tls . Certificate {
2018-01-10 14:20:04 +00:00
log . Debugf ( "Looking for provided certificate to validate %s..." , domains )
2017-11-09 11:16:03 +00:00
cert := searchProvidedCertificateForDomains ( domains , a . TLSConfig . NameToCertificate )
if cert == nil && a . dynamicCerts != nil && a . dynamicCerts . Get ( ) != nil {
2018-03-05 19:54:04 +00:00
cert = searchProvidedCertificateForDomains ( domains , a . dynamicCerts . Get ( ) . ( map [ string ] * tls . Certificate ) )
2017-11-09 11:16:03 +00:00
}
2018-02-26 10:38:03 +00:00
if cert == nil {
log . Debugf ( "No provided certificate found for domains %s, get ACME certificate." , domains )
}
2017-11-09 11:16:03 +00:00
return cert
}
2018-02-26 10:38:03 +00:00
func searchProvidedCertificateForDomains ( domain string , certs map [ string ] * tls . Certificate ) * tls . Certificate {
2017-06-19 11:22:41 +00:00
// Use regex to test for provided certs that might have been added into TLSConfig
2018-02-26 10:38:03 +00:00
for certDomains := range certs {
2018-03-27 14:18:03 +00:00
domainChecked := false
2018-02-26 10:38:03 +00:00
for _ , certDomain := range strings . Split ( certDomains , "," ) {
2018-03-27 14:18:03 +00:00
domainChecked = types . MatchDomain ( domain , certDomain )
if domainChecked {
2017-06-19 11:22:41 +00:00
break
}
}
2018-03-27 14:18:03 +00:00
if domainChecked {
2018-02-26 10:38:03 +00:00
log . Debugf ( "Domain %q checked by provided certificate %q" , domain , certDomains )
return certs [ certDomains ]
2017-06-19 11:22:41 +00:00
}
}
return nil
}
2018-02-26 10:38:03 +00:00
// Get provided certificate which check a domains list (Main and SANs)
// from static and dynamic provided certificates
func ( a * ACME ) getUncheckedDomains ( domains [ ] string , account * Account ) [ ] string {
log . Debugf ( "Looking for provided certificate to validate %s..." , domains )
allCerts := make ( map [ string ] * tls . Certificate )
// Get static certificates
for domains , certificate := range a . TLSConfig . NameToCertificate {
allCerts [ domains ] = certificate
}
// Get dynamic certificates
if a . dynamicCerts != nil && a . dynamicCerts . Get ( ) != nil {
2018-03-06 09:12:04 +00:00
for domains , certificate := range a . dynamicCerts . Get ( ) . ( map [ string ] * tls . Certificate ) {
2018-02-26 10:38:03 +00:00
allCerts [ domains ] = certificate
}
}
// Get ACME certificates
if account != nil {
for domains , certificate := range account . DomainsCertificate . toDomainsMap ( ) {
allCerts [ domains ] = certificate
}
}
2018-03-26 12:12:03 +00:00
// Get Configuration Domains
for i := 0 ; i < len ( a . Domains ) ; i ++ {
allCerts [ a . Domains [ i ] . Main ] = & tls . Certificate { }
for _ , san := range a . Domains [ i ] . SANs {
allCerts [ san ] = & tls . Certificate { }
}
}
2018-02-26 10:38:03 +00:00
return searchUncheckedDomains ( domains , allCerts )
}
func searchUncheckedDomains ( domains [ ] string , certs map [ string ] * tls . Certificate ) [ ] string {
2018-03-26 12:12:03 +00:00
var uncheckedDomains [ ] string
2018-02-26 10:38:03 +00:00
for _ , domainToCheck := range domains {
2018-03-26 12:12:03 +00:00
if ! isDomainAlreadyChecked ( domainToCheck , certs ) {
2018-02-26 10:38:03 +00:00
uncheckedDomains = append ( uncheckedDomains , domainToCheck )
}
}
2018-03-26 12:12:03 +00:00
2018-02-26 10:38:03 +00:00
if len ( uncheckedDomains ) == 0 {
log . Debugf ( "No ACME certificate to generate for domains %q." , domains )
} else {
log . Debugf ( "Domains %q need ACME certificates generation for domains %q." , domains , strings . Join ( uncheckedDomains , "," ) )
}
return uncheckedDomains
}
2016-08-18 12:20:11 +00:00
func ( a * ACME ) getDomainsCertificates ( domains [ ] string ) ( * Certificate , error ) {
2016-10-14 14:04:09 +00:00
domains = fun . Map ( types . CanonicalDomain , domains ) . ( [ ] string )
2016-03-21 10:10:18 +00:00
log . Debugf ( "Loading ACME certificates %s..." , domains )
2016-05-03 00:01:10 +00:00
bundle := true
2016-10-14 00:33:01 +00:00
certificate , failures := a . client . ObtainCertificate ( domains , bundle , nil , OSCPMustStaple )
2016-03-21 10:10:18 +00:00
if len ( failures ) > 0 {
log . Error ( failures )
2018-01-15 15:04:05 +00:00
return nil , fmt . Errorf ( "cannot obtain certificates %+v" , failures )
2016-03-21 10:10:18 +00:00
}
log . Debugf ( "Loaded ACME certificates %s" , domains )
return & Certificate {
Domain : certificate . Domain ,
CertURL : certificate . CertURL ,
CertStableURL : certificate . CertStableURL ,
PrivateKey : certificate . PrivateKey ,
Certificate : certificate . Certificate ,
} , nil
}
2016-12-09 13:37:39 +00:00
func ( a * ACME ) runJobs ( ) {
safe . Go ( func ( ) {
for job := range a . jobs . Out ( ) {
function := job . ( func ( ) )
function ( )
}
} )
}
2018-03-26 12:12:03 +00:00
// getValidDomains checks if given domain is allowed to generate a ACME certificate and return it
func ( a * ACME ) getValidDomains ( domains [ ] string , wildcardAllowed bool ) ( [ ] string , error ) {
2018-04-11 15:16:07 +00:00
// Check if the domains array is empty or contains only one empty value
2018-03-26 12:12:03 +00:00
if len ( domains ) == 0 || ( len ( domains ) == 1 && len ( domains [ 0 ] ) == 0 ) {
return nil , errors . New ( "unable to generate a certificate when no domain is given" )
}
if strings . HasPrefix ( domains [ 0 ] , "*" ) {
if ! wildcardAllowed {
return nil , fmt . Errorf ( "unable to generate a wildcard certificate for domain %q from a 'Host' rule" , strings . Join ( domains , "," ) )
}
if a . DNSChallenge == nil && len ( a . DNSProvider ) == 0 {
return nil , fmt . Errorf ( "unable to generate a wildcard certificate for domain %q : ACME needs a DNSChallenge" , strings . Join ( domains , "," ) )
}
2018-04-11 15:16:07 +00:00
if strings . HasPrefix ( domains [ 0 ] , "*.*" ) {
return nil , fmt . Errorf ( "unable to generate a wildcard certificate for domain %q : ACME does not allow '*.*' wildcard domain" , strings . Join ( domains , "," ) )
2018-03-26 12:12:03 +00:00
}
2018-04-11 15:16:07 +00:00
}
for _ , san := range domains [ 1 : ] {
if strings . HasPrefix ( san , "*" ) {
return nil , fmt . Errorf ( "unable to generate a certificate for domains %q: SANs can not be a wildcard domain" , strings . Join ( domains , "," ) )
2018-03-26 12:12:03 +00:00
}
}
domains = fun . Map ( types . CanonicalDomain , domains ) . ( [ ] string )
return domains , nil
}
func isDomainAlreadyChecked ( domainToCheck string , existentDomains map [ string ] * tls . Certificate ) bool {
for certDomains := range existentDomains {
for _ , certDomain := range strings . Split ( certDomains , "," ) {
2018-03-27 14:18:03 +00:00
if types . MatchDomain ( domainToCheck , certDomain ) {
2018-03-26 12:12:03 +00:00
return true
}
}
}
return false
}
// deleteUnnecessaryDomains deletes from the configuration :
// - Duplicated domains
// - Domains which are checked by wildcard domain
func ( a * ACME ) deleteUnnecessaryDomains ( ) {
var newDomains [ ] types . Domain
for idxDomainToCheck , domainToCheck := range a . Domains {
keepDomain := true
for idxDomain , domain := range a . Domains {
if idxDomainToCheck == idxDomain {
continue
}
if reflect . DeepEqual ( domain , domainToCheck ) {
if idxDomainToCheck > idxDomain {
log . Warnf ( "The domain %v is duplicated in the configuration but will be process by ACME only once." , domainToCheck )
keepDomain = false
}
break
2018-04-11 15:16:07 +00:00
}
2018-03-26 12:12:03 +00:00
2018-04-11 15:16:07 +00:00
var newDomainsToCheck [ ] string
2018-03-26 12:12:03 +00:00
2018-04-11 15:16:07 +00:00
// Check if domains can be validated by the wildcard domain
domainsMap := make ( map [ string ] * tls . Certificate )
domainsMap [ domain . Main ] = & tls . Certificate { }
if len ( domain . SANs ) > 0 {
domainsMap [ strings . Join ( domain . SANs , "," ) ] = & tls . Certificate { }
}
2018-03-26 12:12:03 +00:00
2018-04-11 15:16:07 +00:00
for _ , domainProcessed := range domainToCheck . ToStrArray ( ) {
if idxDomain < idxDomainToCheck && isDomainAlreadyChecked ( domainProcessed , domainsMap ) {
// The domain is duplicated in a CN
log . Warnf ( "Domain %q is duplicated in the configuration or validated by the domain %v. It will be processed once." , domainProcessed , domain )
continue
} else if domain . Main != domainProcessed && strings . HasPrefix ( domain . Main , "*" ) && types . MatchDomain ( domainProcessed , domain . Main ) {
// Check if a wildcard can validate the domain
log . Warnf ( "Domain %q will not be processed by ACME provider because it is validated by the wildcard %q" , domainProcessed , domain . Main )
continue
2018-03-26 12:12:03 +00:00
}
2018-04-11 15:16:07 +00:00
newDomainsToCheck = append ( newDomainsToCheck , domainProcessed )
}
2018-03-26 12:12:03 +00:00
2018-04-11 15:16:07 +00:00
// Delete the domain if both Main and SANs can be validated by the wildcard domain
// otherwise keep the unchecked values
if newDomainsToCheck == nil {
keepDomain = false
break
2018-03-26 12:12:03 +00:00
}
2018-04-11 15:16:07 +00:00
domainToCheck . Set ( newDomainsToCheck )
2018-03-26 12:12:03 +00:00
}
if keepDomain {
newDomains = append ( newDomains , domainToCheck )
}
}
a . Domains = newDomains
}