2016-03-21 10:10:18 +00:00
package acme
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
fmtlog "log"
"os"
"reflect"
2016-05-25 15:06:34 +00:00
"strings"
2016-03-21 10:10:18 +00:00
"sync"
"time"
2016-08-22 10:45:37 +00:00
log "github.com/Sirupsen/logrus"
"github.com/containous/traefik/safe"
"github.com/xenolf/lego/acme"
2016-03-21 10:10:18 +00:00
)
// Account is used to store lets encrypt registration info
type Account struct {
Email string
Registration * acme . RegistrationResource
PrivateKey [ ] byte
DomainsCertificate DomainsCertificates
}
2016-03-22 00:32:02 +00:00
// GetEmail returns email
func ( a Account ) GetEmail ( ) string {
return a . Email
}
// GetRegistration returns lets encrypt registration resource
func ( a Account ) GetRegistration ( ) * acme . RegistrationResource {
return a . Registration
}
// GetPrivateKey returns private key
func ( a Account ) GetPrivateKey ( ) crypto . PrivateKey {
if privateKey , err := x509 . ParsePKCS1PrivateKey ( a . PrivateKey ) ; err == nil {
return privateKey
}
log . Errorf ( "Cannot unmarshall private key %+v" , a . PrivateKey )
return nil
}
// Certificate is used to store certificate info
type Certificate struct {
Domain string
CertURL string
CertStableURL string
PrivateKey [ ] byte
Certificate [ ] byte
}
2016-03-21 10:10:18 +00:00
// DomainsCertificates stores a certificate for multiple domains
type DomainsCertificates struct {
Certs [ ] * DomainsCertificate
lock * sync . RWMutex
}
func ( dc * DomainsCertificates ) init ( ) error {
if dc . lock == nil {
dc . lock = & sync . RWMutex { }
}
dc . lock . Lock ( )
defer dc . lock . Unlock ( )
for _ , domainsCertificate := range dc . Certs {
tlsCert , err := tls . X509KeyPair ( domainsCertificate . Certificate . Certificate , domainsCertificate . Certificate . PrivateKey )
if err != nil {
return err
}
domainsCertificate . tlsCert = & tlsCert
}
return nil
}
func ( dc * DomainsCertificates ) renewCertificates ( acmeCert * Certificate , domain Domain ) error {
dc . lock . Lock ( )
defer dc . lock . Unlock ( )
for _ , domainsCertificate := range dc . Certs {
if reflect . DeepEqual ( domain , domainsCertificate . Domains ) {
tlsCert , err := tls . X509KeyPair ( acmeCert . Certificate , acmeCert . PrivateKey )
if err != nil {
return err
}
2016-06-20 11:55:50 +00:00
domainsCertificate . Certificate = acmeCert
2016-03-21 10:10:18 +00:00
domainsCertificate . tlsCert = & tlsCert
return nil
}
}
2016-03-22 00:32:02 +00:00
return errors . New ( "Certificate to renew not found for domain " + domain . Main )
2016-03-21 10:10:18 +00:00
}
func ( dc * DomainsCertificates ) addCertificateForDomains ( acmeCert * Certificate , domain Domain ) ( * DomainsCertificate , error ) {
dc . lock . Lock ( )
defer dc . lock . Unlock ( )
tlsCert , err := tls . X509KeyPair ( acmeCert . Certificate , acmeCert . PrivateKey )
if err != nil {
return nil , err
}
cert := DomainsCertificate { Domains : domain , Certificate : acmeCert , tlsCert : & tlsCert }
dc . Certs = append ( dc . Certs , & cert )
return & cert , nil
}
func ( dc * DomainsCertificates ) getCertificateForDomain ( domainToFind string ) ( * DomainsCertificate , bool ) {
dc . lock . RLock ( )
defer dc . lock . RUnlock ( )
for _ , domainsCertificate := range dc . Certs {
2016-03-22 00:32:02 +00:00
domains := [ ] string { }
domains = append ( domains , domainsCertificate . Domains . Main )
domains = append ( domains , domainsCertificate . Domains . SANs ... )
2016-03-21 10:10:18 +00:00
for _ , domain := range domains {
if domain == domainToFind {
return domainsCertificate , true
}
}
}
return nil , false
}
func ( dc * DomainsCertificates ) exists ( domainToFind Domain ) ( * DomainsCertificate , bool ) {
dc . lock . RLock ( )
defer dc . lock . RUnlock ( )
for _ , domainsCertificate := range dc . Certs {
if reflect . DeepEqual ( domainToFind , domainsCertificate . Domains ) {
return domainsCertificate , true
}
}
return nil , false
}
// DomainsCertificate contains a certificate for multiple domains
type DomainsCertificate struct {
Domains Domain
Certificate * Certificate
tlsCert * tls . Certificate
}
2016-03-31 11:43:48 +00:00
func ( dc * DomainsCertificate ) needRenew ( ) bool {
for _ , c := range dc . tlsCert . Certificate {
crt , err := x509 . ParseCertificate ( c )
if err != nil {
// If there's an error, we assume the cert is broken, and needs update
return true
}
// <= 7 days left, renew certificate
if crt . NotAfter . Before ( time . Now ( ) . Add ( time . Duration ( 24 * 7 * time . Hour ) ) ) {
return true
}
}
return false
}
2016-03-21 10:10:18 +00:00
// ACME allows to connect to lets encrypt and retrieve certs
type ACME struct {
2016-05-25 15:06:34 +00:00
Email string ` description:"Email address used for registration" `
2016-05-27 09:13:34 +00:00
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'" `
2016-05-25 15:06:34 +00:00
StorageFile string ` description:"File used for certificates storage." `
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." `
2016-08-05 18:42:45 +00:00
OnHostRule bool ` description:"Enable certificate generation on frontends Host rules." `
2016-05-25 15:06:34 +00:00
CAServer string ` description:"CA server to use." `
EntryPoint string ` description:"Entrypoint to proxy acme challenge to." `
2016-03-21 10:10:18 +00:00
storageLock sync . RWMutex
2016-08-05 18:42:45 +00:00
client * acme . Client
account * Account
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-03-22 00:32:02 +00:00
// CreateConfig creates a tls.config from using ACME configuration
func ( a * ACME ) CreateConfig ( tlsConfig * tls . Config , CheckOnDemandDomain func ( domain string ) bool ) error {
2016-03-21 10:10:18 +00:00
acme . Logger = fmtlog . New ( ioutil . Discard , "" , 0 )
if len ( a . StorageFile ) == 0 {
2016-04-21 22:38:44 +00:00
return errors . New ( "Empty StorageFile, please provide a filename for certs storage" )
2016-03-21 10:10:18 +00:00
}
log . Debugf ( "Generating default certificate..." )
if len ( tlsConfig . Certificates ) == 0 {
// no certificates in TLS config, so we add a default one
cert , err := generateDefaultCertificate ( )
if err != nil {
return err
}
tlsConfig . Certificates = append ( tlsConfig . Certificates , * cert )
}
var needRegister bool
2016-08-05 18:42:45 +00:00
var err error
2016-03-21 10:10:18 +00:00
// if certificates in storage, load them
if fileInfo , err := os . Stat ( a . StorageFile ) ; err == nil && fileInfo . Size ( ) != 0 {
log . Infof ( "Loading ACME certificates..." )
// load account
2016-08-05 18:42:45 +00:00
a . account , err = a . loadAccount ( a )
2016-03-21 10:10:18 +00:00
if err != nil {
return err
}
} else {
log . Infof ( "Generating ACME Account..." )
// Create a user. New accounts need an email and private key to start
privateKey , err := rsa . GenerateKey ( rand . Reader , 4096 )
if err != nil {
return err
}
2016-08-05 18:42:45 +00:00
a . account = & Account {
2016-03-21 10:10:18 +00:00
Email : a . Email ,
PrivateKey : x509 . MarshalPKCS1PrivateKey ( privateKey ) ,
}
2016-08-05 18:42:45 +00:00
a . account . DomainsCertificate = DomainsCertificates { Certs : [ ] * DomainsCertificate { } , lock : & sync . RWMutex { } }
2016-03-21 10:10:18 +00:00
needRegister = true
}
2016-08-05 18:42:45 +00:00
a . client , err = a . buildACMEClient ( )
2016-03-21 10:10:18 +00:00
if err != nil {
return err
}
2016-08-05 18:42:45 +00:00
a . client . ExcludeChallenges ( [ ] acme . Challenge { acme . HTTP01 , acme . DNS01 } )
2016-03-21 10:10:18 +00:00
wrapperChallengeProvider := newWrapperChallengeProvider ( )
2016-08-05 18:42:45 +00:00
a . client . SetChallengeProvider ( acme . TLSSNI01 , wrapperChallengeProvider )
2016-03-21 10:10:18 +00:00
if needRegister {
// New users will need to register; be sure to save it
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-05 18:42:45 +00:00
a . 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.
2016-08-05 18:42:45 +00:00
err = a . client . AgreeToTOS ( )
2016-03-21 10:10:18 +00:00
if err != nil {
return err
}
2016-03-31 16:57:08 +00:00
safe . Go ( func ( ) {
2016-08-05 18:42:45 +00:00
a . retrieveCertificates ( a . client )
if err := a . renewCertificates ( a . client ) ; err != nil {
log . Errorf ( "Error renewing ACME certificate %+v: %s" , a . account , err . Error ( ) )
2016-06-20 11:55:50 +00:00
}
2016-03-31 16:57:08 +00:00
} )
2016-03-21 10:10:18 +00:00
tlsConfig . GetCertificate = func ( clientHello * tls . ClientHelloInfo ) ( * tls . Certificate , error ) {
if challengeCert , ok := wrapperChallengeProvider . getCertificate ( clientHello . ServerName ) ; ok {
return challengeCert , nil
}
2016-08-05 18:42:45 +00:00
if domainCert , ok := a . account . DomainsCertificate . getCertificateForDomain ( clientHello . ServerName ) ; ok {
2016-03-21 10:10:18 +00:00
return domainCert . tlsCert , nil
}
if a . OnDemand {
if CheckOnDemandDomain != nil && ! CheckOnDemandDomain ( clientHello . ServerName ) {
return nil , nil
}
2016-08-05 18:42:45 +00:00
return a . loadCertificateOnDemand ( clientHello )
2016-03-21 10:10:18 +00:00
}
return nil , nil
}
ticker := time . NewTicker ( 24 * time . Hour )
2016-03-31 16:57:08 +00:00
safe . Go ( func ( ) {
2016-03-21 10:10:18 +00:00
for {
select {
case <- ticker . C :
2016-08-05 18:42:45 +00:00
if err := a . renewCertificates ( a . client ) ; err != nil {
log . Errorf ( "Error renewing ACME certificate %+v: %s" , a . account , err . Error ( ) )
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-05 18:42:45 +00:00
func ( a * ACME ) retrieveCertificates ( client * acme . Client ) {
2016-03-22 00:32:02 +00:00
log . Infof ( "Retrieving ACME certificates..." )
for _ , domain := range a . Domains {
// check if cert isn't already loaded
2016-08-05 18:42:45 +00:00
if _ , exists := a . account . DomainsCertificate . exists ( domain ) ; ! exists {
2016-03-22 00:32:02 +00:00
domains := [ ] string { }
domains = append ( domains , domain . Main )
domains = append ( domains , domain . SANs ... )
certificateResource , err := a . getDomainsCertificates ( client , domains )
if err != nil {
log . Errorf ( "Error getting ACME certificate for domain %s: %s" , domains , err . Error ( ) )
continue
}
2016-08-05 18:42:45 +00:00
_ , err = a . account . DomainsCertificate . addCertificateForDomains ( certificateResource , domain )
2016-03-22 00:32:02 +00:00
if err != nil {
log . Errorf ( "Error adding ACME certificate for domain %s: %s" , domains , err . Error ( ) )
continue
}
2016-08-05 18:42:45 +00:00
if err = a . saveAccount ( ) ; err != nil {
log . Errorf ( "Error Saving ACME account %+v: %s" , a . account , err . Error ( ) )
2016-03-22 00:32:02 +00:00
continue
}
}
}
log . Infof ( "Retrieved ACME certificates" )
}
2016-08-05 18:42:45 +00:00
func ( a * ACME ) renewCertificates ( client * acme . Client ) error {
2016-06-20 11:55:50 +00:00
log . Debugf ( "Testing certificate renew..." )
2016-08-05 18:42:45 +00:00
for _ , certificateResource := range a . account . DomainsCertificate . Certs {
2016-03-31 11:43:48 +00:00
if certificateResource . needRenew ( ) {
2016-03-21 10:10:18 +00:00
log . Debugf ( "Renewing certificate %+v" , certificateResource . Domains )
renewedCert , err := client . RenewCertificate ( acme . CertificateResource {
Domain : certificateResource . Certificate . Domain ,
CertURL : certificateResource . Certificate . CertURL ,
CertStableURL : certificateResource . Certificate . CertStableURL ,
PrivateKey : certificateResource . Certificate . PrivateKey ,
Certificate : certificateResource . Certificate . Certificate ,
2016-06-20 11:55:50 +00:00
} , true )
2016-03-21 10:10:18 +00:00
if err != nil {
2016-06-20 11:55:50 +00:00
log . Errorf ( "Error renewing certificate: %v" , err )
continue
2016-03-21 10:10:18 +00:00
}
log . Debugf ( "Renewed certificate %+v" , certificateResource . Domains )
renewedACMECert := & Certificate {
Domain : renewedCert . Domain ,
CertURL : renewedCert . CertURL ,
CertStableURL : renewedCert . CertStableURL ,
PrivateKey : renewedCert . PrivateKey ,
Certificate : renewedCert . Certificate ,
}
2016-08-05 18:42:45 +00:00
err = a . account . DomainsCertificate . renewCertificates ( renewedACMECert , certificateResource . Domains )
2016-03-21 10:10:18 +00:00
if err != nil {
2016-06-20 11:55:50 +00:00
log . Errorf ( "Error renewing certificate: %v" , err )
continue
2016-03-21 10:10:18 +00:00
}
2016-08-05 18:42:45 +00:00
if err = a . saveAccount ( ) ; err != nil {
2016-06-20 11:55:50 +00:00
log . Errorf ( "Error saving ACME account: %v" , err )
continue
2016-03-21 10:10:18 +00:00
}
}
}
return nil
}
2016-08-05 18:42:45 +00:00
func ( a * ACME ) buildACMEClient ( ) ( * acme . Client , error ) {
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-05 18:42:45 +00:00
client , err := acme . NewClient ( caServer , a . account , acme . RSA4096 )
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 ) {
if certificateResource , ok := a . account . DomainsCertificate . getCertificateForDomain ( clientHello . ServerName ) ; ok {
2016-03-21 10:10:18 +00:00
return certificateResource . tlsCert , nil
}
2016-08-05 18:42:45 +00:00
certificate , err := a . getDomainsCertificates ( a . client , [ ] string { clientHello . ServerName } )
2016-03-21 10:10:18 +00:00
if err != nil {
return nil , err
}
log . Debugf ( "Got certificate on demand for domain %s" , clientHello . ServerName )
2016-08-05 18:42:45 +00:00
cert , err := a . account . DomainsCertificate . addCertificateForDomains ( certificate , Domain { Main : clientHello . ServerName } )
2016-03-21 10:10:18 +00:00
if err != nil {
return nil , err
}
2016-08-05 18:42:45 +00:00
if err = a . saveAccount ( ) ; 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 ) {
safe . Go ( func ( ) {
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 ] }
}
if _ , exists := a . account . DomainsCertificate . exists ( domain ) ; exists {
// domain already exists
return
}
certificate , err := a . getDomainsCertificates ( a . client , domains )
if err != nil {
log . Errorf ( "Error getting ACME certificates %+v : %v" , domains , err )
return
}
log . Debugf ( "Got certificate for domains %+v" , domains )
_ , err = a . account . DomainsCertificate . addCertificateForDomains ( certificate , domain )
if err != nil {
log . Errorf ( "Error adding ACME certificates %+v : %v" , domains , err )
return
}
if err = a . saveAccount ( ) ; err != nil {
log . Errorf ( "Error Saving ACME account %+v: %v" , a . account , err )
return
}
} )
}
2016-03-21 10:10:18 +00:00
func ( a * ACME ) loadAccount ( acmeConfig * ACME ) ( * Account , error ) {
a . storageLock . RLock ( )
defer a . storageLock . RUnlock ( )
Account := Account {
DomainsCertificate : DomainsCertificates { } ,
}
file , err := ioutil . ReadFile ( acmeConfig . StorageFile )
if err != nil {
return nil , err
}
if err := json . Unmarshal ( file , & Account ) ; err != nil {
return nil , err
}
err = Account . DomainsCertificate . init ( )
if err != nil {
return nil , err
}
log . Infof ( "Loaded ACME config from storage %s" , acmeConfig . StorageFile )
return & Account , nil
}
2016-08-05 18:42:45 +00:00
func ( a * ACME ) saveAccount ( ) error {
2016-03-21 10:10:18 +00:00
a . storageLock . Lock ( )
defer a . storageLock . Unlock ( )
// write account to file
2016-08-05 18:42:45 +00:00
data , err := json . MarshalIndent ( a . account , "" , " " )
2016-03-21 10:10:18 +00:00
if err != nil {
return err
}
2016-08-22 10:45:37 +00:00
return ioutil . WriteFile ( a . StorageFile , data , 0600 )
2016-03-21 10:10:18 +00:00
}
func ( a * ACME ) getDomainsCertificates ( client * acme . Client , domains [ ] string ) ( * Certificate , error ) {
log . Debugf ( "Loading ACME certificates %s..." , domains )
2016-05-03 00:01:10 +00:00
bundle := true
2016-03-21 10:10:18 +00:00
certificate , failures := client . ObtainCertificate ( domains , bundle , nil )
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
}