github.com/letsencrypt/boulder@v0.20251208.0/ratelimits/transaction.go (about) 1 package ratelimits 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "io" 8 "net/netip" 9 "strconv" 10 "time" 11 12 "google.golang.org/grpc" 13 "google.golang.org/protobuf/types/known/emptypb" 14 15 "github.com/prometheus/client_golang/prometheus" 16 "github.com/prometheus/client_golang/prometheus/promauto" 17 18 "github.com/letsencrypt/boulder/config" 19 "github.com/letsencrypt/boulder/core" 20 "github.com/letsencrypt/boulder/identifier" 21 blog "github.com/letsencrypt/boulder/log" 22 sapb "github.com/letsencrypt/boulder/sa/proto" 23 ) 24 25 // ErrInvalidCost indicates that the cost specified was < 0. 26 var ErrInvalidCost = fmt.Errorf("invalid cost, must be >= 0") 27 28 // ErrInvalidCostOverLimit indicates that the cost specified was > limit.Burst. 29 var ErrInvalidCostOverLimit = fmt.Errorf("invalid cost, must be <= limit.Burst") 30 31 // newIPAddressBucketKey returns a bucketKey for limits that use 32 // the 'enum:ipAddress' bucket key format. 33 func newIPAddressBucketKey(name Name, ip netip.Addr) string { 34 return joinWithColon(name.EnumString(), ip.String()) 35 } 36 37 // newIPv6RangeCIDRBucketKey returns a bucketKey for limits that 38 // use the 'enum:ipv6RangeCIDR' bucket key format. 39 func newIPv6RangeCIDRBucketKey(name Name, prefix netip.Prefix) string { 40 return joinWithColon(name.EnumString(), prefix.String()) 41 } 42 43 // newRegIdBucketKey returns a bucketKey for limits that use the 44 // 'enum:regId' bucket key format. 45 func newRegIdBucketKey(name Name, regId int64) string { 46 return joinWithColon(name.EnumString(), strconv.FormatInt(regId, 10)) 47 } 48 49 // newDomainOrCIDRBucketKey returns a bucketKey for limits that use 50 // the 'enum:domainOrCIDR' bucket key formats. 51 func newDomainOrCIDRBucketKey(name Name, domainOrCIDR string) string { 52 return joinWithColon(name.EnumString(), domainOrCIDR) 53 } 54 55 // newRegIdIdentValueBucketKey returns a bucketKey for limits that use the 56 // 'enum:regId:identValue' bucket key format. 57 func newRegIdIdentValueBucketKey(name Name, regId int64, orderIdent string) string { 58 return joinWithColon(name.EnumString(), strconv.FormatInt(regId, 10), orderIdent) 59 } 60 61 // newFQDNSetBucketKey validates and returns a bucketKey for limits that use the 62 // 'enum:fqdnSet' bucket key format. 63 func newFQDNSetBucketKey(name Name, orderIdents identifier.ACMEIdentifiers) string { 64 return joinWithColon(name.EnumString(), fmt.Sprintf("%x", core.HashIdentifiers(orderIdents))) 65 } 66 67 // Transaction represents a single rate limit operation. It includes a 68 // bucketKey, which combines the specific rate limit enum with a unique 69 // identifier to form the key where the state of the "bucket" can be referenced 70 // or stored by the Limiter, the rate limit being enforced, a cost which MUST be 71 // >= 0, and check/spend fields, which indicate how the Transaction should be 72 // processed. The following are acceptable combinations of check/spend: 73 // - check-and-spend: when check and spend are both true, the cost will be 74 // checked against the bucket's capacity and spent/refunded, when possible. 75 // - check-only: when only check is true, the cost will be checked against the 76 // bucket's capacity, but will never be spent/refunded. 77 // - spend-only: when only spend is true, spending is best-effort. Regardless 78 // of the bucket's capacity, the transaction will be considered "allowed". 79 // - reset-only: when reset is true, the bucket will be reset to full capacity. 80 // - allow-only: when neither check nor spend are true, the transaction will 81 // be considered "allowed" regardless of the bucket's capacity. This is 82 // useful for limits that are disabled. 83 // 84 // The zero value of Transaction is an allow-only transaction and is valid even if 85 // it would fail validateTransaction (for instance because cost and burst are zero). 86 type Transaction struct { 87 bucketKey string 88 limit *Limit 89 cost int64 90 check bool 91 spend bool 92 reset bool 93 } 94 95 func (txn Transaction) checkOnly() bool { 96 return txn.check && !txn.spend && !txn.reset 97 } 98 99 func (txn Transaction) spendOnly() bool { 100 return txn.spend && !txn.check && !txn.reset 101 } 102 103 func (txn Transaction) allowOnly() bool { 104 return !txn.check && !txn.spend && !txn.reset 105 } 106 107 func (txn Transaction) resetOnly() bool { 108 return txn.reset && !txn.check && !txn.spend 109 } 110 111 func validateTransaction(txn Transaction) (Transaction, error) { 112 if txn.limit == nil { 113 return Transaction{}, fmt.Errorf("invalid limit, must not be nil") 114 } 115 if txn.reset { 116 if txn.check || txn.spend { 117 return Transaction{}, fmt.Errorf("invalid reset transaction, check and spend must be false") 118 } 119 if txn.limit.Burst == 0 { 120 return Transaction{}, fmt.Errorf("invalid limit, burst must be > 0") 121 } 122 return txn, nil 123 } 124 if txn.cost < 0 { 125 return Transaction{}, ErrInvalidCost 126 } 127 if txn.limit.Burst == 0 { 128 // This should never happen. If the limit was loaded from a file, 129 // Burst was validated then. If this is a zero-valued Transaction 130 // (that is, an allow-only transaction), then validateTransaction 131 // shouldn't be called because zero-valued transactions are automatically 132 // valid. 133 return Transaction{}, fmt.Errorf("invalid limit, burst must be > 0") 134 } 135 if txn.cost > txn.limit.Burst { 136 return Transaction{}, ErrInvalidCostOverLimit 137 } 138 return txn, nil 139 } 140 141 func newTransaction(limit *Limit, bucketKey string, cost int64) (Transaction, error) { 142 return validateTransaction(Transaction{ 143 bucketKey: bucketKey, 144 limit: limit, 145 cost: cost, 146 check: true, 147 spend: true, 148 }) 149 } 150 151 func newCheckOnlyTransaction(limit *Limit, bucketKey string, cost int64) (Transaction, error) { 152 return validateTransaction(Transaction{ 153 bucketKey: bucketKey, 154 limit: limit, 155 cost: cost, 156 check: true, 157 }) 158 } 159 160 func newSpendOnlyTransaction(limit *Limit, bucketKey string, cost int64) (Transaction, error) { 161 return validateTransaction(Transaction{ 162 bucketKey: bucketKey, 163 limit: limit, 164 cost: cost, 165 spend: true, 166 }) 167 } 168 169 func newResetTransaction(limit *Limit, bucketKey string) (Transaction, error) { 170 return validateTransaction(Transaction{ 171 bucketKey: bucketKey, 172 limit: limit, 173 reset: true, 174 }) 175 } 176 177 func newAllowOnlyTransaction() Transaction { 178 // Zero values are sufficient. 179 return Transaction{} 180 } 181 182 // TransactionBuilder is used to build Transactions for various rate limits. 183 // Each rate limit has a corresponding method that returns a Transaction for 184 // that limit. Call NewTransactionBuilder to create a new *TransactionBuilder. 185 type TransactionBuilder struct { 186 *limitRegistry 187 } 188 189 func (builder *TransactionBuilder) Ready() bool { 190 return builder.limitRegistry.overridesLoaded 191 } 192 193 // GetOverridesFunc is used to pass in the sa.GetEnabledRateLimitOverrides 194 // method to NewTransactionBuilderFromDatabase, rather than storing a full 195 // sa.SQLStorageAuthority. This makes testing significantly simpler. 196 type GetOverridesFunc func(context.Context, *emptypb.Empty, ...grpc.CallOption) (grpc.ServerStreamingClient[sapb.RateLimitOverrideResponse], error) 197 198 // NewTransactionBuilderFromDatabase returns a new *TransactionBuilder. The 199 // provided defaults path is expected to be a path to a YAML file that contains 200 // the default limits. The provided overrides function is expected to be an SA's 201 // GetEnabledRateLimitOverrides. Both are required. 202 func NewTransactionBuilderFromDatabase(defaults string, overrides GetOverridesFunc, stats prometheus.Registerer, logger blog.Logger) (*TransactionBuilder, error) { 203 defaultsData, err := loadDefaultsFromFile(defaults) 204 if err != nil { 205 return nil, err 206 } 207 208 refresher := func(ctx context.Context, errorGauge prometheus.Gauge, logger blog.Logger) (Limits, error) { 209 ctx, cancel := context.WithTimeout(ctx, 60*time.Second) 210 defer cancel() 211 212 stream, err := overrides(ctx, &emptypb.Empty{}) 213 if err != nil { 214 return nil, fmt.Errorf("fetching enabled overrides: %w", err) 215 } 216 217 overrides := make(Limits) 218 var errorCount float64 219 for { 220 r, err := stream.Recv() 221 if err != nil { 222 if err == io.EOF { 223 break 224 } 225 return nil, fmt.Errorf("reading overrides stream: %w", err) 226 } 227 228 limitName := Name(r.Override.LimitEnum) 229 230 bucketKey, err := hydrateOverrideLimit(r.Override.BucketKey, limitName) 231 if err != nil { 232 logger.Errf("hydrating %s override with key %q: %v", limitName.String(), r.Override.BucketKey, err) 233 errorCount++ 234 continue 235 } 236 237 newLimit := &Limit{ 238 Burst: r.Override.Burst, 239 Count: r.Override.Count, 240 Period: config.Duration{Duration: r.Override.Period.AsDuration()}, 241 Name: limitName, 242 Comment: fmt.Sprintf("Last Updated: %s - %s", 243 r.UpdatedAt.AsTime().Format("2006-01-02"), 244 r.Override.Comment, 245 ), 246 isOverride: true, 247 } 248 249 err = ValidateLimit(newLimit) 250 if err != nil { 251 logger.Errf("hydrating %s override with key %q: %v", newLimit.Name.String(), r.Override.BucketKey, err) 252 errorCount++ 253 continue 254 } 255 256 overrides[bucketKey] = newLimit 257 } 258 errorGauge.Set(errorCount) 259 return overrides, nil 260 } 261 262 return NewTransactionBuilder(defaultsData, refresher, stats, logger) 263 } 264 265 // NewTransactionBuilderFromFiles returns a new *TransactionBuilder. The 266 // provided defaults and overrides paths are expected to be paths to YAML files 267 // that contain the default and override limits, respectively. Overrides is 268 // optional, defaults is required. 269 func NewTransactionBuilderFromFiles(defaults string, overrides string, stats prometheus.Registerer, logger blog.Logger) (*TransactionBuilder, error) { 270 defaultsData, err := loadDefaultsFromFile(defaults) 271 if err != nil { 272 return nil, err 273 } 274 275 if overrides == "" { 276 return NewTransactionBuilder(defaultsData, nil, stats, logger) 277 } 278 279 refresher := func(ctx context.Context, _ prometheus.Gauge, _ blog.Logger) (Limits, error) { 280 overridesData, err := loadOverridesFromFile(overrides) 281 if err != nil { 282 return nil, err 283 } 284 return parseOverrideLimits(overridesData) 285 } 286 287 return NewTransactionBuilder(defaultsData, refresher, stats, logger) 288 } 289 290 // NewTransactionBuilder returns a new *TransactionBuilder. A defaults map is 291 // required. 292 func NewTransactionBuilder(defaultConfigs LimitConfigs, refresher OverridesRefresher, stats prometheus.Registerer, logger blog.Logger) (*TransactionBuilder, error) { 293 defaults, err := parseDefaultLimits(defaultConfigs) 294 if err != nil { 295 return nil, err 296 } 297 298 if refresher == nil { 299 refresher = func(context.Context, prometheus.Gauge, blog.Logger) (Limits, error) { 300 return nil, nil 301 } 302 } 303 304 overridesTimestamp := promauto.With(stats).NewGauge(prometheus.GaugeOpts{ 305 Namespace: "ratelimits", 306 Subsystem: "overrides", 307 Name: "timestamp_seconds", 308 Help: "A gauge with the last timestamp when overrides were successfully loaded", 309 }) 310 311 overridesErrors := promauto.With(stats).NewGauge(prometheus.GaugeOpts{ 312 Namespace: "ratelimits", 313 Subsystem: "overrides", 314 Name: "errors", 315 Help: "A gauge with the number of errors while last trying to load overrides", 316 }) 317 318 overridesPerLimit := promauto.With(stats).NewGaugeVec(prometheus.GaugeOpts{ 319 Namespace: "ratelimits", 320 Subsystem: "overrides", 321 Name: "active", 322 Help: "A gauge with the number of overrides, partitioned by rate limit", 323 }, []string{"limit"}) 324 325 registry := &limitRegistry{ 326 defaults: defaults, 327 refreshOverrides: refresher, 328 logger: logger, 329 330 overridesTimestamp: overridesTimestamp, 331 overridesErrors: overridesErrors, 332 overridesPerLimit: *overridesPerLimit, 333 } 334 335 return &TransactionBuilder{registry}, nil 336 } 337 338 // registrationsPerIPAddressTransaction returns a Transaction for the 339 // NewRegistrationsPerIPAddress limit for the provided IP address. 340 func (builder *TransactionBuilder) registrationsPerIPAddressTransaction(ip netip.Addr) (Transaction, error) { 341 bucketKey := newIPAddressBucketKey(NewRegistrationsPerIPAddress, ip) 342 limit, err := builder.getLimit(NewRegistrationsPerIPAddress, bucketKey) 343 if err != nil { 344 if errors.Is(err, errLimitDisabled) { 345 return newAllowOnlyTransaction(), nil 346 } 347 return Transaction{}, err 348 } 349 return newTransaction(limit, bucketKey, 1) 350 } 351 352 // registrationsPerIPv6RangeTransaction returns a Transaction for the 353 // NewRegistrationsPerIPv6Range limit for the /48 IPv6 range which contains the 354 // provided IPv6 address. 355 func (builder *TransactionBuilder) registrationsPerIPv6RangeTransaction(ip netip.Addr) (Transaction, error) { 356 prefix, err := coveringIPPrefix(NewRegistrationsPerIPv6Range, ip) 357 if err != nil { 358 return Transaction{}, fmt.Errorf("computing covering prefix for %q: %w", ip, err) 359 } 360 bucketKey := newIPv6RangeCIDRBucketKey(NewRegistrationsPerIPv6Range, prefix) 361 362 limit, err := builder.getLimit(NewRegistrationsPerIPv6Range, bucketKey) 363 if err != nil { 364 if errors.Is(err, errLimitDisabled) { 365 return newAllowOnlyTransaction(), nil 366 } 367 return Transaction{}, err 368 } 369 return newTransaction(limit, bucketKey, 1) 370 } 371 372 // ordersPerAccountTransaction returns a Transaction for the NewOrdersPerAccount 373 // limit for the provided ACME registration Id. 374 func (builder *TransactionBuilder) ordersPerAccountTransaction(regId int64) (Transaction, error) { 375 bucketKey := newRegIdBucketKey(NewOrdersPerAccount, regId) 376 limit, err := builder.getLimit(NewOrdersPerAccount, bucketKey) 377 if err != nil { 378 if errors.Is(err, errLimitDisabled) { 379 return newAllowOnlyTransaction(), nil 380 } 381 return Transaction{}, err 382 } 383 return newTransaction(limit, bucketKey, 1) 384 } 385 386 // FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions returns a slice 387 // of Transactions for the provided order identifiers. An error is returned if 388 // any of the order identifiers' values are invalid. This method should be used 389 // for checking capacity, before allowing more authorizations to be created. 390 // 391 // Precondition: len(orderIdents) < maxNames. 392 func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions(regId int64, orderIdents identifier.ACMEIdentifiers) ([]Transaction, error) { 393 // FailedAuthorizationsPerDomainPerAccount limit uses the 'enum:regId' 394 // bucket key format for overrides. 395 perAccountBucketKey := newRegIdBucketKey(FailedAuthorizationsPerDomainPerAccount, regId) 396 limit, err := builder.getLimit(FailedAuthorizationsPerDomainPerAccount, perAccountBucketKey) 397 if err != nil { 398 if errors.Is(err, errLimitDisabled) { 399 return []Transaction{newAllowOnlyTransaction()}, nil 400 } 401 return nil, err 402 } 403 404 var txns []Transaction 405 for _, ident := range orderIdents { 406 // FailedAuthorizationsPerDomainPerAccount limit uses the 407 // 'enum:regId:identValue' bucket key format for transactions. 408 perIdentValuePerAccountBucketKey := newRegIdIdentValueBucketKey(FailedAuthorizationsPerDomainPerAccount, regId, ident.Value) 409 410 // Add a check-only transaction for each per identValue per account 411 // bucket. 412 txn, err := newCheckOnlyTransaction(limit, perIdentValuePerAccountBucketKey, 1) 413 if err != nil { 414 return nil, err 415 } 416 txns = append(txns, txn) 417 } 418 return txns, nil 419 } 420 421 // FailedAuthorizationsPerDomainPerAccountSpendOnlyTransaction returns a spend- 422 // only Transaction for the provided order identifier. An error is returned if 423 // the order identifier's value is invalid. This method should be used for 424 // spending capacity, as a result of a failed authorization. 425 func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountSpendOnlyTransaction(regId int64, orderIdent identifier.ACMEIdentifier) (Transaction, error) { 426 // FailedAuthorizationsPerDomainPerAccount limit uses the 'enum:regId' 427 // bucket key format for overrides. 428 perAccountBucketKey := newRegIdBucketKey(FailedAuthorizationsPerDomainPerAccount, regId) 429 limit, err := builder.getLimit(FailedAuthorizationsPerDomainPerAccount, perAccountBucketKey) 430 if err != nil { 431 if errors.Is(err, errLimitDisabled) { 432 return newAllowOnlyTransaction(), nil 433 } 434 return Transaction{}, err 435 } 436 437 // FailedAuthorizationsPerDomainPerAccount limit uses the 438 // 'enum:regId:identValue' bucket key format for transactions. 439 perIdentValuePerAccountBucketKey := newRegIdIdentValueBucketKey(FailedAuthorizationsPerDomainPerAccount, regId, orderIdent.Value) 440 txn, err := newSpendOnlyTransaction(limit, perIdentValuePerAccountBucketKey, 1) 441 if err != nil { 442 return Transaction{}, err 443 } 444 445 return txn, nil 446 } 447 448 // FailedAuthorizationsForPausingPerDomainPerAccountTransaction returns a 449 // Transaction for the provided order identifier. An error is returned if the 450 // order identifier's value is invalid. This method should be used for spending 451 // capacity, as a result of a failed authorization. 452 func (builder *TransactionBuilder) FailedAuthorizationsForPausingPerDomainPerAccountTransaction(regId int64, orderIdent identifier.ACMEIdentifier) (Transaction, error) { 453 // FailedAuthorizationsForPausingPerDomainPerAccount limit uses the 'enum:regId' 454 // bucket key format for overrides. 455 perAccountBucketKey := newRegIdBucketKey(FailedAuthorizationsForPausingPerDomainPerAccount, regId) 456 limit, err := builder.getLimit(FailedAuthorizationsForPausingPerDomainPerAccount, perAccountBucketKey) 457 if err != nil { 458 if errors.Is(err, errLimitDisabled) { 459 return newAllowOnlyTransaction(), nil 460 } 461 return Transaction{}, err 462 } 463 464 // FailedAuthorizationsForPausingPerDomainPerAccount limit uses the 465 // 'enum:regId:identValue' bucket key format for transactions. 466 perIdentValuePerAccountBucketKey := newRegIdIdentValueBucketKey(FailedAuthorizationsForPausingPerDomainPerAccount, regId, orderIdent.Value) 467 txn, err := newTransaction(limit, perIdentValuePerAccountBucketKey, 1) 468 if err != nil { 469 return Transaction{}, err 470 } 471 472 return txn, nil 473 } 474 475 // certificatesPerDomainCheckOnlyTransactions returns a slice of Transactions 476 // for the provided order identifiers. It returns an error if any of the order 477 // identifiers' values are invalid. This method should be used for checking 478 // capacity, before allowing more orders to be created. If a 479 // CertificatesPerDomainPerAccount override is active, a check-only Transaction 480 // is created for each per account per domainOrCIDR bucket. Otherwise, a 481 // check-only Transaction is generated for each global per domainOrCIDR bucket. 482 // This method should be used for checking capacity, before allowing more orders 483 // to be created. 484 // 485 // Precondition: All orderIdents must comply with policy.WellFormedIdentifiers. 486 func (builder *TransactionBuilder) certificatesPerDomainCheckOnlyTransactions(regId int64, orderIdents identifier.ACMEIdentifiers) ([]Transaction, error) { 487 if len(orderIdents) > 100 { 488 return nil, fmt.Errorf("unwilling to process more than 100 rate limit transactions, got %d", len(orderIdents)) 489 } 490 491 perAccountLimitBucketKey := newRegIdBucketKey(CertificatesPerDomainPerAccount, regId) 492 accountOverride := true 493 perAccountLimit, err := builder.getLimit(CertificatesPerDomainPerAccount, perAccountLimitBucketKey) 494 if err != nil { 495 // The CertificatesPerDomainPerAccount limit never has a default. If there is an override for it, 496 // the above call will return the override. But if there is none, it will return errLimitDisabled. 497 // In that case we want to continue, but make sure we don't reference `perAccountLimit` because it 498 // is not a valid limit. 499 if errors.Is(err, errLimitDisabled) { 500 accountOverride = false 501 } else { 502 return nil, err 503 } 504 } 505 506 coveringIdents, err := coveringIdentifiers(orderIdents) 507 if err != nil { 508 return nil, err 509 } 510 511 var txns []Transaction 512 for _, ident := range coveringIdents { 513 perDomainOrCIDRBucketKey := newDomainOrCIDRBucketKey(CertificatesPerDomain, ident) 514 if accountOverride { 515 if !perAccountLimit.isOverride { 516 return nil, fmt.Errorf("shouldn't happen: CertificatesPerDomainPerAccount limit is not an override") 517 } 518 perAccountPerDomainOrCIDRBucketKey := newRegIdIdentValueBucketKey(CertificatesPerDomainPerAccount, regId, ident) 519 // Add a check-only transaction for each per account per identValue 520 // bucket. 521 txn, err := newCheckOnlyTransaction(perAccountLimit, perAccountPerDomainOrCIDRBucketKey, 1) 522 if err != nil { 523 if errors.Is(err, errLimitDisabled) { 524 continue 525 } 526 return nil, err 527 } 528 txns = append(txns, txn) 529 } else { 530 // Use the per domainOrCIDR bucket key when no per account per 531 // domainOrCIDR override is configured. 532 perDomainOrCIDRLimit, err := builder.getLimit(CertificatesPerDomain, perDomainOrCIDRBucketKey) 533 if err != nil { 534 if errors.Is(err, errLimitDisabled) { 535 continue 536 } 537 return nil, err 538 } 539 // Add a check-only transaction for each per domainOrCIDR bucket. 540 txn, err := newCheckOnlyTransaction(perDomainOrCIDRLimit, perDomainOrCIDRBucketKey, 1) 541 if err != nil { 542 return nil, err 543 } 544 txns = append(txns, txn) 545 } 546 } 547 return txns, nil 548 } 549 550 // CertificatesPerDomainSpendOnlyTransactions returns a slice of Transactions 551 // for the provided order identifiers. It returns an error if any of the order 552 // identifiers' values are invalid. If a CertificatesPerDomainPerAccount 553 // override is configured, it generates two types of Transactions: 554 // - A spend-only Transaction for each per-account, per-domainOrCIDR bucket, 555 // which enforces the limit on certificates issued per domainOrCIDR for 556 // each account. 557 // - A spend-only Transaction for each per-domainOrCIDR bucket, which 558 // enforces the global limit on certificates issued per domainOrCIDR. 559 // 560 // If no CertificatesPerDomainPerAccount override is present, it returns a 561 // spend-only Transaction for each global per-domainOrCIDR bucket. This method 562 // should be used for spending capacity, when a certificate is issued. 563 // 564 // Precondition: orderIdents must all pass policy.WellFormedIdentifiers. 565 func (builder *TransactionBuilder) CertificatesPerDomainSpendOnlyTransactions(regId int64, orderIdents identifier.ACMEIdentifiers) ([]Transaction, error) { 566 if len(orderIdents) > 100 { 567 return nil, fmt.Errorf("unwilling to process more than 100 rate limit transactions, got %d", len(orderIdents)) 568 } 569 570 perAccountLimitBucketKey := newRegIdBucketKey(CertificatesPerDomainPerAccount, regId) 571 accountOverride := true 572 perAccountLimit, err := builder.getLimit(CertificatesPerDomainPerAccount, perAccountLimitBucketKey) 573 if err != nil { 574 // The CertificatesPerDomainPerAccount limit never has a default. If there is an override for it, 575 // the above call will return the override. But if there is none, it will return errLimitDisabled. 576 // In that case we want to continue, but make sure we don't reference `perAccountLimit` because it 577 // is not a valid limit. 578 if errors.Is(err, errLimitDisabled) { 579 accountOverride = false 580 } else { 581 return nil, err 582 } 583 } 584 585 coveringIdents, err := coveringIdentifiers(orderIdents) 586 if err != nil { 587 return nil, err 588 } 589 590 var txns []Transaction 591 for _, ident := range coveringIdents { 592 perDomainOrCIDRBucketKey := newDomainOrCIDRBucketKey(CertificatesPerDomain, ident) 593 if accountOverride { 594 if !perAccountLimit.isOverride { 595 return nil, fmt.Errorf("shouldn't happen: CertificatesPerDomainPerAccount limit is not an override") 596 } 597 perAccountPerDomainOrCIDRBucketKey := newRegIdIdentValueBucketKey(CertificatesPerDomainPerAccount, regId, ident) 598 // Add a spend-only transaction for each per account per 599 // domainOrCIDR bucket. 600 txn, err := newSpendOnlyTransaction(perAccountLimit, perAccountPerDomainOrCIDRBucketKey, 1) 601 if err != nil { 602 return nil, err 603 } 604 txns = append(txns, txn) 605 606 perDomainOrCIDRLimit, err := builder.getLimit(CertificatesPerDomain, perDomainOrCIDRBucketKey) 607 if err != nil { 608 if errors.Is(err, errLimitDisabled) { 609 continue 610 } 611 return nil, err 612 } 613 614 // Add a spend-only transaction for each per domainOrCIDR bucket. 615 txn, err = newSpendOnlyTransaction(perDomainOrCIDRLimit, perDomainOrCIDRBucketKey, 1) 616 if err != nil { 617 return nil, err 618 } 619 txns = append(txns, txn) 620 } else { 621 // Use the per domainOrCIDR bucket key when no per account per 622 // domainOrCIDR override is configured. 623 perDomainOrCIDRLimit, err := builder.getLimit(CertificatesPerDomain, perDomainOrCIDRBucketKey) 624 if err != nil { 625 if errors.Is(err, errLimitDisabled) { 626 continue 627 } 628 return nil, err 629 } 630 // Add a spend-only transaction for each per domainOrCIDR bucket. 631 txn, err := newSpendOnlyTransaction(perDomainOrCIDRLimit, perDomainOrCIDRBucketKey, 1) 632 if err != nil { 633 return nil, err 634 } 635 txns = append(txns, txn) 636 } 637 } 638 return txns, nil 639 } 640 641 // certificatesPerFQDNSetCheckOnlyTransaction returns a check-only Transaction 642 // for the provided order identifiers. This method should only be used for 643 // checking capacity, before allowing more orders to be created. 644 func (builder *TransactionBuilder) certificatesPerFQDNSetCheckOnlyTransaction(orderIdents identifier.ACMEIdentifiers) (Transaction, error) { 645 bucketKey := newFQDNSetBucketKey(CertificatesPerFQDNSet, orderIdents) 646 limit, err := builder.getLimit(CertificatesPerFQDNSet, bucketKey) 647 if err != nil { 648 if errors.Is(err, errLimitDisabled) { 649 return newAllowOnlyTransaction(), nil 650 } 651 return Transaction{}, err 652 } 653 return newCheckOnlyTransaction(limit, bucketKey, 1) 654 } 655 656 // CertificatesPerFQDNSetSpendOnlyTransaction returns a spend-only Transaction 657 // for the provided order identifiers. This method should only be used for 658 // spending capacity, when a certificate is issued. 659 func (builder *TransactionBuilder) CertificatesPerFQDNSetSpendOnlyTransaction(orderIdents identifier.ACMEIdentifiers) (Transaction, error) { 660 bucketKey := newFQDNSetBucketKey(CertificatesPerFQDNSet, orderIdents) 661 limit, err := builder.getLimit(CertificatesPerFQDNSet, bucketKey) 662 if err != nil { 663 if errors.Is(err, errLimitDisabled) { 664 return newAllowOnlyTransaction(), nil 665 } 666 return Transaction{}, err 667 } 668 return newSpendOnlyTransaction(limit, bucketKey, 1) 669 } 670 671 // NewOrderLimitTransactions takes in values from a new-order request and 672 // returns the set of rate limit transactions that should be evaluated before 673 // allowing the request to proceed. 674 // 675 // Precondition: idents must be a list of identifiers that all pass 676 // policy.WellFormedIdentifiers. 677 func (builder *TransactionBuilder) NewOrderLimitTransactions(regId int64, idents identifier.ACMEIdentifiers, isRenewal bool) ([]Transaction, error) { 678 makeTxnError := func(err error, limit Name) error { 679 return fmt.Errorf("error constructing rate limit transaction for %s rate limit: %w", limit, err) 680 } 681 682 var transactions []Transaction 683 if !isRenewal { 684 txn, err := builder.ordersPerAccountTransaction(regId) 685 if err != nil { 686 return nil, makeTxnError(err, NewOrdersPerAccount) 687 } 688 transactions = append(transactions, txn) 689 } 690 691 txns, err := builder.FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions(regId, idents) 692 if err != nil { 693 return nil, makeTxnError(err, FailedAuthorizationsPerDomainPerAccount) 694 } 695 transactions = append(transactions, txns...) 696 697 if !isRenewal { 698 txns, err := builder.certificatesPerDomainCheckOnlyTransactions(regId, idents) 699 if err != nil { 700 return nil, makeTxnError(err, CertificatesPerDomain) 701 } 702 transactions = append(transactions, txns...) 703 } 704 705 txn, err := builder.certificatesPerFQDNSetCheckOnlyTransaction(idents) 706 if err != nil { 707 return nil, makeTxnError(err, CertificatesPerFQDNSet) 708 } 709 return append(transactions, txn), nil 710 } 711 712 // NewAccountLimitTransactions takes in an IP address from a new-account request 713 // and returns the set of rate limit transactions that should be evaluated 714 // before allowing the request to proceed. 715 func (builder *TransactionBuilder) NewAccountLimitTransactions(ip netip.Addr) ([]Transaction, error) { 716 makeTxnError := func(err error, limit Name) error { 717 return fmt.Errorf("error constructing rate limit transaction for %s rate limit: %w", limit, err) 718 } 719 720 var transactions []Transaction 721 txn, err := builder.registrationsPerIPAddressTransaction(ip) 722 if err != nil { 723 return nil, makeTxnError(err, NewRegistrationsPerIPAddress) 724 } 725 transactions = append(transactions, txn) 726 727 if ip.Is4() { 728 // This request was made from an IPv4 address. 729 return transactions, nil 730 } 731 732 txn, err = builder.registrationsPerIPv6RangeTransaction(ip) 733 if err != nil { 734 return nil, makeTxnError(err, NewRegistrationsPerIPv6Range) 735 } 736 return append(transactions, txn), nil 737 } 738 739 func (builder *TransactionBuilder) NewPausingResetTransactions(regId int64, orderIdent identifier.ACMEIdentifier) ([]Transaction, error) { 740 perAccountBucketKey := newRegIdBucketKey(FailedAuthorizationsForPausingPerDomainPerAccount, regId) 741 limit, err := builder.getLimit(FailedAuthorizationsForPausingPerDomainPerAccount, perAccountBucketKey) 742 if err != nil { 743 if errors.Is(err, errLimitDisabled) { 744 return []Transaction{newAllowOnlyTransaction()}, nil 745 } 746 return nil, err 747 } 748 749 perIdentValuePerAccountBucketKey := newRegIdIdentValueBucketKey(FailedAuthorizationsForPausingPerDomainPerAccount, regId, orderIdent.Value) 750 txn, err := newResetTransaction(limit, perIdentValuePerAccountBucketKey) 751 if err != nil { 752 return nil, err 753 } 754 755 return []Transaction{txn}, nil 756 } 757 758 // LimitOverrideRequestsPerIPAddressTransaction returns a Transaction for the 759 // LimitOverrideRequestsPerIPAddress limit for the provided IP address. This 760 // limit is used to rate limit requests to the SFE override request endpoint. 761 func (builder *TransactionBuilder) LimitOverrideRequestsPerIPAddressTransaction(ip netip.Addr) (Transaction, error) { 762 bucketKey := newIPAddressBucketKey(LimitOverrideRequestsPerIPAddress, ip) 763 limit, err := builder.getLimit(LimitOverrideRequestsPerIPAddress, bucketKey) 764 if err != nil { 765 if errors.Is(err, errLimitDisabled) { 766 return newAllowOnlyTransaction(), nil 767 } 768 return Transaction{}, err 769 } 770 return newTransaction(limit, bucketKey, 1) 771 }