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"
"os"
2017-01-04 21:23:18 +00:00
"regexp"
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"
2016-08-18 12:20:11 +00:00
"github.com/containous/staert"
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
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"
2016-08-18 12:20:11 +00:00
"github.com/xenolf/lego/acme"
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
type ACME struct {
2016-08-18 12:20:11 +00:00
Email string ` description:"Email address used for registration" `
Domains [ ] 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." `
2016-09-23 16:27:01 +00:00
StorageFile string // deprecated
2016-08-18 12:20:11 +00:00
OnDemand bool ` description:"Enable on demand certificate. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate." `
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." `
2016-10-14 00:33:01 +00:00
DNSProvider string ` description:"Use a DNS based challenge provider rather than HTTPS." `
DelayDontCheckDNS int ` description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers." `
ACMELogging bool ` description:"Enable debug logging of ACME actions." `
2016-08-18 12:20:11 +00:00
client * acme . Client
defaultCertificate * tls . Certificate
store cluster . Store
challengeProvider * challengeProvider
checkOnDemandDomain func ( domain string ) bool
2016-12-09 13:37:39 +00:00
jobs * channels . InfiniteChannel
2017-01-04 21:23:18 +00:00
TLSConfig * tls . Config ` description:"TLS config in case wildcard certs are used" `
2016-03-21 10:10:18 +00:00
}
2016-05-25 15:06:34 +00:00
//Domains parse []Domain
type Domains [ ] Domain
//Set []Domain
func ( ds * Domains ) Set ( str string ) error {
fargs := func ( c rune ) bool {
return c == ',' || c == ';'
}
// get function
slice := strings . FieldsFunc ( str , fargs )
2016-05-27 09:13:34 +00:00
if len ( slice ) < 1 {
2016-05-25 15:06:34 +00:00
return fmt . Errorf ( "Parse error ACME.Domain. Imposible to parse %s" , str )
}
d := Domain {
Main : slice [ 0 ] ,
2016-05-27 09:13:34 +00:00
SANs : [ ] string { } ,
}
if len ( slice ) > 1 {
d . SANs = slice [ 1 : ]
2016-05-25 15:06:34 +00:00
}
* ds = append ( * ds , d )
return nil
}
//Get []Domain
func ( ds * Domains ) Get ( ) interface { } { return [ ] Domain ( * ds ) }
//String returns []Domain in string
func ( ds * Domains ) String ( ) string { return fmt . Sprintf ( "%+v" , * ds ) }
//SetValue sets []Domain into the parser
func ( ds * Domains ) SetValue ( val interface { } ) {
* ds = Domains ( val . ( [ ] Domain ) )
}
2016-03-21 10:10:18 +00:00
// Domain holds a domain name with SANs
type Domain struct {
Main string
SANs [ ] string
}
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
cert , err := generateDefaultCertificate ( )
if err != nil {
return err
}
a . defaultCertificate = cert
2016-09-23 16:27:01 +00:00
// TODO: to remove in the futurs
if len ( a . StorageFile ) > 0 && len ( a . Storage ) == 0 {
2017-05-26 15:03:14 +00:00
log . Warn ( "ACME.StorageFile is deprecated, use ACME.Storage instead" )
2016-09-23 16:27:01 +00:00
a . Storage = a . StorageFile
}
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
2016-08-18 12:20:11 +00:00
// CreateClusterConfig creates a tls.config using ACME configuration in cluster mode
func ( a * ACME ) CreateClusterConfig ( leadership * cluster . Leadership , tlsConfig * tls . Config , 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
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
2016-09-29 13:36:52 +00:00
a . challengeProvider = & challengeProvider { store : a . store }
2016-08-18 12:20:11 +00:00
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
}
}
} )
leadership . AddListener ( func ( elected bool ) error {
if elected {
object , err := a . store . Load ( )
if err != nil {
return err
}
transaction , object , err := a . store . Begin ( )
if err != nil {
return err
}
account := object . ( * Account )
account . Init ( )
var needRegister bool
if account == nil || len ( account . Email ) == 0 {
account , err = NewAccount ( a . Email )
if err != nil {
return err
}
needRegister = true
}
if err != nil {
return err
}
a . client , err = a . buildACMEClient ( account )
if err != nil {
return err
}
if needRegister {
// New users will need to register; be sure to save it
2017-05-26 15:03:14 +00:00
log . Debug ( "Register..." )
2016-08-18 12:20:11 +00:00
reg , err := a . client . Register ( )
if err != nil {
return err
}
account . Registration = reg
}
// The client has a URL to the current Let's Encrypt Subscriber
// Agreement. The user will need to agree to it.
2017-05-26 15:03:14 +00:00
log . Debug ( "AgreeToTOS..." )
2016-08-18 12:20:11 +00:00
err = a . client . AgreeToTOS ( )
if err != nil {
2016-09-23 16:27:01 +00:00
// Let's Encrypt Subscriber Agreement renew ?
reg , err := a . client . QueryRegistration ( )
if err != nil {
return err
}
account . Registration = reg
err = a . client . AgreeToTOS ( )
if err != nil {
log . Errorf ( "Error sending ACME agreement to TOS: %+v: %s" , account , err . Error ( ) )
}
2016-08-18 12:20:11 +00:00
}
err = transaction . Commit ( account )
if err != nil {
return err
}
2016-12-09 13:37:39 +00:00
a . retrieveCertificates ( )
a . renewCertificates ( )
a . runJobs ( )
2016-08-18 12:20:11 +00:00
}
return nil
} )
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
// CreateLocalConfig creates a tls.config using local ACME configuration
func ( a * ACME ) CreateLocalConfig ( tlsConfig * tls . Config , 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 filename for certs storage" )
2016-08-16 17:13:18 +00:00
}
2016-08-18 12:20:11 +00:00
a . checkOnDemandDomain = checkOnDemandDomain
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-08-18 12:20:11 +00:00
localStore := NewLocalStore ( a . Storage )
a . store = localStore
2016-09-29 13:36:52 +00:00
a . challengeProvider = & challengeProvider { store : a . store }
2016-08-16 17:13:18 +00:00
2016-03-21 10:10:18 +00:00
var needRegister bool
2016-08-18 12:20:11 +00:00
var account * Account
2016-03-21 10:10:18 +00:00
2016-08-18 12:20:11 +00:00
if fileInfo , fileErr := os . Stat ( a . Storage ) ; fileErr == nil && fileInfo . Size ( ) != 0 {
2017-05-26 15:03:14 +00:00
log . Info ( "Loading ACME Account..." )
2016-03-21 10:10:18 +00:00
// load account
2016-08-18 12:20:11 +00:00
object , err := localStore . Load ( )
2016-03-21 10:10:18 +00:00
if err != nil {
return err
}
2016-08-18 12:20:11 +00:00
account = object . ( * Account )
2016-03-21 10:10:18 +00:00
} else {
2017-05-26 15:03:14 +00:00
log . Info ( "Generating ACME Account..." )
2016-08-18 12:20:11 +00:00
account , err = NewAccount ( a . Email )
2016-03-21 10:10:18 +00:00
if err != nil {
return err
}
needRegister = true
}
2016-08-18 12:20:11 +00:00
a . client , err = a . buildACMEClient ( account )
2016-08-16 17:13:18 +00:00
if err != nil {
return err
}
2016-03-21 10:10:18 +00:00
if needRegister {
// New users will need to register; be sure to save it
2017-05-26 15:03:14 +00:00
log . Info ( "Register..." )
2016-08-05 18:42:45 +00:00
reg , err := a . client . Register ( )
2016-03-21 10:10:18 +00:00
if err != nil {
return err
}
2016-08-18 12:20:11 +00:00
account . Registration = reg
2016-03-21 10:10:18 +00:00
}
// The client has a URL to the current Let's Encrypt Subscriber
// Agreement. The user will need to agree to it.
2017-05-26 15:03:14 +00:00
log . Debug ( "AgreeToTOS..." )
2016-08-05 18:42:45 +00:00
err = a . client . AgreeToTOS ( )
2016-08-02 09:48:44 +00:00
if err != nil {
// Let's Encrypt Subscriber Agreement renew ?
2016-09-19 17:58:34 +00:00
reg , err := a . client . QueryRegistration ( )
2016-08-02 09:48:44 +00:00
if err != nil {
return err
}
2016-08-18 12:20:11 +00:00
account . Registration = reg
2016-09-19 17:58:34 +00:00
err = a . client . AgreeToTOS ( )
2016-08-02 09:48:44 +00:00
if err != nil {
2016-08-18 12:20:11 +00:00
log . Errorf ( "Error sending ACME agreement to TOS: %+v: %s" , account , err . Error ( ) )
2016-08-02 09:48:44 +00:00
}
}
// save account
2016-08-18 12:20:11 +00:00
transaction , _ , err := a . store . Begin ( )
if err != nil {
return err
}
err = transaction . Commit ( account )
2016-03-21 10:10:18 +00:00
if err != nil {
return err
}
2016-12-09 13:37:39 +00:00
a . retrieveCertificates ( )
a . renewCertificates ( )
a . runJobs ( )
2016-03-21 10:10:18 +00:00
ticker := time . NewTicker ( 24 * time . Hour )
2016-03-31 16:57:08 +00:00
safe . Go ( func ( ) {
2016-08-16 17:13:18 +00:00
for range ticker . C {
2016-12-09 13:37:39 +00:00
a . renewCertificates ( )
2016-03-21 10:10:18 +00:00
}
2016-03-31 16:57:08 +00:00
} )
2016-03-21 10:10:18 +00:00
return nil
}
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-01-04 21:23:18 +00:00
//use regex to test for wildcard certs that might have been added into TLSConfig
for k := range a . TLSConfig . NameToCertificate {
2017-03-06 23:32:37 +00:00
selector := "^" + strings . Replace ( k , "*." , "[^\\.]*\\.?" , - 1 ) + "$"
2017-01-04 21:23:18 +00:00
match , _ := regexp . MatchString ( selector , domain )
if match {
return a . TLSConfig . NameToCertificate [ k ] , nil
}
}
2016-10-14 14:04:09 +00:00
if challengeCert , ok := a . challengeProvider . getCertificate ( domain ) ; ok {
log . Debugf ( "ACME got challenge %s" , domain )
2016-08-18 12:20:11 +00:00
return challengeCert , nil
}
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 )
}
2016-10-14 14:04:09 +00:00
log . Debugf ( "ACME got nothing %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..." )
2016-12-09 13:37:39 +00:00
for _ , domain := range a . Domains {
// check if cert isn't already loaded
account := a . store . Get ( ) . ( * Account )
if _ , exists := account . DomainsCertificate . exists ( domain ) ; ! exists {
domains := [ ] string { }
domains = append ( domains , domain . Main )
domains = append ( domains , domain . SANs ... )
certificateResource , err := a . getDomainsCertificates ( domains )
if err != nil {
log . Errorf ( "Error getting ACME certificate for domain %s: %s" , domains , err . Error ( ) )
continue
}
transaction , object , err := a . store . Begin ( )
if err != nil {
log . Errorf ( "Error creating ACME store transaction from domain %s: %s" , domain , err . Error ( ) )
continue
}
account = object . ( * Account )
_ , err = account . DomainsCertificate . addCertificateForDomains ( certificateResource , domain )
if err != nil {
log . Errorf ( "Error adding ACME certificate for domain %s: %s" , domains , err . Error ( ) )
continue
}
2016-08-18 12:20:11 +00:00
2016-12-09 13:37:39 +00:00
if err = transaction . Commit ( account ) ; err != nil {
log . Errorf ( "Error Saving ACME account %+v: %s" , account , err . Error ( ) )
continue
}
2016-03-22 00:32:02 +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-05-26 15:03:14 +00:00
log . Debug ( "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 ( ) {
log . Debugf ( "Renewing certificate %+v" , certificateResource . Domains )
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 {
log . Errorf ( "Error renewing certificate: %v" , err )
continue
}
log . Debugf ( "Renewed certificate %+v" , certificateResource . Domains )
renewedACMECert := & Certificate {
Domain : renewedCert . Domain ,
CertURL : renewedCert . CertURL ,
CertStableURL : renewedCert . CertStableURL ,
PrivateKey : renewedCert . PrivateKey ,
Certificate : renewedCert . Certificate ,
}
transaction , object , err := a . store . Begin ( )
if err != nil {
log . Errorf ( "Error renewing certificate: %v" , err )
continue
}
account = object . ( * Account )
err = account . DomainsCertificate . renewCertificates ( renewedACMECert , certificateResource . Domains )
if err != nil {
log . Errorf ( "Error renewing certificate: %v" , err )
continue
}
2016-08-18 12:20:11 +00:00
2016-12-09 13:37:39 +00:00
if err = transaction . Commit ( account ) ; err != nil {
log . Errorf ( "Error Saving ACME account %+v: %s" , account , err . Error ( ) )
continue
}
2016-03-21 10:10:18 +00:00
}
}
}
}
2016-10-14 00:33:01 +00:00
func dnsOverrideDelay ( delay int ) error {
var err error
if delay > 0 {
log . Debugf ( "Delaying %d seconds rather than validating DNS propagation" , delay )
acme . PreCheckDNS = func ( _ , _ string ) ( bool , error ) {
time . Sleep ( time . Duration ( delay ) * time . Second )
return true , nil
}
} else if delay < 0 {
err = fmt . Errorf ( "Invalid negative DelayDontCheckDNS: %d" , delay )
}
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..." )
2016-03-21 10:10:18 +00:00
caServer := "https://acme-v01.api.letsencrypt.org/directory"
if len ( a . CAServer ) > 0 {
caServer = a . CAServer
}
2016-08-18 12:20:11 +00:00
client , err := acme . NewClient ( caServer , account , acme . RSA4096 )
if err != nil {
return nil , err
}
2016-10-14 00:33:01 +00:00
if len ( a . DNSProvider ) > 0 {
log . Debugf ( "Using DNS Challenge provider: %s" , a . DNSProvider )
err = dnsOverrideDelay ( a . DelayDontCheckDNS )
if err != nil {
return nil , err
}
var provider acme . ChallengeProvider
provider , err = dns . NewDNSChallengeProviderByName ( a . DNSProvider )
if err != nil {
return nil , err
}
client . ExcludeChallenges ( [ ] acme . Challenge { acme . HTTP01 , acme . TLSSNI01 } )
err = client . SetChallengeProvider ( acme . DNS01 , provider )
} else {
client . ExcludeChallenges ( [ ] acme . Challenge { acme . HTTP01 , acme . DNS01 } )
err = client . SetChallengeProvider ( acme . TLSSNI01 , a . challengeProvider )
}
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 )
2016-10-14 14:04:09 +00:00
cert , err := account . DomainsCertificate . addCertificateForDomains ( certificate , 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 ( ) {
log . Debugf ( "LoadCertificateForDomains %s..." , domains )
domains = fun . Map ( types . CanonicalDomain , domains ) . ( [ ] string )
2016-09-23 16:27:01 +00:00
operation := func ( ) error {
if a . client == nil {
return fmt . Errorf ( "ACME client still not built" )
}
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
2016-12-08 12:32:52 +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 )
2016-08-05 18:42:45 +00:00
var domain Domain
if len ( domains ) == 0 {
// no domain
return
} else if len ( domains ) > 1 {
domain = Domain { Main : domains [ 0 ] , SANs : domains [ 1 : ] }
} else {
domain = Domain { Main : domains [ 0 ] }
}
2016-08-18 12:20:11 +00:00
if _ , exists := account . DomainsCertificate . exists ( domain ) ; exists {
2016-08-05 18:42:45 +00:00
// domain already exists
return
}
2016-08-18 12:20:11 +00:00
certificate , err := a . getDomainsCertificates ( domains )
2016-08-05 18:42:45 +00:00
if err != nil {
log . Errorf ( "Error getting ACME certificates %+v : %v" , domains , err )
return
}
log . Debugf ( "Got certificate for domains %+v" , domains )
2016-08-18 12:20:11 +00:00
transaction , object , err := a . store . Begin ( )
if err != nil {
log . Errorf ( "Error creating transaction %+v : %v" , domains , err )
return
}
account = object . ( * Account )
_ , err = account . DomainsCertificate . addCertificateForDomains ( certificate , domain )
2016-08-05 18:42:45 +00:00
if err != nil {
log . Errorf ( "Error adding ACME certificates %+v : %v" , domains , err )
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
}
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 )
return nil , fmt . Errorf ( "Cannot obtain certificates %s+v" , failures )
}
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 ( )
}
} )
}