Commit af48bbd2 authored by Matthew Holt's avatar Matthew Holt

Scope TLS max_certs to site config instead of global

parent 1e1e69b9
...@@ -75,7 +75,7 @@ type Config struct { ...@@ -75,7 +75,7 @@ type Config struct {
CAUrl string CAUrl string
// The host (ONLY the host, not port) to listen // 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 // an ACME challenge
ListenHost string ListenHost string
...@@ -105,6 +105,22 @@ type Config struct { ...@@ -105,6 +105,22 @@ type Config struct {
// implementors are encouraged to cache any heavy // implementors are encouraged to cache any heavy
// instantiations. // instantiations.
StorageCreator StorageCreator 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 // ObtainCert obtains a certificate for c.Hostname, as long as a certificate
......
...@@ -100,7 +100,7 @@ func (cg configGroup) getCertDuringHandshake(name string, loadIfNecessary, obtai ...@@ -100,7 +100,7 @@ func (cg configGroup) getCertDuringHandshake(name string, loadIfNecessary, obtai
name = strings.ToLower(name) name = strings.ToLower(name)
// Make sure aren't over any applicable limits // Make sure aren't over any applicable limits
err := cg.checkLimitsForObtainingNewCerts(name) err := cg.checkLimitsForObtainingNewCerts(name, cfg)
if err != nil { if err != nil {
return Certificate{}, err return Certificate{}, err
} }
...@@ -127,10 +127,11 @@ func (cg configGroup) getCertDuringHandshake(name string, loadIfNecessary, obtai ...@@ -127,10 +127,11 @@ func (cg configGroup) getCertDuringHandshake(name string, loadIfNecessary, obtai
// now according to mitigating factors we keep track of and preferences the // 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 // user has set. If a non-nil error is returned, do not issue a new certificate
// for name. // 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 // User can set hard limit for number of certs for the process to issue
if onDemandMaxIssue > 0 && atomic.LoadInt32(OnDemandIssuedCount) >= onDemandMaxIssue { if cfg.OnDemandState.MaxObtain > 0 &&
return fmt.Errorf("%s: maximum certificates issued (%d)", name, onDemandMaxIssue) 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 // Make sure name hasn't failed a challenge recently
...@@ -146,7 +147,7 @@ func (cg configGroup) checkLimitsForObtainingNewCerts(name string) error { ...@@ -146,7 +147,7 @@ func (cg configGroup) checkLimitsForObtainingNewCerts(name string) error {
lastIssueTimeMu.Lock() lastIssueTimeMu.Lock()
since := time.Since(lastIssueTime) since := time.Since(lastIssueTime)
lastIssueTimeMu.Unlock() 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) 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 ...@@ -202,7 +203,7 @@ func (cg configGroup) obtainOnDemandCertificate(name string, cfg *Config) (Certi
} }
// Success - update counters and stuff // Success - update counters and stuff
atomic.AddInt32(OnDemandIssuedCount, 1) atomic.AddInt32(&cfg.OnDemandState.ObtainedCount, 1)
lastIssueTimeMu.Lock() lastIssueTimeMu.Lock()
lastIssueTime = time.Now() lastIssueTime = time.Now()
lastIssueTimeMu.Unlock() lastIssueTimeMu.Unlock()
...@@ -286,18 +287,6 @@ func (cg configGroup) renewDynamicCertificate(name string, cfg *Config) (Certifi ...@@ -286,18 +287,6 @@ func (cg configGroup) renewDynamicCertificate(name string, cfg *Config) (Certifi
var obtainCertWaitChans = make(map[string]chan struct{}) var obtainCertWaitChans = make(map[string]chan struct{})
var obtainCertWaitChansMu sync.Mutex 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 // 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. // 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. // When a name is in this map, do not issue a certificate for it on-demand.
......
...@@ -156,9 +156,7 @@ func setupTLS(c *caddy.Controller) error { ...@@ -156,9 +156,7 @@ func setupTLS(c *caddy.Controller) error {
if err != nil || maxCertsNum < 1 { if err != nil || maxCertsNum < 1 {
return c.Err("max_certs must be a positive integer") 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... config.OnDemandState.MaxObtain = int32(maxCertsNum)
onDemandMaxIssue = int32(maxCertsNum)
}
} }
// don't try to load certificates unless we're supposed to // don't try to load certificates unless we're supposed to
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment