mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-03 19:17:29 -05:00 
			
		
		
		
	Merge pull request #1366 from mholt/tls-sni-renew-fix
tls: Fix background certificate renewals that use TLS-SNI challenge
This commit is contained in:
		
						commit
						9369b81498
					
				@ -229,7 +229,7 @@ func cacheCertificate(cert Certificate) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	certCacheMu.Lock()
 | 
						certCacheMu.Lock()
 | 
				
			||||||
	if _, ok := certCache[""]; !ok {
 | 
						if _, ok := certCache[""]; !ok {
 | 
				
			||||||
		// use as default - must be *appended* to list, or bad things happen!
 | 
							// use as default - must be *appended* to end of list, or bad things happen!
 | 
				
			||||||
		cert.Names = append(cert.Names, "")
 | 
							cert.Names = append(cert.Names, "")
 | 
				
			||||||
		certCache[""] = cert
 | 
							certCache[""] = cert
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -65,7 +65,7 @@ func maintainAssets(stopChan chan struct{}) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// RenewManagedCertificates renews managed certificates.
 | 
					// RenewManagedCertificates renews managed certificates.
 | 
				
			||||||
func RenewManagedCertificates(allowPrompts bool) (err error) {
 | 
					func RenewManagedCertificates(allowPrompts bool) (err error) {
 | 
				
			||||||
	var renewed, deleted []Certificate
 | 
						var renewQueue, deleteQueue []Certificate
 | 
				
			||||||
	visitedNames := make(map[string]struct{})
 | 
						visitedNames := make(map[string]struct{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	certCacheMu.RLock()
 | 
						certCacheMu.RLock()
 | 
				
			||||||
@ -77,7 +77,7 @@ func RenewManagedCertificates(allowPrompts bool) (err error) {
 | 
				
			|||||||
		// the list of names on this cert should never be empty...
 | 
							// the list of names on this cert should never be empty...
 | 
				
			||||||
		if cert.Names == nil || len(cert.Names) == 0 {
 | 
							if cert.Names == nil || len(cert.Names) == 0 {
 | 
				
			||||||
			log.Printf("[WARNING] Certificate keyed by '%s' has no names: %v - removing from cache", name, cert.Names)
 | 
								log.Printf("[WARNING] Certificate keyed by '%s' has no names: %v - removing from cache", name, cert.Names)
 | 
				
			||||||
			deleted = append(deleted, cert)
 | 
								deleteQueue = append(deleteQueue, cert)
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -99,64 +99,72 @@ func RenewManagedCertificates(allowPrompts bool) (err error) {
 | 
				
			|||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Get the name which we should use to renew this certificate;
 | 
								// queue for renewal when we aren't in a read lock anymore
 | 
				
			||||||
			// we only support managing certificates with one name per cert,
 | 
								// (the TLS-SNI challenge will need a write lock in order to
 | 
				
			||||||
			// so this should be easy. We can't rely on cert.Config.Hostname
 | 
								// present the certificate, so we renew outside of read lock)
 | 
				
			||||||
			// because it may be a wildcard value from the Caddyfile (e.g.
 | 
								renewQueue = append(renewQueue, cert)
 | 
				
			||||||
			// *.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 allowPrompts && timeLeft < 0 {
 | 
					 | 
				
			||||||
					// Certificate renewal failed, the operator is present, and the certificate
 | 
					 | 
				
			||||||
					// is already expired; we should stop immediately and return the error. Note
 | 
					 | 
				
			||||||
					// that we used to do this any time a renewal failed at startup. However,
 | 
					 | 
				
			||||||
					// after discussion in https://github.com/mholt/caddy/issues/642 we decided to
 | 
					 | 
				
			||||||
					// only stop startup if the certificate is expired. We still log the error
 | 
					 | 
				
			||||||
					// otherwise. I'm not sure how permanent the change in #642 will be...
 | 
					 | 
				
			||||||
					certCacheMu.RUnlock()
 | 
					 | 
				
			||||||
					return err
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				log.Printf("[ERROR] %v", err)
 | 
					 | 
				
			||||||
				if cert.Config.OnDemand {
 | 
					 | 
				
			||||||
					deleted = append(deleted, cert)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				renewed = append(renewed, cert)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	certCacheMu.RUnlock()
 | 
						certCacheMu.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Apply changes to the cache
 | 
						// Perform renewals that are queued
 | 
				
			||||||
	for _, cert := range renewed {
 | 
						for _, cert := range renewQueue {
 | 
				
			||||||
		// TODO: Don't do these until we have valid OCSP for the new cert
 | 
							// Get the name which we should use to renew this certificate;
 | 
				
			||||||
		if cert.Names[len(cert.Names)-1] == "" {
 | 
							// we only support managing certificates with one name per cert,
 | 
				
			||||||
			// Special case: This is the default certificate. We must
 | 
							// so this should be easy. We can't rely on cert.Config.Hostname
 | 
				
			||||||
			// flush it out of the cache so that we no longer point to
 | 
							// because it may be a wildcard value from the Caddyfile (e.g.
 | 
				
			||||||
			// the old, un-renewed certificate. Otherwise it will be
 | 
							// *.something.com) which, as of Jan. 2017, is not supported by ACME.
 | 
				
			||||||
			// renewed on every scan, which is too often. When we cache
 | 
							var renewName string
 | 
				
			||||||
			// this certificate in a moment, it will be the default again.
 | 
							for _, name := range cert.Names {
 | 
				
			||||||
			certCacheMu.Lock()
 | 
								if name != "" {
 | 
				
			||||||
			delete(certCache, "")
 | 
									renewName = name
 | 
				
			||||||
			certCacheMu.Unlock()
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		_, err := CacheManagedCertificate(cert.Names[0], cert.Config)
 | 
					
 | 
				
			||||||
 | 
							// perform renewal
 | 
				
			||||||
 | 
							err := cert.Config.RenewCert(renewName, allowPrompts)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			if allowPrompts {
 | 
								if allowPrompts && cert.NotAfter.Sub(time.Now().UTC()) < 0 {
 | 
				
			||||||
				return err // operator is present, so report error immediately
 | 
									// Certificate renewal failed, the operator is present, and the certificate
 | 
				
			||||||
 | 
									// is already expired; we should stop immediately and return the error. Note
 | 
				
			||||||
 | 
									// that we used to do this any time a renewal failed at startup. However,
 | 
				
			||||||
 | 
									// after discussion in https://github.com/mholt/caddy/issues/642 we decided to
 | 
				
			||||||
 | 
									// only stop startup if the certificate is expired. We still log the error
 | 
				
			||||||
 | 
									// otherwise. I'm not sure how permanent the change in #642 will be...
 | 
				
			||||||
 | 
									// TODO: Get rid of the expiration check... always break on error.
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			log.Printf("[ERROR] %v", err)
 | 
								log.Printf("[ERROR] %v", err)
 | 
				
			||||||
 | 
								if cert.Config.OnDemand {
 | 
				
			||||||
 | 
									deleteQueue = append(deleteQueue, cert)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// successful renewal, so update in-memory cache by loading
 | 
				
			||||||
 | 
								// renewed certificate so it will be used with handshakes
 | 
				
			||||||
 | 
								// TODO: Not until CA has valid OCSP response ready for the new cert... sigh.
 | 
				
			||||||
 | 
								if cert.Names[len(cert.Names)-1] == "" {
 | 
				
			||||||
 | 
									// Special case: This is the default certificate. We must
 | 
				
			||||||
 | 
									// flush it out of the cache so that we no longer point to
 | 
				
			||||||
 | 
									// the old, un-renewed certificate. Otherwise it will be
 | 
				
			||||||
 | 
									// renewed on every scan, which is too often. The next cert
 | 
				
			||||||
 | 
									// to be cached (probably this one) will become the default.
 | 
				
			||||||
 | 
									certCacheMu.Lock()
 | 
				
			||||||
 | 
									delete(certCache, "")
 | 
				
			||||||
 | 
									certCacheMu.Unlock()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								_, err := CacheManagedCertificate(cert.Names[0], cert.Config)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									if allowPrompts {
 | 
				
			||||||
 | 
										return err // operator is present, so report error immediately
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									log.Printf("[ERROR] %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, cert := range deleted {
 | 
					
 | 
				
			||||||
 | 
						// Apply queued deletion changes to the cache
 | 
				
			||||||
 | 
						for _, cert := range deleteQueue {
 | 
				
			||||||
		certCacheMu.Lock()
 | 
							certCacheMu.Lock()
 | 
				
			||||||
		for _, name := range cert.Names {
 | 
							for _, name := range cert.Names {
 | 
				
			||||||
			delete(certCache, name)
 | 
								delete(certCache, name)
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user