github.com/letsencrypt/boulder@v0.20251208.0/ratelimits/limiter_test.go (about) 1 package ratelimits 2 3 import ( 4 "context" 5 "math/rand/v2" 6 "net" 7 "net/netip" 8 "testing" 9 "time" 10 11 "github.com/jmhodges/clock" 12 13 "github.com/letsencrypt/boulder/config" 14 berrors "github.com/letsencrypt/boulder/errors" 15 blog "github.com/letsencrypt/boulder/log" 16 "github.com/letsencrypt/boulder/metrics" 17 "github.com/letsencrypt/boulder/test" 18 ) 19 20 // overriddenIP is overridden in 'testdata/working_override.yml' to have higher 21 // burst and count values. 22 const overriddenIP = "64.112.117.1" 23 24 // newTestLimiter constructs a new limiter. 25 func newTestLimiter(t *testing.T, s Source, clk clock.FakeClock) *Limiter { 26 l, err := NewLimiter(clk, s, metrics.NoopRegisterer) 27 test.AssertNotError(t, err, "should not error") 28 return l 29 } 30 31 // newTestTransactionBuilder constructs a new *TransactionBuilder with the 32 // following configuration: 33 // - 'NewRegistrationsPerIPAddress' burst: 20 count: 20 period: 1s 34 // - 'NewRegistrationsPerIPAddress:64.112.117.1' burst: 40 count: 40 period: 1s 35 func newTestTransactionBuilder(t *testing.T) *TransactionBuilder { 36 c, err := NewTransactionBuilderFromFiles("testdata/working_default.yml", "testdata/working_override.yml", metrics.NoopRegisterer, blog.NewMock()) 37 test.AssertNotError(t, err, "should not error") 38 err = c.loadOverrides(context.Background()) 39 test.AssertNotError(t, err, "loading overrides") 40 41 return c 42 } 43 44 func setup(t *testing.T) (context.Context, map[string]*Limiter, *TransactionBuilder, clock.FakeClock, string) { 45 testCtx := context.Background() 46 clk := clock.NewFake() 47 48 // Generate a random IP address to avoid collisions during and between test 49 // runs. 50 randIP := make(net.IP, 4) 51 for i := range 4 { 52 randIP[i] = byte(rand.IntN(256)) 53 } 54 55 // Construct a limiter for each source. 56 return testCtx, map[string]*Limiter{ 57 "inmem": newInmemTestLimiter(t, clk), 58 "redis": newRedisTestLimiter(t, clk), 59 }, newTestTransactionBuilder(t), clk, randIP.String() 60 } 61 62 func resetBucket(t *testing.T, l *Limiter, ctx context.Context, limit *Limit, bucketKey string) { 63 t.Helper() 64 txn, err := newResetTransaction(limit, bucketKey) 65 test.AssertNotError(t, err, "txn should be valid") 66 err = l.BatchReset(ctx, []Transaction{txn}) 67 test.AssertNotError(t, err, "should not error") 68 } 69 70 func TestLimiter_CheckWithLimitOverrides(t *testing.T) { 71 t.Parallel() 72 testCtx, limiters, txnBuilder, clk, testIP := setup(t) 73 for name, l := range limiters { 74 t.Run(name, func(t *testing.T) { 75 overriddenBucketKey := newIPAddressBucketKey(NewRegistrationsPerIPAddress, netip.MustParseAddr(overriddenIP)) 76 overriddenLimit, err := txnBuilder.getLimit(NewRegistrationsPerIPAddress, overriddenBucketKey) 77 test.AssertNotError(t, err, "should not error") 78 79 // Attempt to spend all 40 requests, this should succeed. 80 overriddenTxn40, err := newTransaction(overriddenLimit, overriddenBucketKey, 40) 81 test.AssertNotError(t, err, "txn should be valid") 82 d, err := l.Spend(testCtx, overriddenTxn40) 83 test.AssertNotError(t, err, "should not error") 84 test.Assert(t, d.allowed, "should be allowed") 85 86 // Attempting to spend 1 more, this should fail. 87 overriddenTxn1, err := newTransaction(overriddenLimit, overriddenBucketKey, 1) 88 test.AssertNotError(t, err, "txn should be valid") 89 d, err = l.Spend(testCtx, overriddenTxn1) 90 test.AssertNotError(t, err, "should not error") 91 test.Assert(t, !d.allowed, "should not be allowed") 92 test.AssertEquals(t, d.remaining, int64(0)) 93 test.AssertEquals(t, d.resetIn, time.Second) 94 95 // Verify our RetryIn is correct. 1 second == 1000 milliseconds and 96 // 1000/40 = 25 milliseconds per request. 97 test.AssertEquals(t, d.retryIn, time.Millisecond*25) 98 99 // Wait 50 milliseconds and try again. 100 clk.Add(d.retryIn) 101 102 // We should be allowed to spend 1 more request. 103 d, err = l.Spend(testCtx, overriddenTxn1) 104 test.AssertNotError(t, err, "should not error") 105 test.Assert(t, d.allowed, "should be allowed") 106 test.AssertEquals(t, d.remaining, int64(0)) 107 test.AssertEquals(t, d.resetIn, time.Second) 108 109 // Wait 1 second for a full bucket reset. 110 clk.Add(d.resetIn) 111 112 // Quickly spend 40 requests in a row. 113 for i := range 40 { 114 d, err = l.Spend(testCtx, overriddenTxn1) 115 test.AssertNotError(t, err, "should not error") 116 test.Assert(t, d.allowed, "should be allowed") 117 test.AssertEquals(t, d.remaining, int64(39-i)) 118 } 119 120 // Attempting to spend 1 more, this should fail. 121 d, err = l.Spend(testCtx, overriddenTxn1) 122 test.AssertNotError(t, err, "should not error") 123 test.Assert(t, !d.allowed, "should not be allowed") 124 test.AssertEquals(t, d.remaining, int64(0)) 125 test.AssertEquals(t, d.resetIn, time.Second) 126 127 // Wait 1 second for a full bucket reset. 128 clk.Add(d.resetIn) 129 130 normalBucketKey := newIPAddressBucketKey(NewRegistrationsPerIPAddress, netip.MustParseAddr(testIP)) 131 normalLimit, err := txnBuilder.getLimit(NewRegistrationsPerIPAddress, normalBucketKey) 132 test.AssertNotError(t, err, "should not error") 133 134 // Spend the same bucket but in a batch with bucket subject to 135 // default limits. This should succeed, but the decision should 136 // reflect that of the default bucket. 137 defaultTxn1, err := newTransaction(normalLimit, normalBucketKey, 1) 138 test.AssertNotError(t, err, "txn should be valid") 139 d, err = l.BatchSpend(testCtx, []Transaction{overriddenTxn1, defaultTxn1}) 140 test.AssertNotError(t, err, "should not error") 141 test.Assert(t, d.allowed, "should be allowed") 142 test.AssertEquals(t, d.remaining, int64(19)) 143 test.AssertEquals(t, d.retryIn, time.Duration(0)) 144 test.AssertEquals(t, d.resetIn, time.Millisecond*50) 145 146 // Refund quota to both buckets. This should succeed, but the 147 // decision should reflect that of the default bucket. 148 d, err = l.BatchRefund(testCtx, []Transaction{overriddenTxn1, defaultTxn1}) 149 test.AssertNotError(t, err, "should not error") 150 test.Assert(t, d.allowed, "should be allowed") 151 test.AssertEquals(t, d.remaining, int64(20)) 152 test.AssertEquals(t, d.retryIn, time.Duration(0)) 153 test.AssertEquals(t, d.resetIn, time.Duration(0)) 154 155 // Once more. 156 d, err = l.BatchSpend(testCtx, []Transaction{overriddenTxn1, defaultTxn1}) 157 test.AssertNotError(t, err, "should not error") 158 test.Assert(t, d.allowed, "should be allowed") 159 test.AssertEquals(t, d.remaining, int64(19)) 160 test.AssertEquals(t, d.retryIn, time.Duration(0)) 161 test.AssertEquals(t, d.resetIn, time.Millisecond*50) 162 163 // Reset between tests. 164 resetBucket(t, l, testCtx, overriddenLimit, overriddenBucketKey) 165 resetBucket(t, l, testCtx, normalLimit, normalBucketKey) 166 167 // Spend the same bucket but in a batch with a Transaction that is 168 // check-only. This should succeed, but the decision should reflect 169 // that of the default bucket. 170 defaultCheckOnlyTxn1, err := newCheckOnlyTransaction(normalLimit, normalBucketKey, 1) 171 test.AssertNotError(t, err, "txn should be valid") 172 d, err = l.BatchSpend(testCtx, []Transaction{overriddenTxn1, defaultCheckOnlyTxn1}) 173 test.AssertNotError(t, err, "should not error") 174 test.AssertEquals(t, d.remaining, int64(19)) 175 test.AssertEquals(t, d.retryIn, time.Duration(0)) 176 test.AssertEquals(t, d.resetIn, time.Millisecond*50) 177 178 // Check the remaining quota of the overridden bucket. 179 overriddenCheckOnlyTxn0, err := newCheckOnlyTransaction(overriddenLimit, overriddenBucketKey, 0) 180 test.AssertNotError(t, err, "txn should be valid") 181 d, err = l.Check(testCtx, overriddenCheckOnlyTxn0) 182 test.AssertNotError(t, err, "should not error") 183 test.AssertEquals(t, d.remaining, int64(39)) 184 test.AssertEquals(t, d.retryIn, time.Duration(0)) 185 test.AssertEquals(t, d.resetIn, time.Millisecond*25) 186 187 // Check the remaining quota of the default bucket. 188 defaultTxn0, err := newTransaction(normalLimit, normalBucketKey, 0) 189 test.AssertNotError(t, err, "txn should be valid") 190 d, err = l.Check(testCtx, defaultTxn0) 191 test.AssertNotError(t, err, "should not error") 192 test.AssertEquals(t, d.remaining, int64(20)) 193 test.AssertEquals(t, d.retryIn, time.Duration(0)) 194 test.AssertEquals(t, d.resetIn, time.Duration(0)) 195 196 // Spend the same bucket but in a batch with a Transaction that is 197 // spend-only. This should succeed, but the decision should reflect 198 // that of the overridden bucket. 199 defaultSpendOnlyTxn1, err := newSpendOnlyTransaction(normalLimit, normalBucketKey, 1) 200 test.AssertNotError(t, err, "txn should be valid") 201 d, err = l.BatchSpend(testCtx, []Transaction{overriddenTxn1, defaultSpendOnlyTxn1}) 202 test.AssertNotError(t, err, "should not error") 203 test.AssertEquals(t, d.remaining, int64(38)) 204 test.AssertEquals(t, d.retryIn, time.Duration(0)) 205 test.AssertEquals(t, d.resetIn, time.Millisecond*50) 206 207 // Check the remaining quota of the overridden bucket. 208 d, err = l.Check(testCtx, overriddenCheckOnlyTxn0) 209 test.AssertNotError(t, err, "should not error") 210 test.AssertEquals(t, d.remaining, int64(38)) 211 test.AssertEquals(t, d.retryIn, time.Duration(0)) 212 test.AssertEquals(t, d.resetIn, time.Millisecond*50) 213 214 // Check the remaining quota of the default bucket. 215 d, err = l.Check(testCtx, defaultTxn0) 216 test.AssertNotError(t, err, "should not error") 217 test.AssertEquals(t, d.remaining, int64(19)) 218 test.AssertEquals(t, d.retryIn, time.Duration(0)) 219 test.AssertEquals(t, d.resetIn, time.Millisecond*50) 220 221 // Once more, but in now the spend-only Transaction will attempt to 222 // spend 20 requests. The spend-only Transaction should fail, but 223 // the decision should reflect that of the overridden bucket. 224 defaultSpendOnlyTxn20, err := newSpendOnlyTransaction(normalLimit, normalBucketKey, 20) 225 test.AssertNotError(t, err, "txn should be valid") 226 d, err = l.BatchSpend(testCtx, []Transaction{overriddenTxn1, defaultSpendOnlyTxn20}) 227 test.AssertNotError(t, err, "should not error") 228 test.AssertEquals(t, d.remaining, int64(37)) 229 test.AssertEquals(t, d.retryIn, time.Duration(0)) 230 test.AssertEquals(t, d.resetIn, time.Millisecond*75) 231 232 // Check the remaining quota of the overridden bucket. 233 d, err = l.Check(testCtx, overriddenCheckOnlyTxn0) 234 test.AssertNotError(t, err, "should not error") 235 test.AssertEquals(t, d.remaining, int64(37)) 236 test.AssertEquals(t, d.retryIn, time.Duration(0)) 237 test.AssertEquals(t, d.resetIn, time.Millisecond*75) 238 239 // Check the remaining quota of the default bucket. 240 d, err = l.Check(testCtx, defaultTxn0) 241 test.AssertNotError(t, err, "should not error") 242 test.AssertEquals(t, d.remaining, int64(19)) 243 test.AssertEquals(t, d.retryIn, time.Duration(0)) 244 test.AssertEquals(t, d.resetIn, time.Millisecond*50) 245 246 // Reset between tests. 247 resetBucket(t, l, testCtx, overriddenLimit, overriddenBucketKey) 248 }) 249 } 250 } 251 252 func TestLimiter_InitializationViaCheckAndSpend(t *testing.T) { 253 t.Parallel() 254 testCtx, limiters, txnBuilder, _, testIP := setup(t) 255 for name, l := range limiters { 256 t.Run(name, func(t *testing.T) { 257 bucketKey := newIPAddressBucketKey(NewRegistrationsPerIPAddress, netip.MustParseAddr(testIP)) 258 limit, err := txnBuilder.getLimit(NewRegistrationsPerIPAddress, bucketKey) 259 test.AssertNotError(t, err, "should not error") 260 261 // Check on an empty bucket should return the theoretical next state 262 // of that bucket if the cost were spent. 263 txn1, err := newTransaction(limit, bucketKey, 1) 264 test.AssertNotError(t, err, "txn should be valid") 265 d, err := l.Check(testCtx, txn1) 266 test.AssertNotError(t, err, "should not error") 267 test.Assert(t, d.allowed, "should be allowed") 268 test.AssertEquals(t, d.remaining, int64(19)) 269 // Verify our ResetIn timing is correct. 1 second == 1000 270 // milliseconds and 1000/20 = 50 milliseconds per request. 271 test.AssertEquals(t, d.resetIn, time.Millisecond*50) 272 test.AssertEquals(t, d.retryIn, time.Duration(0)) 273 274 // However, that cost should not be spent yet, a 0 cost check should 275 // tell us that we actually have 20 remaining. 276 txn0, err := newTransaction(limit, bucketKey, 0) 277 test.AssertNotError(t, err, "txn should be valid") 278 d, err = l.Check(testCtx, txn0) 279 test.AssertNotError(t, err, "should not error") 280 test.Assert(t, d.allowed, "should be allowed") 281 test.AssertEquals(t, d.remaining, int64(20)) 282 test.AssertEquals(t, d.resetIn, time.Duration(0)) 283 test.AssertEquals(t, d.retryIn, time.Duration(0)) 284 285 // Reset our bucket. 286 resetBucket(t, l, testCtx, limit, bucketKey) 287 288 // Similar to above, but we'll use Spend() to actually initialize 289 // the bucket. Spend should return the same result as Check. 290 d, err = l.Spend(testCtx, txn1) 291 test.AssertNotError(t, err, "should not error") 292 test.Assert(t, d.allowed, "should be allowed") 293 test.AssertEquals(t, d.remaining, int64(19)) 294 // Verify our ResetIn timing is correct. 1 second == 1000 295 // milliseconds and 1000/20 = 50 milliseconds per request. 296 test.AssertEquals(t, d.resetIn, time.Millisecond*50) 297 test.AssertEquals(t, d.retryIn, time.Duration(0)) 298 299 // And that cost should have been spent; a 0 cost check should still 300 // tell us that we have 19 remaining. 301 d, err = l.Check(testCtx, txn0) 302 test.AssertNotError(t, err, "should not error") 303 test.Assert(t, d.allowed, "should be allowed") 304 test.AssertEquals(t, d.remaining, int64(19)) 305 // Verify our ResetIn is correct. 1 second == 1000 milliseconds and 306 // 1000/20 = 50 milliseconds per request. 307 test.AssertEquals(t, d.resetIn, time.Millisecond*50) 308 test.AssertEquals(t, d.retryIn, time.Duration(0)) 309 }) 310 } 311 } 312 313 func TestLimiter_DefaultLimits(t *testing.T) { 314 t.Parallel() 315 testCtx, limiters, txnBuilder, clk, testIP := setup(t) 316 for name, l := range limiters { 317 t.Run(name, func(t *testing.T) { 318 bucketKey := newIPAddressBucketKey(NewRegistrationsPerIPAddress, netip.MustParseAddr(testIP)) 319 limit, err := txnBuilder.getLimit(NewRegistrationsPerIPAddress, bucketKey) 320 test.AssertNotError(t, err, "should not error") 321 322 // Attempt to spend all 20 requests, this should succeed. 323 txn20, err := newTransaction(limit, bucketKey, 20) 324 test.AssertNotError(t, err, "txn should be valid") 325 d, err := l.Spend(testCtx, txn20) 326 test.AssertNotError(t, err, "should not error") 327 test.Assert(t, d.allowed, "should be allowed") 328 test.AssertEquals(t, d.remaining, int64(0)) 329 test.AssertEquals(t, d.resetIn, time.Second) 330 331 // Attempting to spend 1 more, this should fail. 332 txn1, err := newTransaction(limit, bucketKey, 1) 333 test.AssertNotError(t, err, "txn should be valid") 334 d, err = l.Spend(testCtx, txn1) 335 test.AssertNotError(t, err, "should not error") 336 test.Assert(t, !d.allowed, "should not be allowed") 337 test.AssertEquals(t, d.remaining, int64(0)) 338 test.AssertEquals(t, d.resetIn, time.Second) 339 340 // Verify our ResetIn is correct. 1 second == 1000 milliseconds and 341 // 1000/20 = 50 milliseconds per request. 342 test.AssertEquals(t, d.retryIn, time.Millisecond*50) 343 344 // Wait 50 milliseconds and try again. 345 clk.Add(d.retryIn) 346 347 // We should be allowed to spend 1 more request. 348 d, err = l.Spend(testCtx, txn1) 349 test.AssertNotError(t, err, "should not error") 350 test.Assert(t, d.allowed, "should be allowed") 351 test.AssertEquals(t, d.remaining, int64(0)) 352 test.AssertEquals(t, d.resetIn, time.Second) 353 354 // Wait 1 second for a full bucket reset. 355 clk.Add(d.resetIn) 356 357 // Quickly spend 20 requests in a row. 358 for i := range 20 { 359 d, err = l.Spend(testCtx, txn1) 360 test.AssertNotError(t, err, "should not error") 361 test.Assert(t, d.allowed, "should be allowed") 362 test.AssertEquals(t, d.remaining, int64(19-i)) 363 } 364 365 // Attempting to spend 1 more, this should fail. 366 d, err = l.Spend(testCtx, txn1) 367 test.AssertNotError(t, err, "should not error") 368 test.Assert(t, !d.allowed, "should not be allowed") 369 test.AssertEquals(t, d.remaining, int64(0)) 370 test.AssertEquals(t, d.resetIn, time.Second) 371 }) 372 } 373 } 374 375 func TestLimiter_RefundAndReset(t *testing.T) { 376 t.Parallel() 377 testCtx, limiters, txnBuilder, clk, testIP := setup(t) 378 for name, l := range limiters { 379 t.Run(name, func(t *testing.T) { 380 bucketKey := newIPAddressBucketKey(NewRegistrationsPerIPAddress, netip.MustParseAddr(testIP)) 381 limit, err := txnBuilder.getLimit(NewRegistrationsPerIPAddress, bucketKey) 382 test.AssertNotError(t, err, "should not error") 383 384 // Attempt to spend all 20 requests, this should succeed. 385 txn20, err := newTransaction(limit, bucketKey, 20) 386 test.AssertNotError(t, err, "txn should be valid") 387 d, err := l.Spend(testCtx, txn20) 388 test.AssertNotError(t, err, "should not error") 389 test.Assert(t, d.allowed, "should be allowed") 390 test.AssertEquals(t, d.remaining, int64(0)) 391 test.AssertEquals(t, d.resetIn, time.Second) 392 393 // Refund 10 requests. 394 txn10, err := newTransaction(limit, bucketKey, 10) 395 test.AssertNotError(t, err, "txn should be valid") 396 d, err = l.Refund(testCtx, txn10) 397 test.AssertNotError(t, err, "should not error") 398 test.AssertEquals(t, d.remaining, int64(10)) 399 400 // Spend 10 requests, this should succeed. 401 d, err = l.Spend(testCtx, txn10) 402 test.AssertNotError(t, err, "should not error") 403 test.Assert(t, d.allowed, "should be allowed") 404 test.AssertEquals(t, d.remaining, int64(0)) 405 test.AssertEquals(t, d.resetIn, time.Second) 406 407 resetBucket(t, l, testCtx, limit, bucketKey) 408 409 // Attempt to spend 20 more requests, this should succeed. 410 d, err = l.Spend(testCtx, txn20) 411 test.AssertNotError(t, err, "should not error") 412 test.Assert(t, d.allowed, "should be allowed") 413 test.AssertEquals(t, d.remaining, int64(0)) 414 test.AssertEquals(t, d.resetIn, time.Second) 415 416 // Reset to full. 417 clk.Add(d.resetIn) 418 419 // Refund 1 requests above our limit, this should fail. 420 txn1, err := newTransaction(limit, bucketKey, 1) 421 test.AssertNotError(t, err, "txn should be valid") 422 d, err = l.Refund(testCtx, txn1) 423 test.AssertNotError(t, err, "should not error") 424 test.Assert(t, !d.allowed, "should not be allowed") 425 test.AssertEquals(t, d.remaining, int64(20)) 426 427 // Spend so we can refund. 428 _, err = l.Spend(testCtx, txn1) 429 test.AssertNotError(t, err, "should not error") 430 431 // Refund a spendOnly Transaction, which should succeed. 432 spendOnlyTxn1, err := newSpendOnlyTransaction(limit, bucketKey, 1) 433 test.AssertNotError(t, err, "txn should be valid") 434 _, err = l.Refund(testCtx, spendOnlyTxn1) 435 test.AssertNotError(t, err, "should not error") 436 437 // Spend so we can refund. 438 expectedDecision, err := l.Spend(testCtx, txn1) 439 test.AssertNotError(t, err, "should not error") 440 441 // Refund a checkOnly Transaction, which shouldn't error but should 442 // return the same TAT as the previous spend. 443 checkOnlyTxn1, err := newCheckOnlyTransaction(limit, bucketKey, 1) 444 test.AssertNotError(t, err, "txn should be valid") 445 newDecision, err := l.Refund(testCtx, checkOnlyTxn1) 446 test.AssertNotError(t, err, "should not error") 447 test.AssertEquals(t, newDecision.newTAT, expectedDecision.newTAT) 448 }) 449 } 450 } 451 452 func TestRateLimitError(t *testing.T) { 453 t.Parallel() 454 now := clock.NewFake().Now() 455 456 testCases := []struct { 457 name string 458 decision *Decision 459 expectedErr string 460 expectedErrType berrors.ErrorType 461 }{ 462 { 463 name: "Allowed decision", 464 decision: &Decision{ 465 allowed: true, 466 }, 467 }, 468 { 469 name: "RegistrationsPerIP limit reached", 470 decision: &Decision{ 471 allowed: false, 472 retryIn: 5 * time.Second, 473 transaction: Transaction{ 474 limit: &Limit{ 475 Name: NewRegistrationsPerIPAddress, 476 Burst: 10, 477 Period: config.Duration{Duration: time.Hour}, 478 }, 479 }, 480 }, 481 expectedErr: "too many new registrations (10) from this IP address in the last 1h0m0s, retry after 1970-01-01 00:00:05 UTC: see https://letsencrypt.org/docs/rate-limits/#new-registrations-per-ip-address", 482 expectedErrType: berrors.RateLimit, 483 }, 484 { 485 name: "RegistrationsPerIPv6Range limit reached", 486 decision: &Decision{ 487 allowed: false, 488 retryIn: 10 * time.Second, 489 transaction: Transaction{ 490 limit: &Limit{ 491 Name: NewRegistrationsPerIPv6Range, 492 Burst: 5, 493 Period: config.Duration{Duration: time.Hour}, 494 }, 495 }, 496 }, 497 expectedErr: "too many new registrations (5) from this /48 subnet of IPv6 addresses in the last 1h0m0s, retry after 1970-01-01 00:00:10 UTC: see https://letsencrypt.org/docs/rate-limits/#new-registrations-per-ipv6-range", 498 expectedErrType: berrors.RateLimit, 499 }, 500 { 501 name: "NewOrdersPerAccount limit reached", 502 decision: &Decision{ 503 allowed: false, 504 retryIn: 10 * time.Second, 505 transaction: Transaction{ 506 limit: &Limit{ 507 Name: NewOrdersPerAccount, 508 Burst: 2, 509 Period: config.Duration{Duration: time.Hour}, 510 }, 511 }, 512 }, 513 expectedErr: "too many new orders (2) from this account in the last 1h0m0s, retry after 1970-01-01 00:00:10 UTC: see https://letsencrypt.org/docs/rate-limits/#new-orders-per-account", 514 expectedErrType: berrors.RateLimit, 515 }, 516 { 517 name: "FailedAuthorizationsPerDomainPerAccount limit reached", 518 decision: &Decision{ 519 allowed: false, 520 retryIn: 15 * time.Second, 521 transaction: Transaction{ 522 limit: &Limit{ 523 Name: FailedAuthorizationsPerDomainPerAccount, 524 Burst: 7, 525 Period: config.Duration{Duration: time.Hour}, 526 }, 527 bucketKey: "4:12345:example.com", 528 }, 529 }, 530 expectedErr: "too many failed authorizations (7) for \"example.com\" in the last 1h0m0s, retry after 1970-01-01 00:00:15 UTC: see https://letsencrypt.org/docs/rate-limits/#authorization-failures-per-hostname-per-account", 531 expectedErrType: berrors.RateLimit, 532 }, 533 { 534 name: "CertificatesPerDomain limit reached", 535 decision: &Decision{ 536 allowed: false, 537 retryIn: 20 * time.Second, 538 transaction: Transaction{ 539 limit: &Limit{ 540 Name: CertificatesPerDomain, 541 Burst: 3, 542 Period: config.Duration{Duration: time.Hour}, 543 }, 544 bucketKey: "5:example.org", 545 }, 546 }, 547 expectedErr: "too many certificates (3) already issued for \"example.org\" in the last 1h0m0s, retry after 1970-01-01 00:00:20 UTC: see https://letsencrypt.org/docs/rate-limits/#new-certificates-per-registered-domain", 548 expectedErrType: berrors.RateLimit, 549 }, 550 { 551 name: "CertificatesPerDomainPerAccount limit reached", 552 decision: &Decision{ 553 allowed: false, 554 retryIn: 20 * time.Second, 555 transaction: Transaction{ 556 limit: &Limit{ 557 Name: CertificatesPerDomainPerAccount, 558 Burst: 3, 559 Period: config.Duration{Duration: time.Hour}, 560 }, 561 bucketKey: "6:12345678:example.net", 562 }, 563 }, 564 expectedErr: "too many certificates (3) already issued for \"example.net\" in the last 1h0m0s, retry after 1970-01-01 00:00:20 UTC: see https://letsencrypt.org/docs/rate-limits/#new-certificates-per-registered-domain", 565 expectedErrType: berrors.RateLimit, 566 }, 567 { 568 name: "LimitOverrideRequestsPerIPAddress limit reached", 569 decision: &Decision{ 570 allowed: false, 571 retryIn: 20 * time.Second, 572 transaction: Transaction{ 573 limit: &Limit{ 574 Name: LimitOverrideRequestsPerIPAddress, 575 Burst: 3, 576 Period: config.Duration{Duration: time.Hour}, 577 }, 578 }, 579 }, 580 expectedErr: "too many override request form submissions (3) from this IP address in the last 1h0m0s, retry after 1970-01-01 00:00:20 UTC: see https://letsencrypt.org/docs/rate-limits/#limit-override-requests-per-ip-address", 581 expectedErrType: berrors.RateLimit, 582 }, 583 { 584 name: "Unknown rate limit name", 585 decision: &Decision{ 586 allowed: false, 587 retryIn: 30 * time.Second, 588 transaction: Transaction{ 589 limit: &Limit{ 590 Name: 9999999, 591 }, 592 }, 593 }, 594 expectedErr: "cannot generate error for unknown rate limit", 595 expectedErrType: berrors.InternalServer, 596 }, 597 } 598 599 for _, tc := range testCases { 600 t.Run(tc.name, func(t *testing.T) { 601 t.Parallel() 602 err := tc.decision.Result(now) 603 if tc.expectedErr == "" { 604 test.AssertNotError(t, err, "expected no error") 605 } else { 606 test.AssertError(t, err, "expected an error") 607 test.AssertEquals(t, err.Error(), tc.expectedErr) 608 test.AssertErrorIs(t, err, tc.expectedErrType) 609 } 610 }) 611 } 612 }