Skip to content

Commit 26a9910

Browse files
authored
ratelimits: improve disabled limit handling (#7803)
In the FailedAuthorizations limits, there was code that intentionally ignored errLimitDisabled errors (`errors.Is(err, errLimitDisabled)`). However, that that resulted in those functions later using a returned `limit` value that was invalid (i.e. its zero value). That happened to trigger some later checks in validateTransaction. Specifically this check failed: if txn.cost > txn.limit.Burst { // error When txt.limit.Burst is zero, this will always fail. This problem doesn't really show up in prod, where all the limits are configured. But it showed up in tests, specifically TestPerformValidation_FailedValidationsTriggerPauseIdentifiersRatelimit, where the limits are constructed using a simplified config that leaves most of them disabled. In this change, I tried to make handling of errLimitDisabled more consistent, and always return an allow-only transaction as early as possible instead of falling through the error condition. Where that wasn't possible, I used a boolean to record whether the result of `builder.getLimit()` was valid before referencing any of its fields. I also added some "shouldn't happen" errors to catch this problem earlier if it recurs. I removed some "skip disabled limit" comments because those say "what the code does" (which the code also says), not "why the code does it". Fixes the test failures in #7797.
1 parent 0a27cba commit 26a9910

File tree

2 files changed

+76
-32
lines changed

2 files changed

+76
-32
lines changed

ratelimits/limit.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ import (
1515
// currently configured.
1616
var errLimitDisabled = errors.New("limit disabled")
1717

18+
// limit defines the configuration for a rate limit or a rate limit override.
19+
//
20+
// The zero value of this struct is invalid, because some of the fields must
21+
// be greater than zero.
1822
type limit struct {
1923
// Burst specifies maximum concurrent allowed requests at any given time. It
2024
// must be greater than zero.

ratelimits/transaction.go

Lines changed: 72 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ func newFQDNSetBucketKey(name Name, orderNames []string) (string, error) { //nol
102102
// - allow-only: when neither check nor spend are true, the transaction will
103103
// be considered "allowed" regardless of the bucket's capacity. This is
104104
// useful for limits that are disabled.
105+
//
106+
// The zero value of Transaction is an allow-only transaction and is valid even if
107+
// it would fail validateTransaction (for instance because cost and burst are zero).
105108
type Transaction struct {
106109
bucketKey string
107110
limit limit
@@ -126,6 +129,14 @@ func validateTransaction(txn Transaction) (Transaction, error) {
126129
if txn.cost < 0 {
127130
return Transaction{}, ErrInvalidCost
128131
}
132+
if txn.limit.Burst == 0 {
133+
// This should never happen. If the limit was loaded from a file,
134+
// Burst was validated then. If this is a zero-valued Transaction
135+
// (that is, an allow-only transaction), then validateTransaction
136+
// shouldn't be called because zero-valued transactions are automatically
137+
// valid.
138+
return Transaction{}, fmt.Errorf("invalid limit, burst must be > 0")
139+
}
129140
if txn.cost > txn.limit.Burst {
130141
return Transaction{}, ErrInvalidCostOverLimit
131142
}
@@ -160,9 +171,9 @@ func newSpendOnlyTransaction(limit limit, bucketKey string, cost int64) (Transac
160171
})
161172
}
162173

163-
func newAllowOnlyTransaction() (Transaction, error) {
174+
func newAllowOnlyTransaction() Transaction {
164175
// Zero values are sufficient.
165-
return validateTransaction(Transaction{})
176+
return Transaction{}
166177
}
167178

168179
// TransactionBuilder is used to build Transactions for various rate limits.
@@ -194,7 +205,7 @@ func (builder *TransactionBuilder) registrationsPerIPAddressTransaction(ip net.I
194205
limit, err := builder.getLimit(NewRegistrationsPerIPAddress, bucketKey)
195206
if err != nil {
196207
if errors.Is(err, errLimitDisabled) {
197-
return newAllowOnlyTransaction()
208+
return newAllowOnlyTransaction(), nil
198209
}
199210
return Transaction{}, err
200211
}
@@ -212,7 +223,7 @@ func (builder *TransactionBuilder) registrationsPerIPv6RangeTransaction(ip net.I
212223
limit, err := builder.getLimit(NewRegistrationsPerIPv6Range, bucketKey)
213224
if err != nil {
214225
if errors.Is(err, errLimitDisabled) {
215-
return newAllowOnlyTransaction()
226+
return newAllowOnlyTransaction(), nil
216227
}
217228
return Transaction{}, err
218229
}
@@ -229,7 +240,7 @@ func (builder *TransactionBuilder) ordersPerAccountTransaction(regId int64) (Tra
229240
limit, err := builder.getLimit(NewOrdersPerAccount, bucketKey)
230241
if err != nil {
231242
if errors.Is(err, errLimitDisabled) {
232-
return newAllowOnlyTransaction()
243+
return newAllowOnlyTransaction(), nil
233244
}
234245
return Transaction{}, err
235246
}
@@ -250,7 +261,10 @@ func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountCheckO
250261
return nil, err
251262
}
252263
limit, err := builder.getLimit(FailedAuthorizationsPerDomainPerAccount, perAccountBucketKey)
253-
if err != nil && !errors.Is(err, errLimitDisabled) {
264+
if err != nil {
265+
if errors.Is(err, errLimitDisabled) {
266+
return []Transaction{newAllowOnlyTransaction()}, nil
267+
}
254268
return nil, err
255269
}
256270

@@ -287,7 +301,10 @@ func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountSpendO
287301
return Transaction{}, err
288302
}
289303
limit, err := builder.getLimit(FailedAuthorizationsPerDomainPerAccount, perAccountBucketKey)
290-
if err != nil && !errors.Is(err, errLimitDisabled) {
304+
if err != nil {
305+
if errors.Is(err, errLimitDisabled) {
306+
return newAllowOnlyTransaction(), nil
307+
}
291308
return Transaction{}, err
292309
}
293310

@@ -317,7 +334,10 @@ func (builder *TransactionBuilder) FailedAuthorizationsForPausingPerDomainPerAcc
317334
return Transaction{}, err
318335
}
319336
limit, err := builder.getLimit(FailedAuthorizationsForPausingPerDomainPerAccount, perAccountBucketKey)
320-
if err != nil && !errors.Is(err, errLimitDisabled) {
337+
if err != nil {
338+
if errors.Is(err, errLimitDisabled) {
339+
return newAllowOnlyTransaction(), nil
340+
}
321341
return Transaction{}, err
322342
}
323343

@@ -351,9 +371,18 @@ func (builder *TransactionBuilder) certificatesPerDomainCheckOnlyTransactions(re
351371
if err != nil {
352372
return nil, err
353373
}
374+
accountOverride := true
354375
perAccountLimit, err := builder.getLimit(CertificatesPerDomainPerAccount, perAccountLimitBucketKey)
355-
if err != nil && !errors.Is(err, errLimitDisabled) {
356-
return nil, err
376+
if err != nil {
377+
// The CertificatesPerDomainPerAccount limit never has a default. If there is an override for it,
378+
// the above call will return the override. But if there is none, it will return errLimitDisabled.
379+
// In that case we want to continue, but make sure we don't reference `perAccountLimit` because it
380+
// is not a valid limit.
381+
if errors.Is(err, errLimitDisabled) {
382+
accountOverride = false
383+
} else {
384+
return nil, err
385+
}
357386
}
358387

359388
var txns []Transaction
@@ -362,9 +391,10 @@ func (builder *TransactionBuilder) certificatesPerDomainCheckOnlyTransactions(re
362391
if err != nil {
363392
return nil, err
364393
}
365-
if perAccountLimit.isOverride() {
366-
// An override is configured for the CertificatesPerDomainPerAccount
367-
// limit.
394+
if accountOverride {
395+
if !perAccountLimit.isOverride() {
396+
return nil, fmt.Errorf("shouldn't happen: CertificatesPerDomainPerAccount limit is not an override")
397+
}
368398
perAccountPerDomainKey, err := NewRegIdDomainBucketKey(CertificatesPerDomainPerAccount, regId, name)
369399
if err != nil {
370400
return nil, err
@@ -373,18 +403,20 @@ func (builder *TransactionBuilder) certificatesPerDomainCheckOnlyTransactions(re
373403
// bucket.
374404
txn, err := newCheckOnlyTransaction(perAccountLimit, perAccountPerDomainKey, 1)
375405
if err != nil {
406+
if errors.Is(err, errLimitDisabled) {
407+
continue
408+
}
376409
return nil, err
377410
}
378411
txns = append(txns, txn)
379412
} else {
380413
// Use the per domain bucket key when no per account per domain override
381414
// is configured.
382415
perDomainLimit, err := builder.getLimit(CertificatesPerDomain, perDomainBucketKey)
383-
if errors.Is(err, errLimitDisabled) {
384-
// Skip disabled limit.
385-
continue
386-
}
387416
if err != nil {
417+
if errors.Is(err, errLimitDisabled) {
418+
continue
419+
}
388420
return nil, err
389421
}
390422
// Add a check-only transaction for each per domain bucket.
@@ -417,9 +449,18 @@ func (builder *TransactionBuilder) CertificatesPerDomainSpendOnlyTransactions(re
417449
if err != nil {
418450
return nil, err
419451
}
452+
accountOverride := true
420453
perAccountLimit, err := builder.getLimit(CertificatesPerDomainPerAccount, perAccountLimitBucketKey)
421-
if err != nil && !errors.Is(err, errLimitDisabled) {
422-
return nil, err
454+
if err != nil {
455+
// The CertificatesPerDomainPerAccount limit never has a default. If there is an override for it,
456+
// the above call will return the override. But if there is none, it will return errLimitDisabled.
457+
// In that case we want to continue, but make sure we don't reference `perAccountLimit` because it
458+
// is not a valid limit.
459+
if errors.Is(err, errLimitDisabled) {
460+
accountOverride = false
461+
} else {
462+
return nil, err
463+
}
423464
}
424465

425466
var txns []Transaction
@@ -428,9 +469,10 @@ func (builder *TransactionBuilder) CertificatesPerDomainSpendOnlyTransactions(re
428469
if err != nil {
429470
return nil, err
430471
}
431-
if perAccountLimit.isOverride() {
432-
// An override is configured for the CertificatesPerDomainPerAccount
433-
// limit.
472+
if accountOverride {
473+
if !perAccountLimit.isOverride() {
474+
return nil, fmt.Errorf("shouldn't happen: CertificatesPerDomainPerAccount limit is not an override")
475+
}
434476
perAccountPerDomainKey, err := NewRegIdDomainBucketKey(CertificatesPerDomainPerAccount, regId, name)
435477
if err != nil {
436478
return nil, err
@@ -444,11 +486,10 @@ func (builder *TransactionBuilder) CertificatesPerDomainSpendOnlyTransactions(re
444486
txns = append(txns, txn)
445487

446488
perDomainLimit, err := builder.getLimit(CertificatesPerDomain, perDomainBucketKey)
447-
if errors.Is(err, errLimitDisabled) {
448-
// Skip disabled limit.
449-
continue
450-
}
451489
if err != nil {
490+
if errors.Is(err, errLimitDisabled) {
491+
continue
492+
}
452493
return nil, err
453494
}
454495

@@ -462,11 +503,10 @@ func (builder *TransactionBuilder) CertificatesPerDomainSpendOnlyTransactions(re
462503
// Use the per domain bucket key when no per account per domain
463504
// override is configured.
464505
perDomainLimit, err := builder.getLimit(CertificatesPerDomain, perDomainBucketKey)
465-
if errors.Is(err, errLimitDisabled) {
466-
// Skip disabled limit.
467-
continue
468-
}
469506
if err != nil {
507+
if errors.Is(err, errLimitDisabled) {
508+
continue
509+
}
470510
return nil, err
471511
}
472512
// Add a spend-only transaction for each per domain bucket.
@@ -491,7 +531,7 @@ func (builder *TransactionBuilder) certificatesPerFQDNSetCheckOnlyTransaction(or
491531
limit, err := builder.getLimit(CertificatesPerFQDNSet, bucketKey)
492532
if err != nil {
493533
if errors.Is(err, errLimitDisabled) {
494-
return newAllowOnlyTransaction()
534+
return newAllowOnlyTransaction(), nil
495535
}
496536
return Transaction{}, err
497537
}
@@ -509,7 +549,7 @@ func (builder *TransactionBuilder) CertificatesPerFQDNSetSpendOnlyTransaction(or
509549
limit, err := builder.getLimit(CertificatesPerFQDNSet, bucketKey)
510550
if err != nil {
511551
if errors.Is(err, errLimitDisabled) {
512-
return newAllowOnlyTransaction()
552+
return newAllowOnlyTransaction(), nil
513553
}
514554
return Transaction{}, err
515555
}

0 commit comments

Comments
 (0)