ratelimits: Fix transaction building for Failed Authorizations Limit (#7344)
- Update the failed authorizations limit to use 'enum:regId:domain' for transactions while maintaining 'enum:regId' for overrides. - Modify the failed authorizations transaction builder to generate a transaction for each order name. - Rename the `FailedAuthorizationsPerAccount` enum to `FailedAuthorizationsPerDomainPerAccount` to align with its corrected implementation. This change is possible because the limit isn't yet deployed in staging or production. Blocks #7346 Part of #5545
This commit is contained in:
parent
51231a3942
commit
529157ce56
|
@ -235,39 +235,84 @@ func (builder *TransactionBuilder) OrdersPerAccountTransaction(regId int64) (Tra
|
||||||
return newTransaction(limit, bucketKey, 1)
|
return newTransaction(limit, bucketKey, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FailedAuthorizationsPerAccountCheckOnlyTransaction returns a check-only
|
// FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions returns a slice
|
||||||
// Transaction for the provided ACME registration Id for the
|
// of Transactions for the provided order domain names. An error is returned if
|
||||||
// FailedAuthorizationsPerAccount limit.
|
// any of the order domain names are invalid. This method should be used for
|
||||||
func (builder *TransactionBuilder) FailedAuthorizationsPerAccountCheckOnlyTransaction(regId int64) (Transaction, error) {
|
// checking capacity, before allowing more authorizations to be created.
|
||||||
bucketKey, err := newRegIdBucketKey(FailedAuthorizationsPerAccount, regId)
|
func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions(regId int64, orderDomains []string, maxNames int) ([]Transaction, error) {
|
||||||
if err != nil {
|
if len(orderDomains) > maxNames {
|
||||||
return Transaction{}, err
|
return nil, fmt.Errorf("order contains more than %d DNS names", maxNames)
|
||||||
}
|
}
|
||||||
limit, err := builder.getLimit(FailedAuthorizationsPerAccount, bucketKey)
|
|
||||||
|
// FailedAuthorizationsPerDomainPerAccount limit uses the 'enum:regId'
|
||||||
|
// bucket key format for overrides.
|
||||||
|
perAccountBucketKey, err := newRegIdBucketKey(FailedAuthorizationsPerDomainPerAccount, regId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, errLimitDisabled) {
|
return nil, err
|
||||||
return newAllowOnlyTransaction()
|
}
|
||||||
|
limit, err := builder.getLimit(FailedAuthorizationsPerDomainPerAccount, perAccountBucketKey)
|
||||||
|
if err != nil && !errors.Is(err, errLimitDisabled) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var txns []Transaction
|
||||||
|
for _, name := range DomainsForRateLimiting(orderDomains) {
|
||||||
|
// FailedAuthorizationsPerDomainPerAccount limit uses the
|
||||||
|
// 'enum:regId:domain' bucket key format for transactions.
|
||||||
|
perDomainPerAccountBucketKey, err := newRegIdDomainBucketKey(FailedAuthorizationsPerDomainPerAccount, regId, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return Transaction{}, err
|
|
||||||
|
// Add a check-only transaction for each per domain per account bucket.
|
||||||
|
// The cost is 0, as we are only checking that the account and domain
|
||||||
|
// pair aren't already over the limit.
|
||||||
|
txn, err := newCheckOnlyTransaction(limit, perDomainPerAccountBucketKey, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
txns = append(txns, txn)
|
||||||
}
|
}
|
||||||
return newCheckOnlyTransaction(limit, bucketKey, 1)
|
return txns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FailedAuthorizationsPerAccountTransaction returns a Transaction for the
|
// FailedAuthorizationsPerDomainPerAccountSpendOnlyTransactions returns a slice
|
||||||
// FailedAuthorizationsPerAccount limit for the provided ACME registration Id.
|
// of Transactions for the provided order domain names. An error is returned if
|
||||||
func (builder *TransactionBuilder) FailedAuthorizationsPerAccountTransaction(regId int64) (Transaction, error) {
|
// any of the order domain names are invalid. This method should be used for
|
||||||
bucketKey, err := newRegIdBucketKey(FailedAuthorizationsPerAccount, regId)
|
// spending capacity, as a result of a failed authorization.
|
||||||
if err != nil {
|
func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountSpendOnlyTransactions(regId int64, orderDomains []string, maxNames int) ([]Transaction, error) {
|
||||||
return Transaction{}, err
|
if len(orderDomains) > maxNames {
|
||||||
|
return nil, fmt.Errorf("order contains more than %d DNS names", maxNames)
|
||||||
}
|
}
|
||||||
limit, err := builder.getLimit(FailedAuthorizationsPerAccount, bucketKey)
|
|
||||||
|
// FailedAuthorizationsPerDomainPerAccount limit uses the 'enum:regId'
|
||||||
|
// bucket key format for overrides.
|
||||||
|
perAccountBucketKey, err := newRegIdBucketKey(FailedAuthorizationsPerDomainPerAccount, regId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, errLimitDisabled) {
|
return nil, err
|
||||||
return newAllowOnlyTransaction()
|
}
|
||||||
|
limit, err := builder.getLimit(FailedAuthorizationsPerDomainPerAccount, perAccountBucketKey)
|
||||||
|
if err != nil && !errors.Is(err, errLimitDisabled) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var txns []Transaction
|
||||||
|
for _, name := range DomainsForRateLimiting(orderDomains) {
|
||||||
|
// FailedAuthorizationsPerDomainPerAccount limit uses the
|
||||||
|
// 'enum:regId:domain' bucket key format for transactions.
|
||||||
|
perDomainPerAccountBucketKey, err := newRegIdDomainBucketKey(FailedAuthorizationsPerDomainPerAccount, regId, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return Transaction{}, err
|
|
||||||
|
// Add an allow-only transaction for each per domain per account bucket.
|
||||||
|
txn, err := newSpendOnlyTransaction(limit, perDomainPerAccountBucketKey, 1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
txns = append(txns, txn)
|
||||||
}
|
}
|
||||||
return newTransaction(limit, bucketKey, 1)
|
return txns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CertificatesPerDomainTransactions returns a slice of Transactions for the
|
// CertificatesPerDomainTransactions returns a slice of Transactions for the
|
||||||
|
|
|
@ -40,11 +40,14 @@ const (
|
||||||
// NewOrdersPerAccount uses bucket key 'enum:regId'.
|
// NewOrdersPerAccount uses bucket key 'enum:regId'.
|
||||||
NewOrdersPerAccount
|
NewOrdersPerAccount
|
||||||
|
|
||||||
// FailedAuthorizationsPerAccount uses bucket key 'enum:regId', where regId
|
// FailedAuthorizationsPerDomainPerAccount uses two different bucket keys
|
||||||
// is the ACME registration Id of the account. Cost MUST be consumed from
|
// depending on the context:
|
||||||
// this bucket only when the authorization is considered "failed". It SHOULD
|
// - When referenced in an overrides file: uses bucket key 'enum:regId',
|
||||||
// be checked before new authorizations are created.
|
// where regId is the ACME registration Id of the account.
|
||||||
FailedAuthorizationsPerAccount
|
// - When referenced in a transaction: uses bucket key 'enum:regId:domain',
|
||||||
|
// where regId is the ACME registration Id of the account and domain is a
|
||||||
|
// domain name in the certificate.
|
||||||
|
FailedAuthorizationsPerDomainPerAccount
|
||||||
|
|
||||||
// CertificatesPerDomain uses bucket key 'enum:domain', where domain is a
|
// CertificatesPerDomain uses bucket key 'enum:domain', where domain is a
|
||||||
// domain name in the certificate.
|
// domain name in the certificate.
|
||||||
|
@ -96,14 +99,14 @@ func (n Name) EnumString() string {
|
||||||
|
|
||||||
// nameToString is a map of Name values to string names.
|
// nameToString is a map of Name values to string names.
|
||||||
var nameToString = map[Name]string{
|
var nameToString = map[Name]string{
|
||||||
Unknown: "Unknown",
|
Unknown: "Unknown",
|
||||||
NewRegistrationsPerIPAddress: "NewRegistrationsPerIPAddress",
|
NewRegistrationsPerIPAddress: "NewRegistrationsPerIPAddress",
|
||||||
NewRegistrationsPerIPv6Range: "NewRegistrationsPerIPv6Range",
|
NewRegistrationsPerIPv6Range: "NewRegistrationsPerIPv6Range",
|
||||||
NewOrdersPerAccount: "NewOrdersPerAccount",
|
NewOrdersPerAccount: "NewOrdersPerAccount",
|
||||||
FailedAuthorizationsPerAccount: "FailedAuthorizationsPerAccount",
|
FailedAuthorizationsPerDomainPerAccount: "FailedAuthorizationsPerDomainPerAccount",
|
||||||
CertificatesPerDomain: "CertificatesPerDomain",
|
CertificatesPerDomain: "CertificatesPerDomain",
|
||||||
CertificatesPerDomainPerAccount: "CertificatesPerDomainPerAccount",
|
CertificatesPerDomainPerAccount: "CertificatesPerDomainPerAccount",
|
||||||
CertificatesPerFQDNSet: "CertificatesPerFQDNSet",
|
CertificatesPerFQDNSet: "CertificatesPerFQDNSet",
|
||||||
}
|
}
|
||||||
|
|
||||||
// validIPAddress validates that the provided string is a valid IP address.
|
// validIPAddress validates that the provided string is a valid IP address.
|
||||||
|
@ -202,10 +205,19 @@ func validateIdForName(name Name, id string) error {
|
||||||
// 'enum:ipv6rangeCIDR'
|
// 'enum:ipv6rangeCIDR'
|
||||||
return validIPv6RangeCIDR(id)
|
return validIPv6RangeCIDR(id)
|
||||||
|
|
||||||
case NewOrdersPerAccount, FailedAuthorizationsPerAccount:
|
case NewOrdersPerAccount:
|
||||||
// 'enum:regId'
|
// 'enum:regId'
|
||||||
return validateRegId(id)
|
return validateRegId(id)
|
||||||
|
|
||||||
|
case FailedAuthorizationsPerDomainPerAccount:
|
||||||
|
// 'enum:regId:domain' for transaction
|
||||||
|
err := validateRegIdDomain(id)
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// 'enum:regId' for overrides
|
||||||
|
return validateRegId(id)
|
||||||
|
|
||||||
case CertificatesPerDomainPerAccount:
|
case CertificatesPerDomainPerAccount:
|
||||||
// 'enum:regId:domain' for transaction
|
// 'enum:regId:domain' for transaction
|
||||||
err := validateRegIdDomain(id)
|
err := validateRegIdDomain(id)
|
||||||
|
|
|
@ -8,3 +8,9 @@
|
||||||
count: 50
|
count: 50
|
||||||
period: 2s
|
period: 2s
|
||||||
ids: [2001:0db8:0000::/48]
|
ids: [2001:0db8:0000::/48]
|
||||||
|
- FailedAuthorizationsPerDomainPerAccount:
|
||||||
|
burst: 60
|
||||||
|
count: 60
|
||||||
|
period: 3s
|
||||||
|
ids: [1234, 5678]
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ CertificatesPerDomain:
|
||||||
count: 2
|
count: 2
|
||||||
burst: 2
|
burst: 2
|
||||||
period: 2160h
|
period: 2160h
|
||||||
FailedAuthorizationsPerAccount:
|
FailedAuthorizationsPerDomainPerAccount:
|
||||||
count: 3
|
count: 3
|
||||||
burst: 3
|
burst: 3
|
||||||
period: 5m
|
period: 5m
|
||||||
|
|
10
wfe2/wfe.go
10
wfe2/wfe.go
|
@ -2064,19 +2064,19 @@ func (wfe *WebFrontEndImpl) newNewOrderLimitTransactions(regId int64, names []st
|
||||||
}
|
}
|
||||||
transactions = append(transactions, txn)
|
transactions = append(transactions, txn)
|
||||||
|
|
||||||
txn, err = wfe.txnBuilder.FailedAuthorizationsPerAccountCheckOnlyTransaction(regId)
|
failedAuthzTxns, err := wfe.txnBuilder.FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions(regId, names, wfe.maxNames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logTxnErr(err, ratelimits.FailedAuthorizationsPerAccount)
|
logTxnErr(err, ratelimits.FailedAuthorizationsPerDomainPerAccount)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
transactions = append(transactions, txn)
|
transactions = append(transactions, failedAuthzTxns...)
|
||||||
|
|
||||||
txns, err := wfe.txnBuilder.CertificatesPerDomainTransactions(regId, names, wfe.maxNames)
|
certsPerDomainTxns, err := wfe.txnBuilder.CertificatesPerDomainTransactions(regId, names, wfe.maxNames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logTxnErr(err, ratelimits.CertificatesPerDomain)
|
logTxnErr(err, ratelimits.CertificatesPerDomain)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
transactions = append(transactions, txns...)
|
transactions = append(transactions, certsPerDomainTxns...)
|
||||||
|
|
||||||
txn, err = wfe.txnBuilder.CertificatesPerFQDNSetTransaction(names)
|
txn, err = wfe.txnBuilder.CertificatesPerFQDNSetTransaction(names)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue