mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05:00 
			
		
		
		
	caddytls: Use latest certmagic package, with updated Storage interface
This commit is contained in:
		
							parent
							
								
									0684cf8611
								
							
						
					
					
						commit
						0b83014ff8
					
				
							
								
								
									
										1
									
								
								caddy.go
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								caddy.go
									
									
									
									
									
								
							@ -487,7 +487,6 @@ func Start(cdyfile Input) (*Instance, error) {
 | 
				
			|||||||
			return nil, fmt.Errorf("constructing cluster plugin %s: %v", clusterPluginName, err)
 | 
								return nil, fmt.Errorf("constructing cluster plugin %s: %v", clusterPluginName, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		certmagic.DefaultStorage = storage
 | 
							certmagic.DefaultStorage = storage
 | 
				
			||||||
		OnProcessExit = append(OnProcessExit, certmagic.DefaultStorage.UnlockAllObtained)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	inst := &Instance{serverType: cdyfile.ServerType(), wg: new(sync.WaitGroup), Storage: make(map[interface{}]interface{})}
 | 
						inst := &Instance{serverType: cdyfile.ServerType(), wg: new(sync.WaitGroup), Storage: make(map[interface{}]interface{})}
 | 
				
			||||||
 | 
				
			|||||||
@ -432,5 +432,5 @@ func loadCertsInDir(cfg *Config, c *caddy.Controller, dir string) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func constructDefaultClusterPlugin() (certmagic.Storage, error) {
 | 
					func constructDefaultClusterPlugin() (certmagic.Storage, error) {
 | 
				
			||||||
	return certmagic.FileStorage{Path: caddy.AssetsPath()}, nil
 | 
						return &certmagic.FileStorage{Path: caddy.AssetsPath()}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										3
									
								
								vendor/github.com/mholt/certmagic/certificates.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/mholt/certmagic/certificates.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -64,12 +64,11 @@ func (c Certificate) NeedsRenewal() bool {
 | 
				
			|||||||
	if c.NotAfter.IsZero() {
 | 
						if c.NotAfter.IsZero() {
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	timeLeft := c.NotAfter.UTC().Sub(time.Now().UTC())
 | 
					 | 
				
			||||||
	renewDurationBefore := DefaultRenewDurationBefore
 | 
						renewDurationBefore := DefaultRenewDurationBefore
 | 
				
			||||||
	if len(c.configs) > 0 && c.configs[0].RenewDurationBefore > 0 {
 | 
						if len(c.configs) > 0 && c.configs[0].RenewDurationBefore > 0 {
 | 
				
			||||||
		renewDurationBefore = c.configs[0].RenewDurationBefore
 | 
							renewDurationBefore = c.configs[0].RenewDurationBefore
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return timeLeft < renewDurationBefore
 | 
						return time.Until(c.NotAfter) < renewDurationBefore
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CacheManagedCertificate loads the certificate for domain into the
 | 
					// CacheManagedCertificate loads the certificate for domain into the
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										35
									
								
								vendor/github.com/mholt/certmagic/certmagic.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/mholt/certmagic/certmagic.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -52,6 +52,16 @@ import (
 | 
				
			|||||||
// HTTPS serves mux for all domainNames using the HTTP
 | 
					// HTTPS serves mux for all domainNames using the HTTP
 | 
				
			||||||
// and HTTPS ports, redirecting all HTTP requests to HTTPS.
 | 
					// and HTTPS ports, redirecting all HTTP requests to HTTPS.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
 | 
					// This high-level convenience function is opinionated and
 | 
				
			||||||
 | 
					// applies sane defaults for production use, including
 | 
				
			||||||
 | 
					// timeouts for HTTP requests and responses. To allow very
 | 
				
			||||||
 | 
					// long-lived requests or connections, you should make your
 | 
				
			||||||
 | 
					// own http.Server values and use this package's Listen(),
 | 
				
			||||||
 | 
					// TLS(), or Config.TLSConfig() functions to customize to
 | 
				
			||||||
 | 
					// your needs. For example, servers which need to support
 | 
				
			||||||
 | 
					// large uploads or downloads with slow clients may need to
 | 
				
			||||||
 | 
					// use longer timeouts, thus this function is not suitable.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
// Calling this function signifies your acceptance to
 | 
					// Calling this function signifies your acceptance to
 | 
				
			||||||
// the CA's Subscriber Agreement and/or Terms of Service.
 | 
					// the CA's Subscriber Agreement and/or Terms of Service.
 | 
				
			||||||
func HTTPS(domainNames []string, mux http.Handler) error {
 | 
					func HTTPS(domainNames []string, mux http.Handler) error {
 | 
				
			||||||
@ -96,13 +106,32 @@ func HTTPS(domainNames []string, mux http.Handler) error {
 | 
				
			|||||||
	hln, hsln := httpLn, httpsLn
 | 
						hln, hsln := httpLn, httpsLn
 | 
				
			||||||
	lnMu.Unlock()
 | 
						lnMu.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	httpHandler := cfg.HTTPChallengeHandler(http.HandlerFunc(httpRedirectHandler))
 | 
						// create HTTP/S servers that are configured
 | 
				
			||||||
 | 
						// with sane default timeouts and appropriate
 | 
				
			||||||
 | 
						// handlers (the HTTP server solves the HTTP
 | 
				
			||||||
 | 
						// challenge and issues redirects to HTTPS,
 | 
				
			||||||
 | 
						// while the HTTPS server simply serves the
 | 
				
			||||||
 | 
						// user's handler)
 | 
				
			||||||
 | 
						httpServer := &http.Server{
 | 
				
			||||||
 | 
							ReadHeaderTimeout: 5 * time.Second,
 | 
				
			||||||
 | 
							ReadTimeout:       5 * time.Second,
 | 
				
			||||||
 | 
							WriteTimeout:      5 * time.Second,
 | 
				
			||||||
 | 
							IdleTimeout:       5 * time.Second,
 | 
				
			||||||
 | 
							Handler:           cfg.HTTPChallengeHandler(http.HandlerFunc(httpRedirectHandler)),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						httpsServer := &http.Server{
 | 
				
			||||||
 | 
							ReadHeaderTimeout: 10 * time.Second,
 | 
				
			||||||
 | 
							ReadTimeout:       30 * time.Second,
 | 
				
			||||||
 | 
							WriteTimeout:      2 * time.Minute,
 | 
				
			||||||
 | 
							IdleTimeout:       5 * time.Minute,
 | 
				
			||||||
 | 
							Handler:           mux,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Printf("%v Serving HTTP->HTTPS on %s and %s",
 | 
						log.Printf("%v Serving HTTP->HTTPS on %s and %s",
 | 
				
			||||||
		domainNames, hln.Addr(), hsln.Addr())
 | 
							domainNames, hln.Addr(), hsln.Addr())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	go http.Serve(hln, httpHandler)
 | 
						go httpServer.Serve(hln)
 | 
				
			||||||
	return http.Serve(hsln, mux)
 | 
						return httpsServer.Serve(hsln)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func httpRedirectHandler(w http.ResponseWriter, r *http.Request) {
 | 
					func httpRedirectHandler(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										44
									
								
								vendor/github.com/mholt/certmagic/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										44
									
								
								vendor/github.com/mholt/certmagic/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -208,6 +208,8 @@ func (cfg *Config) newACMEClient(interactive bool) (*acmeClient, error) {
 | 
				
			|||||||
	return c, nil
 | 
						return c, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// lockKey returns a key for a lock that is specific to the operation
 | 
				
			||||||
 | 
					// named op being performed related to domainName and this config's CA.
 | 
				
			||||||
func (cfg *Config) lockKey(op, domainName string) string {
 | 
					func (cfg *Config) lockKey(op, domainName string) string {
 | 
				
			||||||
	return fmt.Sprintf("%s:%s:%s", op, domainName, cfg.CA)
 | 
						return fmt.Sprintf("%s:%s:%s", op, domainName, cfg.CA)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -215,30 +217,34 @@ func (cfg *Config) lockKey(op, domainName string) string {
 | 
				
			|||||||
// Obtain obtains a single certificate for name. It stores the certificate
 | 
					// Obtain obtains a single certificate for name. It stores the certificate
 | 
				
			||||||
// on the disk if successful. This function is safe for concurrent use.
 | 
					// on the disk if successful. This function is safe for concurrent use.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Right now our storage mechanism only supports one name per certificate,
 | 
					// Our storage mechanism only supports one name per certificate, so this
 | 
				
			||||||
// so this function (along with Renew and Revoke) only accepts one domain
 | 
					// function (along with Renew and Revoke) only accepts one domain as input.
 | 
				
			||||||
// as input. It can be easily modified to support SAN certificates if our
 | 
					// It could be easily modified to support SAN certificates if our storage
 | 
				
			||||||
// storage mechanism is upgraded later.
 | 
					// mechanism is upgraded later, but that will increase logical complexity
 | 
				
			||||||
 | 
					// in other areas.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Callers who have access to a Config value should use the ObtainCert
 | 
					// Callers who have access to a Config value should use the ObtainCert
 | 
				
			||||||
// method on that instead of this lower-level method.
 | 
					// method on that instead of this lower-level method.
 | 
				
			||||||
func (c *acmeClient) Obtain(name string) error {
 | 
					func (c *acmeClient) Obtain(name string) error {
 | 
				
			||||||
 | 
						// ensure idempotency of the obtain operation for this name
 | 
				
			||||||
	lockKey := c.config.lockKey("cert_acme", name)
 | 
						lockKey := c.config.lockKey("cert_acme", name)
 | 
				
			||||||
	waiter, err := c.config.certCache.storage.TryLock(lockKey)
 | 
						err := c.config.certCache.storage.Lock(lockKey)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if waiter != nil {
 | 
					 | 
				
			||||||
		log.Printf("[INFO] Certificate for %s is already being obtained elsewhere and stored; waiting", name)
 | 
					 | 
				
			||||||
		waiter.Wait()
 | 
					 | 
				
			||||||
		return nil // we assume the process with the lock succeeded, rather than hammering this execution path again
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		if err := c.config.certCache.storage.Unlock(lockKey); err != nil {
 | 
							if err := c.config.certCache.storage.Unlock(lockKey); err != nil {
 | 
				
			||||||
			log.Printf("[ERROR] Unable to unlock obtain call for %s: %v", name, err)
 | 
								log.Printf("[ERROR][%s] Obtain: Unable to unlock '%s': %v", name, lockKey, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check if obtain is still needed -- might have
 | 
				
			||||||
 | 
						// been obtained during lock
 | 
				
			||||||
 | 
						if c.config.storageHasCertResources(name) {
 | 
				
			||||||
 | 
							log.Printf("[INFO][%s] Obtain: Certificate already exists in storage", name)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for attempts := 0; attempts < 2; attempts++ {
 | 
						for attempts := 0; attempts < 2; attempts++ {
 | 
				
			||||||
		request := certificate.ObtainRequest{
 | 
							request := certificate.ObtainRequest{
 | 
				
			||||||
			Domains:    []string{name},
 | 
								Domains:    []string{name},
 | 
				
			||||||
@ -280,19 +286,15 @@ func (c *acmeClient) Obtain(name string) error {
 | 
				
			|||||||
// Callers who have access to a Config value should use the RenewCert
 | 
					// Callers who have access to a Config value should use the RenewCert
 | 
				
			||||||
// method on that instead of this lower-level method.
 | 
					// method on that instead of this lower-level method.
 | 
				
			||||||
func (c *acmeClient) Renew(name string) error {
 | 
					func (c *acmeClient) Renew(name string) error {
 | 
				
			||||||
 | 
						// ensure idempotency of the renew operation for this name
 | 
				
			||||||
	lockKey := c.config.lockKey("cert_acme", name)
 | 
						lockKey := c.config.lockKey("cert_acme", name)
 | 
				
			||||||
	waiter, err := c.config.certCache.storage.TryLock(lockKey)
 | 
						err := c.config.certCache.storage.Lock(lockKey)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if waiter != nil {
 | 
					 | 
				
			||||||
		log.Printf("[INFO] Certificate for %s is already being renewed elsewhere and stored; waiting", name)
 | 
					 | 
				
			||||||
		waiter.Wait()
 | 
					 | 
				
			||||||
		return nil // assume that the worker that renewed the cert succeeded to avoid hammering this path over and over
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		if err := c.config.certCache.storage.Unlock(lockKey); err != nil {
 | 
							if err := c.config.certCache.storage.Unlock(lockKey); err != nil {
 | 
				
			||||||
			log.Printf("[ERROR] Unable to unlock renew call for %s: %v", name, err)
 | 
								log.Printf("[ERROR][%s] Renew: Unable to unlock '%s': %v", name, lockKey, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -302,6 +304,12 @@ func (c *acmeClient) Renew(name string) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check if renew is still needed - might have been renewed while waiting for lock
 | 
				
			||||||
 | 
						if !c.config.managedCertNeedsRenewal(certRes) {
 | 
				
			||||||
 | 
							log.Printf("[INFO][%s] Renew: Certificate appears to have been renewed already", name)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Perform renewal and retry if necessary, but not too many times.
 | 
						// Perform renewal and retry if necessary, but not too many times.
 | 
				
			||||||
	var newCertMeta *certificate.Resource
 | 
						var newCertMeta *certificate.Resource
 | 
				
			||||||
	var success bool
 | 
						var success bool
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										66
									
								
								vendor/github.com/mholt/certmagic/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										66
									
								
								vendor/github.com/mholt/certmagic/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -21,6 +21,7 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/xenolf/lego/certcrypto"
 | 
						"github.com/xenolf/lego/certcrypto"
 | 
				
			||||||
 | 
						"github.com/xenolf/lego/certificate"
 | 
				
			||||||
	"github.com/xenolf/lego/challenge"
 | 
						"github.com/xenolf/lego/challenge"
 | 
				
			||||||
	"github.com/xenolf/lego/challenge/tlsalpn01"
 | 
						"github.com/xenolf/lego/challenge/tlsalpn01"
 | 
				
			||||||
	"github.com/xenolf/lego/lego"
 | 
						"github.com/xenolf/lego/lego"
 | 
				
			||||||
@ -277,12 +278,6 @@ func (cfg *Config) ObtainCert(name string, interactive bool) error {
 | 
				
			|||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// we expect this to be a new site; if the
 | 
					 | 
				
			||||||
	// cert already exists, then no-op
 | 
					 | 
				
			||||||
	if cfg.certCache.storage.Exists(StorageKeys.SiteCert(cfg.CA, name)) {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	client, err := cfg.newACMEClient(interactive)
 | 
						client, err := cfg.newACMEClient(interactive)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@ -317,24 +312,37 @@ func (cfg *Config) RevokeCert(domain string, interactive bool) error {
 | 
				
			|||||||
	return client.Revoke(domain)
 | 
						return client.Revoke(domain)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TLSConfig returns a TLS configuration that
 | 
					// TLSConfig is an opinionated method that returns a
 | 
				
			||||||
// can be used to configure TLS listeners. It
 | 
					// recommended, modern TLS configuration that can be
 | 
				
			||||||
// supports the TLS-ALPN challenge and serves
 | 
					// used to configure TLS listeners, which also supports
 | 
				
			||||||
// up certificates managed by cfg.
 | 
					// the TLS-ALPN challenge and serves up certificates
 | 
				
			||||||
 | 
					// managed by cfg.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unlike the package TLS() function, this method does
 | 
				
			||||||
 | 
					// not, by itself, enable certificate management for
 | 
				
			||||||
 | 
					// any domain names.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Feel free to further customize the returned tls.Config,
 | 
				
			||||||
 | 
					// but do not mess with the GetCertificate or NextProtos
 | 
				
			||||||
 | 
					// fields unless you know what you're doing, as they're
 | 
				
			||||||
 | 
					// necessary to solve the TLS-ALPN challenge.
 | 
				
			||||||
func (cfg *Config) TLSConfig() *tls.Config {
 | 
					func (cfg *Config) TLSConfig() *tls.Config {
 | 
				
			||||||
	return &tls.Config{
 | 
						return &tls.Config{
 | 
				
			||||||
 | 
							// these two fields necessary for TLS-ALPN challenge
 | 
				
			||||||
		GetCertificate: cfg.GetCertificate,
 | 
							GetCertificate: cfg.GetCertificate,
 | 
				
			||||||
		NextProtos:     []string{"h2", "http/1.1", tlsalpn01.ACMETLS1Protocol},
 | 
							NextProtos:     []string{"h2", "http/1.1", tlsalpn01.ACMETLS1Protocol},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// the rest recommended for modern TLS servers
 | 
				
			||||||
 | 
							MinVersion: tls.VersionTLS12,
 | 
				
			||||||
 | 
							CurvePreferences: []tls.CurveID{
 | 
				
			||||||
 | 
								tls.X25519,
 | 
				
			||||||
 | 
								tls.CurveP256,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							CipherSuites:             preferredDefaultCipherSuites(),
 | 
				
			||||||
 | 
							PreferServerCipherSuites: true,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RenewAllCerts triggers a renewal check of all
 | 
					 | 
				
			||||||
// certificates in the cache. It only renews
 | 
					 | 
				
			||||||
// certificates if they need to be renewed.
 | 
					 | 
				
			||||||
// func (cfg *Config) RenewAllCerts(interactive bool) error {
 | 
					 | 
				
			||||||
// 	return cfg.certCache.RenewManagedCertificates(interactive)
 | 
					 | 
				
			||||||
// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// preObtainOrRenewChecks perform a few simple checks before
 | 
					// preObtainOrRenewChecks perform a few simple checks before
 | 
				
			||||||
// obtaining or renewing a certificate with ACME, and returns
 | 
					// obtaining or renewing a certificate with ACME, and returns
 | 
				
			||||||
// whether this name should be skipped (like if it's not
 | 
					// whether this name should be skipped (like if it's not
 | 
				
			||||||
@ -356,3 +364,27 @@ func (cfg *Config) preObtainOrRenewChecks(name string, allowPrompts bool) (bool,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return false, nil
 | 
						return false, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// storageHasCertResources returns true if the storage
 | 
				
			||||||
 | 
					// associated with cfg's certificate cache has all the
 | 
				
			||||||
 | 
					// resources related to the certificate for domain: the
 | 
				
			||||||
 | 
					// certificate, the private key, and the metadata.
 | 
				
			||||||
 | 
					func (cfg *Config) storageHasCertResources(domain string) bool {
 | 
				
			||||||
 | 
						certKey := StorageKeys.SiteCert(cfg.CA, domain)
 | 
				
			||||||
 | 
						keyKey := StorageKeys.SitePrivateKey(cfg.CA, domain)
 | 
				
			||||||
 | 
						metaKey := StorageKeys.SiteMeta(cfg.CA, domain)
 | 
				
			||||||
 | 
						return cfg.certCache.storage.Exists(certKey) &&
 | 
				
			||||||
 | 
							cfg.certCache.storage.Exists(keyKey) &&
 | 
				
			||||||
 | 
							cfg.certCache.storage.Exists(metaKey)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// managedCertNeedsRenewal returns true if certRes is
 | 
				
			||||||
 | 
					// expiring soon or already expired, or if the process
 | 
				
			||||||
 | 
					// of checking the expiration returned an error.
 | 
				
			||||||
 | 
					func (cfg *Config) managedCertNeedsRenewal(certRes certificate.Resource) bool {
 | 
				
			||||||
 | 
						cert, err := cfg.makeCertificate(certRes.Certificate, certRes.PrivateKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cert.NeedsRenewal()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										33
									
								
								vendor/github.com/mholt/certmagic/crypto.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/mholt/certmagic/crypto.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -19,12 +19,14 @@ import (
 | 
				
			|||||||
	"crypto/ecdsa"
 | 
						"crypto/ecdsa"
 | 
				
			||||||
	"crypto/rsa"
 | 
						"crypto/rsa"
 | 
				
			||||||
	"crypto/sha256"
 | 
						"crypto/sha256"
 | 
				
			||||||
 | 
						"crypto/tls"
 | 
				
			||||||
	"crypto/x509"
 | 
						"crypto/x509"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"encoding/pem"
 | 
						"encoding/pem"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"hash/fnv"
 | 
						"hash/fnv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/klauspost/cpuid"
 | 
				
			||||||
	"github.com/xenolf/lego/certificate"
 | 
						"github.com/xenolf/lego/certificate"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -153,3 +155,34 @@ func hashCertificateChain(certChain [][]byte) string {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return fmt.Sprintf("%x", h.Sum(nil))
 | 
						return fmt.Sprintf("%x", h.Sum(nil))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// preferredDefaultCipherSuites returns an appropriate
 | 
				
			||||||
 | 
					// cipher suite to use depending on hardware support
 | 
				
			||||||
 | 
					// for AES-NI.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// See https://github.com/mholt/caddy/issues/1674
 | 
				
			||||||
 | 
					func preferredDefaultCipherSuites() []uint16 {
 | 
				
			||||||
 | 
						if cpuid.CPU.AesNi() {
 | 
				
			||||||
 | 
							return defaultCiphersPreferAES
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return defaultCiphersPreferChaCha
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						defaultCiphersPreferAES = []uint16{
 | 
				
			||||||
 | 
							tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
 | 
				
			||||||
 | 
							tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
 | 
				
			||||||
 | 
							tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
 | 
				
			||||||
 | 
							tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
 | 
				
			||||||
 | 
							tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
 | 
				
			||||||
 | 
							tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultCiphersPreferChaCha = []uint16{
 | 
				
			||||||
 | 
							tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
 | 
				
			||||||
 | 
							tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
 | 
				
			||||||
 | 
							tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
 | 
				
			||||||
 | 
							tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
 | 
				
			||||||
 | 
							tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
 | 
				
			||||||
 | 
							tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										293
									
								
								vendor/github.com/mholt/certmagic/filestorage.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										293
									
								
								vendor/github.com/mholt/certmagic/filestorage.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -22,7 +22,6 @@ import (
 | 
				
			|||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -34,13 +33,13 @@ type FileStorage struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Exists returns true if key exists in fs.
 | 
					// Exists returns true if key exists in fs.
 | 
				
			||||||
func (fs FileStorage) Exists(key string) bool {
 | 
					func (fs *FileStorage) Exists(key string) bool {
 | 
				
			||||||
	_, err := os.Stat(fs.Filename(key))
 | 
						_, err := os.Stat(fs.Filename(key))
 | 
				
			||||||
	return !os.IsNotExist(err)
 | 
						return !os.IsNotExist(err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Store saves value at key.
 | 
					// Store saves value at key.
 | 
				
			||||||
func (fs FileStorage) Store(key string, value []byte) error {
 | 
					func (fs *FileStorage) Store(key string, value []byte) error {
 | 
				
			||||||
	filename := fs.Filename(key)
 | 
						filename := fs.Filename(key)
 | 
				
			||||||
	err := os.MkdirAll(filepath.Dir(filename), 0700)
 | 
						err := os.MkdirAll(filepath.Dir(filename), 0700)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@ -50,7 +49,7 @@ func (fs FileStorage) Store(key string, value []byte) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Load retrieves the value at key.
 | 
					// Load retrieves the value at key.
 | 
				
			||||||
func (fs FileStorage) Load(key string) ([]byte, error) {
 | 
					func (fs *FileStorage) Load(key string) ([]byte, error) {
 | 
				
			||||||
	contents, err := ioutil.ReadFile(fs.Filename(key))
 | 
						contents, err := ioutil.ReadFile(fs.Filename(key))
 | 
				
			||||||
	if os.IsNotExist(err) {
 | 
						if os.IsNotExist(err) {
 | 
				
			||||||
		return nil, ErrNotExist(err)
 | 
							return nil, ErrNotExist(err)
 | 
				
			||||||
@ -59,8 +58,7 @@ func (fs FileStorage) Load(key string) ([]byte, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Delete deletes the value at key.
 | 
					// Delete deletes the value at key.
 | 
				
			||||||
// TODO: Delete any empty folders caused by this operation
 | 
					func (fs *FileStorage) Delete(key string) error {
 | 
				
			||||||
func (fs FileStorage) Delete(key string) error {
 | 
					 | 
				
			||||||
	err := os.Remove(fs.Filename(key))
 | 
						err := os.Remove(fs.Filename(key))
 | 
				
			||||||
	if os.IsNotExist(err) {
 | 
						if os.IsNotExist(err) {
 | 
				
			||||||
		return ErrNotExist(err)
 | 
							return ErrNotExist(err)
 | 
				
			||||||
@ -69,7 +67,7 @@ func (fs FileStorage) Delete(key string) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// List returns all keys that match prefix.
 | 
					// List returns all keys that match prefix.
 | 
				
			||||||
func (fs FileStorage) List(prefix string, recursive bool) ([]string, error) {
 | 
					func (fs *FileStorage) List(prefix string, recursive bool) ([]string, error) {
 | 
				
			||||||
	var keys []string
 | 
						var keys []string
 | 
				
			||||||
	walkPrefix := fs.Filename(prefix)
 | 
						walkPrefix := fs.Filename(prefix)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -100,7 +98,7 @@ func (fs FileStorage) List(prefix string, recursive bool) ([]string, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Stat returns information about key.
 | 
					// Stat returns information about key.
 | 
				
			||||||
func (fs FileStorage) Stat(key string) (KeyInfo, error) {
 | 
					func (fs *FileStorage) Stat(key string) (KeyInfo, error) {
 | 
				
			||||||
	fi, err := os.Stat(fs.Filename(key))
 | 
						fi, err := os.Stat(fs.Filename(key))
 | 
				
			||||||
	if os.IsNotExist(err) {
 | 
						if os.IsNotExist(err) {
 | 
				
			||||||
		return KeyInfo{}, ErrNotExist(err)
 | 
							return KeyInfo{}, ErrNotExist(err)
 | 
				
			||||||
@ -118,10 +116,127 @@ func (fs FileStorage) Stat(key string) (KeyInfo, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Filename returns the key as a path on the file
 | 
					// Filename returns the key as a path on the file
 | 
				
			||||||
// system prefixed by fs.Path.
 | 
					// system prefixed by fs.Path.
 | 
				
			||||||
func (fs FileStorage) Filename(key string) string {
 | 
					func (fs *FileStorage) Filename(key string) string {
 | 
				
			||||||
	return filepath.Join(fs.Path, filepath.FromSlash(key))
 | 
						return filepath.Join(fs.Path, filepath.FromSlash(key))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Lock obtains a lock named by the given key. It blocks
 | 
				
			||||||
 | 
					// until the lock can be obtained or an error is returned.
 | 
				
			||||||
 | 
					func (fs *FileStorage) Lock(key string) error {
 | 
				
			||||||
 | 
						start := time.Now()
 | 
				
			||||||
 | 
						filename := fs.lockFilename(key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							err := createLockfile(filename)
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								// got the lock, yay
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !os.IsExist(err) {
 | 
				
			||||||
 | 
								// unexpected error
 | 
				
			||||||
 | 
								return fmt.Errorf("creating lock file: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// lock file already exists
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							info, err := os.Stat(filename)
 | 
				
			||||||
 | 
							switch {
 | 
				
			||||||
 | 
							case os.IsNotExist(err):
 | 
				
			||||||
 | 
								// must have just been removed; try again to create it
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case err != nil:
 | 
				
			||||||
 | 
								// unexpected error
 | 
				
			||||||
 | 
								return fmt.Errorf("accessing lock file: %v", err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case fileLockIsStale(info):
 | 
				
			||||||
 | 
								// lock file is stale - delete it and try again to create one
 | 
				
			||||||
 | 
								log.Printf("[INFO][%s] Lock for '%s' is stale; removing then retrying: %s",
 | 
				
			||||||
 | 
									fs, key, filename)
 | 
				
			||||||
 | 
								removeLockfile(filename)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case time.Since(start) > staleLockDuration*2:
 | 
				
			||||||
 | 
								// should never happen, hopefully
 | 
				
			||||||
 | 
								return fmt.Errorf("possible deadlock: %s passed trying to obtain lock for %s",
 | 
				
			||||||
 | 
									time.Since(start), key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								// lockfile exists and is not stale;
 | 
				
			||||||
 | 
								// just wait a moment and try again
 | 
				
			||||||
 | 
								time.Sleep(fileLockPollInterval)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Unlock releases the lock for name.
 | 
				
			||||||
 | 
					func (fs *FileStorage) Unlock(key string) error {
 | 
				
			||||||
 | 
						return removeLockfile(fs.lockFilename(key))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (fs *FileStorage) String() string {
 | 
				
			||||||
 | 
						return "FileStorage:" + fs.Path
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (fs *FileStorage) lockFilename(key string) string {
 | 
				
			||||||
 | 
						return filepath.Join(fs.lockDir(), StorageKeys.safe(key)+".lock")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (fs *FileStorage) lockDir() string {
 | 
				
			||||||
 | 
						return filepath.Join(fs.Path, "locks")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func fileLockIsStale(info os.FileInfo) bool {
 | 
				
			||||||
 | 
						if info == nil {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return time.Since(info.ModTime()) > staleLockDuration
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// createLockfile atomically creates the lockfile
 | 
				
			||||||
 | 
					// identified by filename. A successfully created
 | 
				
			||||||
 | 
					// lockfile should be removed with removeLockfile.
 | 
				
			||||||
 | 
					func createLockfile(filename string) error {
 | 
				
			||||||
 | 
						err := atomicallyCreateFile(filename)
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							// if the app crashes in removeLockfile(), there is a
 | 
				
			||||||
 | 
							// small chance the .unlock file is left behind; it's
 | 
				
			||||||
 | 
							// safe to simply remove it as it's a guard against
 | 
				
			||||||
 | 
							// double removal of the .lock file.
 | 
				
			||||||
 | 
							os.Remove(filename + ".unlock")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// removeLockfile atomically removes filename,
 | 
				
			||||||
 | 
					// which must be a lockfile created by createLockfile.
 | 
				
			||||||
 | 
					// See discussion in PR #7 for more background:
 | 
				
			||||||
 | 
					// https://github.com/mholt/certmagic/pull/7
 | 
				
			||||||
 | 
					func removeLockfile(filename string) error {
 | 
				
			||||||
 | 
						unlockFilename := filename + ".unlock"
 | 
				
			||||||
 | 
						if err := atomicallyCreateFile(unlockFilename); err != nil {
 | 
				
			||||||
 | 
							if os.IsExist(err) {
 | 
				
			||||||
 | 
								// another process is handling the unlocking
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer os.Remove(unlockFilename)
 | 
				
			||||||
 | 
						return os.Remove(filename)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// atomicallyCreateFile atomically creates the file
 | 
				
			||||||
 | 
					// identified by filename if it doesn't already exist.
 | 
				
			||||||
 | 
					func atomicallyCreateFile(filename string) error {
 | 
				
			||||||
 | 
						// no need to check this, we only really care about the file creation error
 | 
				
			||||||
 | 
						os.MkdirAll(filepath.Dir(filename), 0700)
 | 
				
			||||||
 | 
						f, err := os.OpenFile(filename, os.O_CREATE|os.O_EXCL, 0644)
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							f.Close()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// homeDir returns the best guess of the current user's home
 | 
					// homeDir returns the best guess of the current user's home
 | 
				
			||||||
// directory from environment variables. If unknown, "." (the
 | 
					// directory from environment variables. If unknown, "." (the
 | 
				
			||||||
// current directory) is returned instead.
 | 
					// current directory) is returned instead.
 | 
				
			||||||
@ -149,160 +264,12 @@ func dataDir() string {
 | 
				
			|||||||
	return filepath.Join(baseDir, "certmagic")
 | 
						return filepath.Join(baseDir, "certmagic")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TryLock attempts to get a lock for name, otherwise it returns
 | 
					 | 
				
			||||||
// a Waiter value to wait until the other process is finished.
 | 
					 | 
				
			||||||
func (fs FileStorage) TryLock(key string) (Waiter, error) {
 | 
					 | 
				
			||||||
	fileStorageNameLocksMu.Lock()
 | 
					 | 
				
			||||||
	defer fileStorageNameLocksMu.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// see if lock already exists within this process - allows
 | 
					 | 
				
			||||||
	// for faster unlocking since we don't have to poll the disk
 | 
					 | 
				
			||||||
	fw, ok := fileStorageNameLocks[key]
 | 
					 | 
				
			||||||
	if ok {
 | 
					 | 
				
			||||||
		// lock already created within process, let caller wait on it
 | 
					 | 
				
			||||||
		return fw, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// attempt to persist lock to disk by creating lock file
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// parent dir must exist
 | 
					 | 
				
			||||||
	lockDir := fs.lockDir()
 | 
					 | 
				
			||||||
	if err := os.MkdirAll(lockDir, 0700); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fw = &fileStorageWaiter{
 | 
					 | 
				
			||||||
		key:      key,
 | 
					 | 
				
			||||||
		filename: filepath.Join(lockDir, StorageKeys.safe(key)+".lock"),
 | 
					 | 
				
			||||||
		wg:       new(sync.WaitGroup),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var checkedStaleLock bool // sentinel value to avoid infinite goto-ing
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
createLock:
 | 
					 | 
				
			||||||
	// create the file in a special mode such that an
 | 
					 | 
				
			||||||
	// error is returned if it already exists
 | 
					 | 
				
			||||||
	lf, err := os.OpenFile(fw.filename, os.O_CREATE|os.O_EXCL, 0644)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		if os.IsExist(err) {
 | 
					 | 
				
			||||||
			// another process has the lock
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// check to see if the lock is stale, if we haven't already
 | 
					 | 
				
			||||||
			if !checkedStaleLock {
 | 
					 | 
				
			||||||
				checkedStaleLock = true
 | 
					 | 
				
			||||||
				if fs.lockFileStale(fw.filename) {
 | 
					 | 
				
			||||||
					log.Printf("[INFO][%s] Lock for '%s' is stale; removing then retrying: %s",
 | 
					 | 
				
			||||||
						fs, key, fw.filename)
 | 
					 | 
				
			||||||
					os.Remove(fw.filename)
 | 
					 | 
				
			||||||
					goto createLock
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// if lock is not stale, wait upon it
 | 
					 | 
				
			||||||
			return fw, nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// otherwise, this was some unexpected error
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	lf.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// looks like we get the lock
 | 
					 | 
				
			||||||
	fw.wg.Add(1)
 | 
					 | 
				
			||||||
	fileStorageNameLocks[key] = fw
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Unlock releases the lock for name.
 | 
					 | 
				
			||||||
func (fs FileStorage) Unlock(key string) error {
 | 
					 | 
				
			||||||
	fileStorageNameLocksMu.Lock()
 | 
					 | 
				
			||||||
	defer fileStorageNameLocksMu.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fw, ok := fileStorageNameLocks[key]
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return fmt.Errorf("FileStorage: no lock to release for %s", key)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// remove lock file
 | 
					 | 
				
			||||||
	os.Remove(fw.filename)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// if parent folder is now empty, remove it too to keep it tidy
 | 
					 | 
				
			||||||
	dir, err := os.Open(fs.lockDir()) // OK to ignore error here
 | 
					 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		items, _ := dir.Readdirnames(3) // OK to ignore error here
 | 
					 | 
				
			||||||
		if len(items) == 0 {
 | 
					 | 
				
			||||||
			os.Remove(dir.Name())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		dir.Close()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// clean up in memory
 | 
					 | 
				
			||||||
	fw.wg.Done()
 | 
					 | 
				
			||||||
	delete(fileStorageNameLocks, key)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// UnlockAllObtained removes all locks obtained by
 | 
					 | 
				
			||||||
// this instance of fs.
 | 
					 | 
				
			||||||
func (fs FileStorage) UnlockAllObtained() {
 | 
					 | 
				
			||||||
	for key, fw := range fileStorageNameLocks {
 | 
					 | 
				
			||||||
		err := fs.Unlock(fw.key)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Printf("[ERROR][%s] Releasing obtained lock for %s: %v", fs, key, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (fs FileStorage) lockFileStale(filename string) bool {
 | 
					 | 
				
			||||||
	info, err := os.Stat(filename)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return true // no good way to handle this, really; lock is useless?
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return time.Since(info.ModTime()) > staleLockDuration
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (fs FileStorage) lockDir() string {
 | 
					 | 
				
			||||||
	return filepath.Join(fs.Path, "locks")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (fs FileStorage) String() string {
 | 
					 | 
				
			||||||
	return "FileStorage:" + fs.Path
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// fileStorageWaiter waits for a file to disappear; it
 | 
					 | 
				
			||||||
// polls the file system to check for the existence of
 | 
					 | 
				
			||||||
// a file. It also uses a WaitGroup to optimize the
 | 
					 | 
				
			||||||
// polling in the case when this process is the only
 | 
					 | 
				
			||||||
// one waiting. (Other processes that are waiting for
 | 
					 | 
				
			||||||
// the lock will still block, but must wait for the
 | 
					 | 
				
			||||||
// polling to get their answer.)
 | 
					 | 
				
			||||||
type fileStorageWaiter struct {
 | 
					 | 
				
			||||||
	key      string
 | 
					 | 
				
			||||||
	filename string
 | 
					 | 
				
			||||||
	wg       *sync.WaitGroup
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Wait waits until the lock is released.
 | 
					 | 
				
			||||||
func (fw *fileStorageWaiter) Wait() {
 | 
					 | 
				
			||||||
	start := time.Now()
 | 
					 | 
				
			||||||
	fw.wg.Wait()
 | 
					 | 
				
			||||||
	for time.Since(start) < 1*time.Hour {
 | 
					 | 
				
			||||||
		_, err := os.Stat(fw.filename)
 | 
					 | 
				
			||||||
		if os.IsNotExist(err) {
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		time.Sleep(1 * time.Second)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var fileStorageNameLocks = make(map[string]*fileStorageWaiter)
 | 
					 | 
				
			||||||
var fileStorageNameLocksMu sync.Mutex
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _ Storage = FileStorage{}
 | 
					 | 
				
			||||||
var _ Waiter = &fileStorageWaiter{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// staleLockDuration is the length of time
 | 
					// staleLockDuration is the length of time
 | 
				
			||||||
// before considering a lock to be stale.
 | 
					// before considering a lock to be stale.
 | 
				
			||||||
const staleLockDuration = 2 * time.Hour
 | 
					const staleLockDuration = 2 * time.Hour
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// fileLockPollInterval is how frequently
 | 
				
			||||||
 | 
					// to check the existence of a lock file
 | 
				
			||||||
 | 
					const fileLockPollInterval = 1 * time.Second
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ Storage = (*FileStorage)(nil)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										34
									
								
								vendor/github.com/mholt/certmagic/storage.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										34
									
								
								vendor/github.com/mholt/certmagic/storage.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -64,22 +64,24 @@ type Storage interface {
 | 
				
			|||||||
// Locker facilitates synchronization of certificate tasks across
 | 
					// Locker facilitates synchronization of certificate tasks across
 | 
				
			||||||
// machines and networks.
 | 
					// machines and networks.
 | 
				
			||||||
type Locker interface {
 | 
					type Locker interface {
 | 
				
			||||||
	// TryLock will attempt to acquire the lock for key. If a
 | 
						// Lock acquires the lock for key, blocking until the lock
 | 
				
			||||||
	// lock could be obtained, nil values are returned as no
 | 
						// can be obtained or an error is returned. Note that, even
 | 
				
			||||||
	// waiting is required. If not (meaning another process is
 | 
						// after acquiring a lock, an idempotent operation may have
 | 
				
			||||||
	// already working on key), a Waiter value will be returned,
 | 
						// already been performed by another process that acquired
 | 
				
			||||||
	// upon which you should Wait() until it is finished.
 | 
						// the lock before - so always check to make sure idempotent
 | 
				
			||||||
 | 
						// operations still need to be performed after acquiring the
 | 
				
			||||||
 | 
						// lock.
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// The actual implementation of obtaining of a lock must be
 | 
						// The actual implementation of obtaining of a lock must be
 | 
				
			||||||
	// an atomic operation so that multiple TryLock calls at the
 | 
						// an atomic operation so that multiple Lock calls at the
 | 
				
			||||||
	// same time always results in only one caller receiving the
 | 
						// same time always results in only one caller receiving the
 | 
				
			||||||
	// lock. TryLock always returns without waiting.
 | 
						// lock at any given time.
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// To prevent deadlocks, all implementations (where this concern
 | 
						// To prevent deadlocks, all implementations (where this concern
 | 
				
			||||||
	// is relevant) should put a reasonable expiration on the lock in
 | 
						// is relevant) should put a reasonable expiration on the lock in
 | 
				
			||||||
	// case Unlock is unable to be called due to some sort of network
 | 
						// case Unlock is unable to be called due to some sort of network
 | 
				
			||||||
	// or system failure or crash.
 | 
						// or system failure or crash.
 | 
				
			||||||
	TryLock(key string) (Waiter, error)
 | 
						Lock(key string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Unlock releases the lock for key. This method must ONLY be
 | 
						// Unlock releases the lock for key. This method must ONLY be
 | 
				
			||||||
	// called after a successful call to TryLock where no Waiter was
 | 
						// called after a successful call to TryLock where no Waiter was
 | 
				
			||||||
@ -89,20 +91,6 @@ type Locker interface {
 | 
				
			|||||||
	// TryLock or if Unlock was not called at all. Unlock should also
 | 
						// TryLock or if Unlock was not called at all. Unlock should also
 | 
				
			||||||
	// clean up any unused resources allocated during TryLock.
 | 
						// clean up any unused resources allocated during TryLock.
 | 
				
			||||||
	Unlock(key string) error
 | 
						Unlock(key string) error
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// UnlockAllObtained removes all locks obtained by this process,
 | 
					 | 
				
			||||||
	// upon which others may be waiting. The importer should call
 | 
					 | 
				
			||||||
	// this on shutdowns (and crashes, ideally) to avoid leaving stale
 | 
					 | 
				
			||||||
	// locks, but Locker implementations must NOT rely on this being
 | 
					 | 
				
			||||||
	// the case and should anticipate and handle stale locks. Errors
 | 
					 | 
				
			||||||
	// should be printed or logged, since there could be multiple,
 | 
					 | 
				
			||||||
	// with no good way to handle them anyway.
 | 
					 | 
				
			||||||
	UnlockAllObtained()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Waiter is a type that can block until a lock is released.
 | 
					 | 
				
			||||||
type Waiter interface {
 | 
					 | 
				
			||||||
	Wait()
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// KeyInfo holds information about a key in storage.
 | 
					// KeyInfo holds information about a key in storage.
 | 
				
			||||||
@ -281,7 +269,7 @@ type ErrNotExist interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// defaultFileStorage is a convenient, default storage
 | 
					// defaultFileStorage is a convenient, default storage
 | 
				
			||||||
// implementation using the local file system.
 | 
					// implementation using the local file system.
 | 
				
			||||||
var defaultFileStorage = FileStorage{Path: dataDir()}
 | 
					var defaultFileStorage = &FileStorage{Path: dataDir()}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DefaultStorage is the default Storage implementation.
 | 
					// DefaultStorage is the default Storage implementation.
 | 
				
			||||||
var DefaultStorage Storage = defaultFileStorage
 | 
					var DefaultStorage Storage = defaultFileStorage
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								vendor/manifest
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/manifest
									
									
									
									
										vendored
									
									
								
							@ -138,7 +138,7 @@
 | 
				
			|||||||
			"importpath": "github.com/mholt/certmagic",
 | 
								"importpath": "github.com/mholt/certmagic",
 | 
				
			||||||
			"repository": "https://github.com/mholt/certmagic",
 | 
								"repository": "https://github.com/mholt/certmagic",
 | 
				
			||||||
			"vcs": "git",
 | 
								"vcs": "git",
 | 
				
			||||||
			"revision": "fe722057f2654b33cd528b8fd8b90e53fa495564",
 | 
								"revision": "a3b276a1b44e1c2c3dcab752729976ea04f4839b",
 | 
				
			||||||
			"branch": "master",
 | 
								"branch": "master",
 | 
				
			||||||
			"notests": true
 | 
								"notests": true
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user