2016-03-21 11:10:18 +01:00
package acme
import (
"crypto/tls"
2016-12-30 09:21:13 +01:00
"fmt"
2016-09-23 18:27:01 +02:00
"strings"
2016-03-21 11:10:18 +01:00
"sync"
2016-12-30 09:21:13 +01:00
"time"
2016-03-21 11:10:18 +01:00
2016-09-23 18:27:01 +02:00
"github.com/cenk/backoff"
2016-08-18 14:20:11 +02:00
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/log"
2016-12-08 13:32:12 +01:00
"github.com/containous/traefik/safe"
2016-03-21 11:10:18 +01:00
"github.com/xenolf/lego/acme"
)
2016-08-18 14:20:11 +02:00
var _ acme . ChallengeProviderTimeout = ( * challengeProvider ) ( nil )
2016-08-16 19:13:18 +02:00
2016-08-18 14:20:11 +02:00
type challengeProvider struct {
store cluster . Store
lock sync . RWMutex
2016-03-21 11:10:18 +01:00
}
2016-08-18 14:20:11 +02:00
func ( c * challengeProvider ) getCertificate ( domain string ) ( cert * tls . Certificate , exists bool ) {
log . Debugf ( "Challenge GetCertificate %s" , domain )
2016-09-23 18:27:01 +02:00
if ! strings . HasSuffix ( domain , ".acme.invalid" ) {
return nil , false
}
2016-03-21 11:10:18 +01:00
c . lock . RLock ( )
defer c . lock . RUnlock ( )
2016-08-18 14:20:11 +02:00
account := c . store . Get ( ) . ( * Account )
if account . ChallengeCerts == nil {
return nil , false
}
2016-09-23 18:27:01 +02:00
account . Init ( )
var result * tls . Certificate
operation := func ( ) error {
for _ , cert := range account . ChallengeCerts {
for _ , dns := range cert . certificate . Leaf . DNSNames {
if domain == dns {
result = cert . certificate
return nil
}
}
2016-08-18 14:20:11 +02:00
}
2017-09-13 10:34:04 +02:00
return fmt . Errorf ( "cannot find challenge cert for domain %s" , domain )
2016-03-21 11:10:18 +01:00
}
2016-09-23 18:27:01 +02:00
notify := func ( err error , time time . Duration ) {
log . Errorf ( "Error getting cert: %v, retrying in %s" , err , time )
}
ebo := backoff . NewExponentialBackOff ( )
ebo . MaxElapsedTime = 60 * time . Second
2016-12-08 13:32:12 +01:00
err := backoff . RetryNotify ( safe . OperationWithRecover ( operation ) , ebo , notify )
2016-09-23 18:27:01 +02:00
if err != nil {
log . Errorf ( "Error getting cert: %v" , err )
return nil , false
}
return result , true
2016-03-21 11:10:18 +01:00
}
2016-08-18 14:20:11 +02:00
func ( c * challengeProvider ) Present ( domain , token , keyAuth string ) error {
log . Debugf ( "Challenge Present %s" , domain )
2016-09-23 18:27:01 +02:00
cert , _ , err := TLSSNI01ChallengeCert ( keyAuth )
2016-03-21 11:10:18 +01:00
if err != nil {
return err
}
c . lock . Lock ( )
defer c . lock . Unlock ( )
2016-08-18 14:20:11 +02:00
transaction , object , err := c . store . Begin ( )
if err != nil {
return err
}
account := object . ( * Account )
if account . ChallengeCerts == nil {
2016-09-23 18:27:01 +02:00
account . ChallengeCerts = map [ string ] * ChallengeCert { }
2016-03-21 11:10:18 +01:00
}
2016-09-23 18:27:01 +02:00
account . ChallengeCerts [ domain ] = & cert
2016-08-18 14:20:11 +02:00
return transaction . Commit ( account )
2016-03-21 11:10:18 +01:00
}
2016-08-18 14:20:11 +02:00
func ( c * challengeProvider ) CleanUp ( domain , token , keyAuth string ) error {
log . Debugf ( "Challenge CleanUp %s" , domain )
2016-03-21 11:10:18 +01:00
c . lock . Lock ( )
defer c . lock . Unlock ( )
2016-08-18 14:20:11 +02:00
transaction , object , err := c . store . Begin ( )
if err != nil {
return err
}
account := object . ( * Account )
delete ( account . ChallengeCerts , domain )
return transaction . Commit ( account )
}
func ( c * challengeProvider ) Timeout ( ) ( timeout , interval time . Duration ) {
return 60 * time . Second , 5 * time . Second
2016-03-21 11:10:18 +01:00
}