mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-13 22:51:08 -05:00
Clean up some significant portions of the TLS management code
This commit is contained in:
parent
0e7635c54b
commit
bedad34b25
6 changed files with 112 additions and 166 deletions
|
@ -23,7 +23,7 @@ func activateHTTPS(cctx caddy.Context) error {
|
||||||
|
|
||||||
// place certificates and keys on disk
|
// place certificates and keys on disk
|
||||||
for _, c := range ctx.siteConfigs {
|
for _, c := range ctx.siteConfigs {
|
||||||
err := c.TLS.ObtainCert(operatorPresent)
|
err := c.TLS.ObtainCert(c.TLS.Hostname, operatorPresent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,13 @@ import (
|
||||||
// acmeMu ensures that only one ACME challenge occurs at a time.
|
// acmeMu ensures that only one ACME challenge occurs at a time.
|
||||||
var acmeMu sync.Mutex
|
var acmeMu sync.Mutex
|
||||||
|
|
||||||
// ACMEClient is an acme.Client with custom state attached.
|
// ACMEClient is a wrapper over acme.Client with
|
||||||
|
// some custom state attached. It is used to obtain,
|
||||||
|
// renew, and revoke certificates with ACME.
|
||||||
type ACMEClient struct {
|
type ACMEClient struct {
|
||||||
*acme.Client
|
|
||||||
AllowPrompts bool
|
AllowPrompts bool
|
||||||
config *Config
|
config *Config
|
||||||
|
acmeClient *acme.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// newACMEClient creates a new ACMEClient given an email and whether
|
// newACMEClient creates a new ACMEClient given an email and whether
|
||||||
|
@ -100,7 +102,11 @@ var newACMEClient = func(config *Config, allowPrompts bool) (*ACMEClient, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c := &ACMEClient{Client: client, AllowPrompts: allowPrompts, config: config}
|
c := &ACMEClient{
|
||||||
|
AllowPrompts: allowPrompts,
|
||||||
|
config: config,
|
||||||
|
acmeClient: client,
|
||||||
|
}
|
||||||
|
|
||||||
if config.DNSProvider == "" {
|
if config.DNSProvider == "" {
|
||||||
// Use HTTP and TLS-SNI challenges by default
|
// Use HTTP and TLS-SNI challenges by default
|
||||||
|
@ -116,15 +122,15 @@ var newACMEClient = func(config *Config, allowPrompts bool) (*ACMEClient, error)
|
||||||
|
|
||||||
// See if TLS challenge needs to be handled by our own facilities
|
// See if TLS challenge needs to be handled by our own facilities
|
||||||
if caddy.HasListenerWithAddress(net.JoinHostPort(config.ListenHost, TLSSNIChallengePort)) {
|
if caddy.HasListenerWithAddress(net.JoinHostPort(config.ListenHost, TLSSNIChallengePort)) {
|
||||||
c.SetChallengeProvider(acme.TLSSNI01, tlsSniSolver{})
|
c.acmeClient.SetChallengeProvider(acme.TLSSNI01, tlsSniSolver{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always respect user's bind preferences by using config.ListenHost
|
// Always respect user's bind preferences by using config.ListenHost
|
||||||
err := c.SetHTTPAddress(net.JoinHostPort(config.ListenHost, useHTTPPort))
|
err := c.acmeClient.SetHTTPAddress(net.JoinHostPort(config.ListenHost, useHTTPPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = c.SetTLSAddress(net.JoinHostPort(config.ListenHost, ""))
|
err = c.acmeClient.SetTLSAddress(net.JoinHostPort(config.ListenHost, ""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -145,23 +151,50 @@ var newACMEClient = func(config *Config, allowPrompts bool) (*ACMEClient, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the DNS challenge exclusively
|
// Use the DNS challenge exclusively
|
||||||
c.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01})
|
c.acmeClient.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01})
|
||||||
c.SetChallengeProvider(acme.DNS01, prov)
|
c.acmeClient.SetChallengeProvider(acme.DNS01, prov)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain obtains a single certificate for names. It stores the certificate
|
// Obtain obtains a single certificate for name. It stores the certificate
|
||||||
// on the disk if successful.
|
// on the disk if successful. This function is safe for concurrent use.
|
||||||
func (c *ACMEClient) Obtain(names []string) error {
|
//
|
||||||
|
// Right now our storage mechanism only supports one name per certificate,
|
||||||
|
// so this function (along with Renew and Revoke) only accepts one domain
|
||||||
|
// as input. It can be easily modified to support SAN certificates if our
|
||||||
|
// storage mechanism is upgraded later.
|
||||||
|
//
|
||||||
|
// Callers who have access to a Config value should use the ObtainCert
|
||||||
|
// method on that instead of this lower-level method.
|
||||||
|
func (c *ACMEClient) Obtain(name string) error {
|
||||||
|
// Get access to ACME storage
|
||||||
|
storage, err := c.config.StorageFor(c.config.CAUrl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We must lock the obtain with the storage engine
|
||||||
|
if lockObtained, err := storage.LockRegister(name); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !lockObtained {
|
||||||
|
log.Printf("[INFO] Certificate for %v is already being obtained elsewhere", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := storage.UnlockRegister(name); err != nil {
|
||||||
|
log.Printf("[ERROR] Unable to unlock obtain lock for %v: %v", name, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
Attempts:
|
Attempts:
|
||||||
for attempts := 0; attempts < 2; attempts++ {
|
for attempts := 0; attempts < 2; attempts++ {
|
||||||
namesObtaining.Add(names)
|
namesObtaining.Add([]string{name})
|
||||||
acmeMu.Lock()
|
acmeMu.Lock()
|
||||||
certificate, failures := c.ObtainCertificate(names, true, nil)
|
certificate, failures := c.acmeClient.ObtainCertificate([]string{name}, true, nil)
|
||||||
acmeMu.Unlock()
|
acmeMu.Unlock()
|
||||||
namesObtaining.Remove(names)
|
namesObtaining.Remove([]string{name})
|
||||||
if len(failures) > 0 {
|
if len(failures) > 0 {
|
||||||
// Error - try to fix it or report it to the user and abort
|
// Error - try to fix it or report it to the user and abort
|
||||||
var errMsg string // we'll combine all the failures into a single error message
|
var errMsg string // we'll combine all the failures into a single error message
|
||||||
|
@ -178,7 +211,7 @@ Attempts:
|
||||||
promptedForAgreement = true
|
promptedForAgreement = true
|
||||||
}
|
}
|
||||||
if Agreed || !c.AllowPrompts {
|
if Agreed || !c.AllowPrompts {
|
||||||
err := c.AgreeToTOS()
|
err := c.acmeClient.AgreeToTOS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("error agreeing to updated terms: " + err.Error())
|
return errors.New("error agreeing to updated terms: " + err.Error())
|
||||||
}
|
}
|
||||||
|
@ -193,13 +226,9 @@ Attempts:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Success - immediately save the certificate resource
|
// Success - immediately save the certificate resource
|
||||||
storage, err := c.config.StorageFor(c.config.CAUrl)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = saveCertResource(storage, certificate)
|
err = saveCertResource(storage, certificate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error saving assets for %v: %v", names, err)
|
return fmt.Errorf("error saving assets for %v: %v", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
|
@ -208,13 +237,11 @@ Attempts:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renew renews the managed certificate for name. Right now our storage
|
// Renew renews the managed certificate for name. This function is
|
||||||
// mechanism only supports one name per certificate, so this function only
|
// safe for concurrent use.
|
||||||
// accepts one domain as input. It can be easily modified to support SAN
|
|
||||||
// certificates if, one day, they become desperately needed enough that our
|
|
||||||
// storage mechanism is upgraded to be more complex to support SAN certs.
|
|
||||||
//
|
//
|
||||||
// Anyway, this function is safe for concurrent use.
|
// Callers who have access to a Config value should use the RenewCert
|
||||||
|
// method on that instead of this lower-level method.
|
||||||
func (c *ACMEClient) Renew(name string) error {
|
func (c *ACMEClient) Renew(name string) error {
|
||||||
// Get access to ACME storage
|
// Get access to ACME storage
|
||||||
storage, err := c.config.StorageFor(c.config.CAUrl)
|
storage, err := c.config.StorageFor(c.config.CAUrl)
|
||||||
|
@ -251,7 +278,7 @@ func (c *ACMEClient) Renew(name string) error {
|
||||||
for attempts := 0; attempts < 2; attempts++ {
|
for attempts := 0; attempts < 2; attempts++ {
|
||||||
namesObtaining.Add([]string{name})
|
namesObtaining.Add([]string{name})
|
||||||
acmeMu.Lock()
|
acmeMu.Lock()
|
||||||
newCertMeta, err = c.RenewCertificate(certMeta, true)
|
newCertMeta, err = c.acmeClient.RenewCertificate(certMeta, true)
|
||||||
acmeMu.Unlock()
|
acmeMu.Unlock()
|
||||||
namesObtaining.Remove([]string{name})
|
namesObtaining.Remove([]string{name})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -259,10 +286,10 @@ func (c *ACMEClient) Renew(name string) error {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the legal terms changed and need to be agreed to again,
|
// If the legal terms were updated and need to be
|
||||||
// we can handle that.
|
// agreed to again, we can handle that.
|
||||||
if _, ok := err.(acme.TOSError); ok {
|
if _, ok := err.(acme.TOSError); ok {
|
||||||
err := c.AgreeToTOS()
|
err := c.acmeClient.AgreeToTOS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -304,7 +331,7 @@ func (c *ACMEClient) Revoke(name string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.Client.RevokeCertificate(siteData.Cert)
|
err = c.acmeClient.RevokeCertificate(siteData.Cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,9 @@ package caddytls
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"time"
|
|
||||||
|
|
||||||
"log"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -116,24 +112,21 @@ type OnDemandState struct {
|
||||||
// If it reaches MaxObtain, on-demand issuances must fail.
|
// If it reaches MaxObtain, on-demand issuances must fail.
|
||||||
ObtainedCount int32
|
ObtainedCount int32
|
||||||
|
|
||||||
// Based on max_certs in tls config, it specifies the
|
// Set from max_certs in tls config, it specifies the
|
||||||
// maximum number of certificates that can be issued.
|
// maximum number of certificates that can be issued.
|
||||||
MaxObtain int32
|
MaxObtain int32
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObtainCert obtains a certificate for c.Hostname, as long as a certificate
|
// ObtainCert obtains a certificate for name using c, as long
|
||||||
// does not already exist in storage on disk. It only obtains and stores
|
// as a certificate does not already exist in storage for that
|
||||||
// certificates (and their keys) to disk, it does not load them into memory.
|
// name. The name must qualify and c must be flagged as Managed.
|
||||||
// If allowPrompts is true, the user may be shown a prompt. If proxyACME is
|
// This function is a no-op if storage already has a certificate
|
||||||
// true, the relevant ACME challenges will be proxied to the alternate port.
|
// for name.
|
||||||
func (c *Config) ObtainCert(allowPrompts bool) error {
|
//
|
||||||
return c.obtainCertName(c.Hostname, allowPrompts)
|
// It only obtains and stores certificates (and their keys),
|
||||||
}
|
// it does not load them into memory. If allowPrompts is true,
|
||||||
|
// the user may be shown a prompt.
|
||||||
// obtainCertName gets a certificate for name using the ACME config c
|
func (c *Config) ObtainCert(name string, allowPrompts bool) error {
|
||||||
// if c and name both qualify. It places the certificate in storage.
|
|
||||||
// It is a no-op if the storage already has a certificate for name.
|
|
||||||
func (c *Config) obtainCertName(name string, allowPrompts bool) error {
|
|
||||||
if !c.Managed || !HostQualifies(name) {
|
if !c.Managed || !HostQualifies(name) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -142,29 +135,13 @@ func (c *Config) obtainCertName(name string, allowPrompts bool) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
siteExists, err := storage.SiteExists(name)
|
siteExists, err := storage.SiteExists(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if siteExists {
|
if siteExists {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// We must lock the obtain with the storage engine
|
|
||||||
if lockObtained, err := storage.LockRegister(name); err != nil {
|
|
||||||
return err
|
|
||||||
} else if !lockObtained {
|
|
||||||
log.Printf("[INFO] Certificate for %v is already being obtained elsewhere", name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := storage.UnlockRegister(name); err != nil {
|
|
||||||
log.Printf("[ERROR] Unable to unlock obtain lock for %v: %v", name, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if c.ACMEEmail == "" {
|
if c.ACMEEmail == "" {
|
||||||
c.ACMEEmail = getEmail(storage, allowPrompts)
|
c.ACMEEmail = getEmail(storage, allowPrompts)
|
||||||
}
|
}
|
||||||
|
@ -173,86 +150,17 @@ func (c *Config) obtainCertName(name string, allowPrompts bool) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return client.Obtain(name)
|
||||||
return client.Obtain([]string{name})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenewCert renews the certificate for c.Hostname. If there is already a lock
|
// RenewCert renews the certificate for name using c. It stows the
|
||||||
// on renewal, this will not perform the renewal and no error will occur.
|
// renewed certificate and its assets in storage if successful.
|
||||||
func (c *Config) RenewCert(allowPrompts bool) error {
|
func (c *Config) RenewCert(name string, allowPrompts bool) error {
|
||||||
return c.renewCertName(c.Hostname, allowPrompts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// renewCertName renews the certificate for the given name. If there is already
|
|
||||||
// a lock on renewal, this will not perform the renewal and no error will
|
|
||||||
// occur.
|
|
||||||
func (c *Config) renewCertName(name string, allowPrompts bool) error {
|
|
||||||
storage, err := c.StorageFor(c.CAUrl)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// We must lock the renewal with the storage engine
|
|
||||||
if lockObtained, err := storage.LockRegister(name); err != nil {
|
|
||||||
return err
|
|
||||||
} else if !lockObtained {
|
|
||||||
log.Printf("[INFO] Certificate for %v is already being renewed elsewhere", name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := storage.UnlockRegister(name); err != nil {
|
|
||||||
log.Printf("[ERROR] Unable to unlock renewal lock for %v: %v", name, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Prepare for renewal (load PEM cert, key, and meta)
|
|
||||||
siteData, err := storage.LoadSite(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var certMeta acme.CertificateResource
|
|
||||||
err = json.Unmarshal(siteData.Meta, &certMeta)
|
|
||||||
certMeta.Certificate = siteData.Cert
|
|
||||||
certMeta.PrivateKey = siteData.Key
|
|
||||||
|
|
||||||
client, err := newACMEClient(c, allowPrompts)
|
client, err := newACMEClient(c, allowPrompts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return client.Renew(name)
|
||||||
// Perform renewal and retry if necessary, but not too many times.
|
|
||||||
var newCertMeta acme.CertificateResource
|
|
||||||
var success bool
|
|
||||||
for attempts := 0; attempts < 2; attempts++ {
|
|
||||||
namesObtaining.Add([]string{name})
|
|
||||||
acmeMu.Lock()
|
|
||||||
newCertMeta, err = client.RenewCertificate(certMeta, true)
|
|
||||||
acmeMu.Unlock()
|
|
||||||
namesObtaining.Remove([]string{name})
|
|
||||||
if err == nil {
|
|
||||||
success = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the legal terms were updated and need to be
|
|
||||||
// agreed to again, we can handle that.
|
|
||||||
if _, ok := err.(acme.TOSError); ok {
|
|
||||||
err := client.AgreeToTOS()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// For any other kind of error, wait 10s and try again.
|
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !success {
|
|
||||||
return errors.New("too many renewal attempts; last error: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return saveCertResource(storage, newCertMeta)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorageFor obtains a TLS Storage instance for the given CA URL which should
|
// StorageFor obtains a TLS Storage instance for the given CA URL which should
|
||||||
|
|
|
@ -123,7 +123,6 @@ func (s FileStorage) readFile(file string) ([]byte, error) {
|
||||||
return nil, ErrNotExist(err)
|
return nil, ErrNotExist(err)
|
||||||
}
|
}
|
||||||
return b, err
|
return b, err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SiteExists implements Storage.SiteExists by checking for the presence of
|
// SiteExists implements Storage.SiteExists by checking for the presence of
|
||||||
|
|
|
@ -172,22 +172,25 @@ func (cg configGroup) obtainOnDemandCertificate(name string, cfg *Config) (Certi
|
||||||
return cg.getCertDuringHandshake(name, true, false)
|
return cg.getCertDuringHandshake(name, true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// looks like it's up to us to do all the work and obtain the cert
|
// looks like it's up to us to do all the work and obtain the cert.
|
||||||
|
// make a chan others can wait on if needed
|
||||||
wait = make(chan struct{})
|
wait = make(chan struct{})
|
||||||
obtainCertWaitChans[name] = wait
|
obtainCertWaitChans[name] = wait
|
||||||
obtainCertWaitChansMu.Unlock()
|
obtainCertWaitChansMu.Unlock()
|
||||||
|
|
||||||
// Unblock waiters and delete waitgroup when we return
|
// do the obtain
|
||||||
defer func() {
|
log.Printf("[INFO] Obtaining new certificate for %s", name)
|
||||||
|
err := cfg.ObtainCert(name, false)
|
||||||
|
|
||||||
|
// immediately unblock anyone waiting for it; doing this in
|
||||||
|
// a defer would risk deadlock because of the recursive call
|
||||||
|
// to getCertDuringHandshake below when we return!
|
||||||
obtainCertWaitChansMu.Lock()
|
obtainCertWaitChansMu.Lock()
|
||||||
close(wait)
|
close(wait)
|
||||||
delete(obtainCertWaitChans, name)
|
delete(obtainCertWaitChans, name)
|
||||||
obtainCertWaitChansMu.Unlock()
|
obtainCertWaitChansMu.Unlock()
|
||||||
}()
|
|
||||||
|
|
||||||
log.Printf("[INFO] Obtaining new certificate for %s", name)
|
if err != nil {
|
||||||
|
|
||||||
if err := cfg.obtainCertName(name, false); err != nil {
|
|
||||||
// Failed to solve challenge, so don't allow another on-demand
|
// Failed to solve challenge, so don't allow another on-demand
|
||||||
// issue for this name to be attempted for a little while.
|
// issue for this name to be attempted for a little while.
|
||||||
failedIssuanceMu.Lock()
|
failedIssuanceMu.Lock()
|
||||||
|
@ -208,7 +211,7 @@ func (cg configGroup) obtainOnDemandCertificate(name string, cfg *Config) (Certi
|
||||||
lastIssueTime = time.Now()
|
lastIssueTime = time.Now()
|
||||||
lastIssueTimeMu.Unlock()
|
lastIssueTimeMu.Unlock()
|
||||||
|
|
||||||
// The certificate is already on disk; now just start over to load it and serve it
|
// certificate is already on disk; now just start over to load it and serve it
|
||||||
return cg.getCertDuringHandshake(name, true, false)
|
return cg.getCertDuringHandshake(name, true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,17 +268,18 @@ func (cg configGroup) renewDynamicCertificate(name string, cfg *Config) (Certifi
|
||||||
obtainCertWaitChans[name] = wait
|
obtainCertWaitChans[name] = wait
|
||||||
obtainCertWaitChansMu.Unlock()
|
obtainCertWaitChansMu.Unlock()
|
||||||
|
|
||||||
// unblock waiters and delete waitgroup when we return
|
// do the renew
|
||||||
defer func() {
|
log.Printf("[INFO] Renewing certificate for %s", name)
|
||||||
|
err := cfg.RenewCert(name, false)
|
||||||
|
|
||||||
|
// immediately unblock anyone waiting for it; doing this in
|
||||||
|
// a defer would risk deadlock because of the recursive call
|
||||||
|
// to getCertDuringHandshake below when we return!
|
||||||
obtainCertWaitChansMu.Lock()
|
obtainCertWaitChansMu.Lock()
|
||||||
close(wait)
|
close(wait)
|
||||||
delete(obtainCertWaitChans, name)
|
delete(obtainCertWaitChans, name)
|
||||||
obtainCertWaitChansMu.Unlock()
|
obtainCertWaitChansMu.Unlock()
|
||||||
}()
|
|
||||||
|
|
||||||
log.Printf("[INFO] Renewing certificate for %s", name)
|
|
||||||
|
|
||||||
err := cfg.renewCertName(name, false)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Certificate{}, err
|
return Certificate{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,12 +99,20 @@ func RenewManagedCertificates(allowPrompts bool) (err error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// This works well because managed certs are only associated with one name per config.
|
// Get the name which we should use to renew this certificate;
|
||||||
// Note, the renewal inside here may not actually occur and no error will be returned
|
// we only support managing certificates with one name per cert,
|
||||||
// due to renewal lock (i.e. because a renewal is already happening). This lack of
|
// so this should be easy. We can't rely on cert.Config.Hostname
|
||||||
// error is by intention to force cache invalidation as though it has renewed.
|
// because it may be a wildcard value from the Caddyfile (e.g.
|
||||||
err := cert.Config.RenewCert(allowPrompts)
|
// *.something.com) which, as of 2016, is not supported by ACME.
|
||||||
|
var renewName string
|
||||||
|
for _, name := range cert.Names {
|
||||||
|
if name != "" {
|
||||||
|
renewName = name
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cert.Config.RenewCert(renewName, allowPrompts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if allowPrompts && timeLeft < 0 {
|
if allowPrompts && timeLeft < 0 {
|
||||||
// Certificate renewal failed, the operator is present, and the certificate
|
// Certificate renewal failed, the operator is present, and the certificate
|
||||||
|
|
Loading…
Reference in a new issue