diff --git a/caddytls/config.go b/caddytls/config.go index 82c2d654a..89ccafd75 100644 --- a/caddytls/config.go +++ b/caddytls/config.go @@ -75,7 +75,7 @@ type Config struct { CAUrl string // The host (ONLY the host, not port) to listen - //on if necessary to start a a listener to solve + // on if necessary to start a listener to solve // an ACME challenge ListenHost string @@ -105,6 +105,22 @@ type Config struct { // implementors are encouraged to cache any heavy // instantiations. StorageCreator StorageCreator + + // The state needed to operate on-demand TLS + OnDemandState OnDemandState +} + +// OnDemandState contains some state relevant for providing +// on-demand TLS. +type OnDemandState struct { + // The number of certificates that have been issued on-demand + // by this config. It is only safe to modify this count atomically. + // If it reaches MaxObtain, on-demand issuances must fail. + ObtainedCount int32 + + // Based on max_certs in tls config, it specifies the + // maximum number of certificates that can be issued. + MaxObtain int32 } // ObtainCert obtains a certificate for c.Hostname, as long as a certificate diff --git a/caddytls/handshake.go b/caddytls/handshake.go index 65041b889..09e26fe95 100644 --- a/caddytls/handshake.go +++ b/caddytls/handshake.go @@ -100,7 +100,7 @@ func (cg configGroup) getCertDuringHandshake(name string, loadIfNecessary, obtai name = strings.ToLower(name) // Make sure aren't over any applicable limits - err := cg.checkLimitsForObtainingNewCerts(name) + err := cg.checkLimitsForObtainingNewCerts(name, cfg) if err != nil { return Certificate{}, err } @@ -127,10 +127,11 @@ func (cg configGroup) getCertDuringHandshake(name string, loadIfNecessary, obtai // now according to mitigating factors we keep track of and preferences the // user has set. If a non-nil error is returned, do not issue a new certificate // for name. -func (cg configGroup) checkLimitsForObtainingNewCerts(name string) error { +func (cg configGroup) checkLimitsForObtainingNewCerts(name string, cfg *Config) error { // User can set hard limit for number of certs for the process to issue - if onDemandMaxIssue > 0 && atomic.LoadInt32(OnDemandIssuedCount) >= onDemandMaxIssue { - return fmt.Errorf("%s: maximum certificates issued (%d)", name, onDemandMaxIssue) + if cfg.OnDemandState.MaxObtain > 0 && + atomic.LoadInt32(&cfg.OnDemandState.ObtainedCount) >= cfg.OnDemandState.MaxObtain { + return fmt.Errorf("%s: maximum certificates issued (%d)", name, cfg.OnDemandState.MaxObtain) } // Make sure name hasn't failed a challenge recently @@ -146,7 +147,7 @@ func (cg configGroup) checkLimitsForObtainingNewCerts(name string) error { lastIssueTimeMu.Lock() since := time.Since(lastIssueTime) lastIssueTimeMu.Unlock() - if atomic.LoadInt32(OnDemandIssuedCount) >= 10 && since < 10*time.Minute { + if atomic.LoadInt32(&cfg.OnDemandState.ObtainedCount) >= 10 && since < 10*time.Minute { return fmt.Errorf("%s: throttled; last certificate was obtained %v ago", name, since) } @@ -202,7 +203,7 @@ func (cg configGroup) obtainOnDemandCertificate(name string, cfg *Config) (Certi } // Success - update counters and stuff - atomic.AddInt32(OnDemandIssuedCount, 1) + atomic.AddInt32(&cfg.OnDemandState.ObtainedCount, 1) lastIssueTimeMu.Lock() lastIssueTime = time.Now() lastIssueTimeMu.Unlock() @@ -286,18 +287,6 @@ func (cg configGroup) renewDynamicCertificate(name string, cfg *Config) (Certifi var obtainCertWaitChans = make(map[string]chan struct{}) var obtainCertWaitChansMu sync.Mutex -// OnDemandIssuedCount is the number of certificates that have been issued -// on-demand by this process. It is only safe to modify this count atomically. -// If it reaches onDemandMaxIssue, on-demand issuances will fail. -var OnDemandIssuedCount = new(int32) - -// onDemandMaxIssue is set based on max_certs in tls config. It specifies the -// maximum number of certificates that can be issued. -// TODO: This applies globally, but we should probably make a server-specific -// way to keep track of these limits and counts, since it's specified in the -// Caddyfile... -var onDemandMaxIssue int32 - // failedIssuance is a set of names that we recently failed to get a // certificate for from the ACME CA. They are removed after some time. // When a name is in this map, do not issue a certificate for it on-demand. diff --git a/caddytls/setup.go b/caddytls/setup.go index 7269eea2d..488537e1a 100644 --- a/caddytls/setup.go +++ b/caddytls/setup.go @@ -156,9 +156,7 @@ func setupTLS(c *caddy.Controller) error { if err != nil || maxCertsNum < 1 { return c.Err("max_certs must be a positive integer") } - if onDemandMaxIssue == 0 || int32(maxCertsNum) < onDemandMaxIssue { // keep the minimum; TODO: We have to do this because it is global; should be per-server or per-vhost... - onDemandMaxIssue = int32(maxCertsNum) - } + config.OnDemandState.MaxObtain = int32(maxCertsNum) } // don't try to load certificates unless we're supposed to