Add registration-based overrides.
This commit is contained in:
parent
bd842922ac
commit
1899866d6b
|
|
@ -25,15 +25,20 @@ type RateLimitPolicy struct {
|
|||
Window ConfigDuration `yaml:"window"`
|
||||
// The max number of items that can be present before triggering the rate
|
||||
// limit. Zero means "no limit."
|
||||
Threshold int64 `yaml:"threshold"`
|
||||
// A per-key override granting higher limits. The key is defined on a
|
||||
// per-limit basis and should match the key it counts on. For instance, a rate
|
||||
// limit on the number of certificates per name uses name as a key, whilte a
|
||||
// rate limit on the number of registrations per IP subnet would use subnet as
|
||||
// a key.
|
||||
Threshold int `yaml:"threshold"`
|
||||
// A per-key override setting different limits than the default (higher or lower).
|
||||
// The key is defined on a per-limit basis and should match the key it counts on.
|
||||
// For instance, a rate limit on the number of certificates per name uses name as
|
||||
// a key, while a rate limit on the number of registrations per IP subnet would
|
||||
// use subnet as a key.
|
||||
// Note that a zero entry in the overrides map does not mean "not limit," it
|
||||
// means a limit of zero.
|
||||
Overrides map[string]int64 `yaml:"overrides"`
|
||||
Overrides map[string]int `yaml:"overrides"`
|
||||
// A per-registration override setting. This can be used, e.g. if there are
|
||||
// hosting providers that we would like to grant a higher rate of issuance
|
||||
// than the default. If both key-based and registration-based overrides are
|
||||
// available, the registration-based on takes priority.
|
||||
RegistrationOverrides map[int64]int `yaml:"registrationOverrides"`
|
||||
}
|
||||
|
||||
// Enabled returns true iff the RateLimitPolicy is enabled.
|
||||
|
|
@ -43,7 +48,10 @@ func (rlp *RateLimitPolicy) Enabled() bool {
|
|||
|
||||
// GetThreshold returns the threshold for this rate limit, taking into account
|
||||
// any overrides for `key`.
|
||||
func (rlp *RateLimitPolicy) GetThreshold(key string) int64 {
|
||||
func (rlp *RateLimitPolicy) GetThreshold(key string, regID int64) int {
|
||||
if override, ok := rlp.RegistrationOverrides[regID]; ok {
|
||||
return override
|
||||
}
|
||||
if override, ok := rlp.Overrides[key]; ok {
|
||||
return override
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestEnabled(t *testing.T) {
|
||||
policy := RateLimitPolicy{
|
||||
Threshold: 10,
|
||||
}
|
||||
if !policy.Enabled() {
|
||||
t.Errorf("Policy should have been enabled.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotEnabled(t *testing.T) {
|
||||
policy := RateLimitPolicy{
|
||||
Threshold: 0,
|
||||
}
|
||||
if policy.Enabled() {
|
||||
t.Errorf("Policy should not have been enabled.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetThreshold(t *testing.T) {
|
||||
policy := RateLimitPolicy{
|
||||
Threshold: 1,
|
||||
Overrides: map[string]int{
|
||||
"key": 2,
|
||||
},
|
||||
RegistrationOverrides: map[int64]int{
|
||||
101: 3,
|
||||
},
|
||||
}
|
||||
if policy.GetThreshold("foo", 11) != 1 {
|
||||
t.Errorf("threshold should have been 1")
|
||||
}
|
||||
if policy.GetThreshold("key", 11) != 2 {
|
||||
t.Errorf("threshold should have been 2")
|
||||
}
|
||||
if policy.GetThreshold("key", 101) != 3 {
|
||||
t.Errorf("threshold should have been 3")
|
||||
}
|
||||
if policy.GetThreshold("foo", 101) != 3 {
|
||||
t.Errorf("threshold should have been 3")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWindowBegin(t *testing.T) {
|
||||
policy := RateLimitPolicy{
|
||||
Window: ConfigDuration{Duration: 24 * time.Hour},
|
||||
}
|
||||
now := time.Date(2015, 9, 22, 0, 0, 0, 0, time.UTC)
|
||||
expected := time.Date(2015, 9, 21, 0, 0, 0, 0, time.UTC)
|
||||
actual := policy.WindowBegin(now)
|
||||
if actual != expected {
|
||||
t.Errorf("Incorrect WindowBegin: %s, expected %s", actual, expected)
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +48,7 @@ type RegistrationAuthorityImpl struct {
|
|||
authorizationLifetime time.Duration
|
||||
rlPolicies cmd.RateLimitConfig
|
||||
tiMu *sync.RWMutex
|
||||
totalIssuedCache int64
|
||||
totalIssuedCache int
|
||||
lastIssuedCount *time.Time
|
||||
}
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ func (ra *RegistrationAuthorityImpl) issuanceCountInvalid(now time.Time) bool {
|
|||
return ra.lastIssuedCount == nil || ra.lastIssuedCount.Add(issuanceCountCacheLife).Before(now)
|
||||
}
|
||||
|
||||
func (ra *RegistrationAuthorityImpl) getIssuanceCount() (int64, error) {
|
||||
func (ra *RegistrationAuthorityImpl) getIssuanceCount() (int, error) {
|
||||
ra.tiMu.RLock()
|
||||
if ra.issuanceCountInvalid(ra.clk.Now()) {
|
||||
ra.tiMu.RUnlock()
|
||||
|
|
@ -119,7 +119,7 @@ func (ra *RegistrationAuthorityImpl) getIssuanceCount() (int64, error) {
|
|||
return count, nil
|
||||
}
|
||||
|
||||
func (ra *RegistrationAuthorityImpl) setIssuanceCount() (int64, error) {
|
||||
func (ra *RegistrationAuthorityImpl) setIssuanceCount() (int, error) {
|
||||
ra.tiMu.Lock()
|
||||
defer ra.tiMu.Unlock()
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ func (ra *RegistrationAuthorityImpl) setIssuanceCount() (int64, error) {
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
ra.totalIssuedCache = count
|
||||
ra.totalIssuedCache = int(count)
|
||||
ra.lastIssuedCount = &now
|
||||
}
|
||||
return ra.totalIssuedCache, nil
|
||||
|
|
@ -430,7 +430,7 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest,
|
|||
// Check rate limits before checking authorizations. If someone is unable to
|
||||
// issue a cert due to rate limiting, we don't want to tell them to go get the
|
||||
// necessary authorizations, only to later fail the rate limit check.
|
||||
err = ra.checkLimits(names)
|
||||
err = ra.checkLimits(names, registration.ID)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
return emptyCert, err
|
||||
|
|
@ -497,7 +497,7 @@ func domainsForRateLimiting(names []string) ([]string, error) {
|
|||
return domains, nil
|
||||
}
|
||||
|
||||
func (ra *RegistrationAuthorityImpl) checkCertificatesPerNameLimit(names []string, limit cmd.RateLimitPolicy) error {
|
||||
func (ra *RegistrationAuthorityImpl) checkCertificatesPerNameLimit(names []string, limit cmd.RateLimitPolicy, regID int64) error {
|
||||
names, err := domainsForRateLimiting(names)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -515,7 +515,7 @@ func (ra *RegistrationAuthorityImpl) checkCertificatesPerNameLimit(names []strin
|
|||
// Shouldn't happen, but let's be careful anyhow.
|
||||
return errors.New("StorageAuthority failed to return a count for every name")
|
||||
}
|
||||
if int64(count) >= limit.GetThreshold(name) {
|
||||
if count >= limit.GetThreshold(name, regID) {
|
||||
badNames = append(badNames, name)
|
||||
}
|
||||
}
|
||||
|
|
@ -527,7 +527,7 @@ func (ra *RegistrationAuthorityImpl) checkCertificatesPerNameLimit(names []strin
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ra *RegistrationAuthorityImpl) checkLimits(names []string) error {
|
||||
func (ra *RegistrationAuthorityImpl) checkLimits(names []string, regID int64) error {
|
||||
limits := ra.rlPolicies
|
||||
if limits.TotalCertificates.Enabled() {
|
||||
totalIssued, err := ra.getIssuanceCount()
|
||||
|
|
@ -539,7 +539,7 @@ func (ra *RegistrationAuthorityImpl) checkLimits(names []string) error {
|
|||
}
|
||||
}
|
||||
if limits.CertificatesPerName.Enabled() {
|
||||
err := ra.checkCertificatesPerNameLimit(names, limits.CertificatesPerName)
|
||||
err := ra.checkCertificatesPerNameLimit(names, limits.CertificatesPerName, regID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -655,7 +655,7 @@ func TestCheckCertificatesPerNameLimit(t *testing.T) {
|
|||
rlp := cmd.RateLimitPolicy{
|
||||
Threshold: 3,
|
||||
Window: cmd.ConfigDuration{Duration: 23 * time.Hour},
|
||||
Overrides: map[string]int64{
|
||||
Overrides: map[string]int{
|
||||
"bigissuer.com": 100,
|
||||
"smallissuer.co.uk": 1,
|
||||
},
|
||||
|
|
@ -672,31 +672,31 @@ func TestCheckCertificatesPerNameLimit(t *testing.T) {
|
|||
ra.SA = mockSA
|
||||
|
||||
// One base domain, below threshold
|
||||
err := ra.checkCertificatesPerNameLimit([]string{"www.example.com", "example.com"}, rlp)
|
||||
err := ra.checkCertificatesPerNameLimit([]string{"www.example.com", "example.com"}, rlp, 99)
|
||||
test.AssertNotError(t, err, "rate limited example.com incorrectly")
|
||||
|
||||
// One base domain, above threshold
|
||||
mockSA.nameCounts["example.com"] = 10
|
||||
err = ra.checkCertificatesPerNameLimit([]string{"www.example.com", "example.com"}, rlp)
|
||||
err = ra.checkCertificatesPerNameLimit([]string{"www.example.com", "example.com"}, rlp, 99)
|
||||
test.AssertError(t, err, "incorrectly failed to rate limit example.com")
|
||||
if _, ok := err.(core.RateLimitedError); !ok {
|
||||
t.Errorf("Incorrect error type %#v", err)
|
||||
}
|
||||
|
||||
// SA misbehaved and didn't send back a count for every input name
|
||||
err = ra.checkCertificatesPerNameLimit([]string{"zombo.com", "www.example.com", "example.com"}, rlp)
|
||||
err = ra.checkCertificatesPerNameLimit([]string{"zombo.com", "www.example.com", "example.com"}, rlp, 99)
|
||||
test.AssertError(t, err, "incorrectly failed to error on misbehaving SA")
|
||||
|
||||
// Two base domains, one above threshold but with an override.
|
||||
mockSA.nameCounts["example.com"] = 0
|
||||
mockSA.nameCounts["bigissuer.com"] = 50
|
||||
err = ra.checkCertificatesPerNameLimit([]string{"www.example.com", "subdomain.bigissuer.com"}, rlp)
|
||||
err = ra.checkCertificatesPerNameLimit([]string{"www.example.com", "subdomain.bigissuer.com"}, rlp, 99)
|
||||
test.AssertNotError(t, err, "incorrectly rate limited bigissuer")
|
||||
|
||||
// Two base domains, one above its override
|
||||
mockSA.nameCounts["example.com"] = 0
|
||||
mockSA.nameCounts["bigissuer.com"] = 100
|
||||
err = ra.checkCertificatesPerNameLimit([]string{"www.example.com", "subdomain.bigissuer.com"}, rlp)
|
||||
err = ra.checkCertificatesPerNameLimit([]string{"www.example.com", "subdomain.bigissuer.com"}, rlp, 99)
|
||||
test.AssertError(t, err, "incorrectly failed to rate limit bigissuer")
|
||||
if _, ok := err.(core.RateLimitedError); !ok {
|
||||
t.Errorf("Incorrect error type")
|
||||
|
|
@ -704,7 +704,7 @@ func TestCheckCertificatesPerNameLimit(t *testing.T) {
|
|||
|
||||
// One base domain, above its override (which is below threshold)
|
||||
mockSA.nameCounts["smallissuer.co.uk"] = 1
|
||||
err = ra.checkCertificatesPerNameLimit([]string{"www.smallissuer.co.uk"}, rlp)
|
||||
err = ra.checkCertificatesPerNameLimit([]string{"www.smallissuer.co.uk"}, rlp, 99)
|
||||
test.AssertError(t, err, "incorrectly failed to rate limit smallissuer")
|
||||
if _, ok := err.(core.RateLimitedError); !ok {
|
||||
t.Errorf("Incorrect error type %#v", err)
|
||||
|
|
|
|||
|
|
@ -13,3 +13,5 @@ certificatesPerName:
|
|||
le2.wtf: 10000
|
||||
le3.wtf: 10000
|
||||
nginx.wtf: 10000
|
||||
registrationOverrides:
|
||||
101: 1000
|
||||
|
|
|
|||
Loading…
Reference in New Issue