diff --git a/errors/errors.go b/errors/errors.go index d7328b08dc9..6f83b2ce48d 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -179,26 +179,50 @@ func RateLimitError(retryAfter time.Duration, msg string, args ...interface{}) e } } -func DuplicateCertificateError(retryAfter time.Duration, msg string, args ...interface{}) error { +func RegistrationsPerIPAddressError(retryAfter time.Duration, msg string, args ...interface{}) error { return &BoulderError{ Type: RateLimit, - Detail: fmt.Sprintf(msg+": see https://letsencrypt.org/docs/duplicate-certificate-limit/", args...), + Detail: fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/#new-registrations-per-ip-address", args...), RetryAfter: retryAfter, } } -func FailedValidationError(retryAfter time.Duration, msg string, args ...interface{}) error { +func RegistrationsPerIPv6RangeError(retryAfter time.Duration, msg string, args ...interface{}) error { return &BoulderError{ Type: RateLimit, - Detail: fmt.Sprintf(msg+": see https://letsencrypt.org/docs/failed-validation-limit/", args...), + Detail: fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/#new-registrations-per-ipv6-range", args...), RetryAfter: retryAfter, } } -func RegistrationsPerIPError(retryAfter time.Duration, msg string, args ...interface{}) error { +func NewOrdersPerAccountError(retryAfter time.Duration, msg string, args ...interface{}) error { return &BoulderError{ Type: RateLimit, - Detail: fmt.Sprintf(msg+": see https://letsencrypt.org/docs/too-many-registrations-for-this-ip/", args...), + Detail: fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/#new-orders-per-account", args...), + RetryAfter: retryAfter, + } +} + +func CertificatesPerDomainError(retryAfter time.Duration, msg string, args ...interface{}) error { + return &BoulderError{ + Type: RateLimit, + Detail: fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/#new-certificates-per-registered-domain", args...), + RetryAfter: retryAfter, + } +} + +func CertificatesPerFQDNSetError(retryAfter time.Duration, msg string, args ...interface{}) error { + return &BoulderError{ + Type: RateLimit, + Detail: fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/#new-certificates-per-exact-set-of-hostnames", args...), + RetryAfter: retryAfter, + } +} + +func FailedAuthorizationsPerDomainPerAccountError(retryAfter time.Duration, msg string, args ...interface{}) error { + return &BoulderError{ + Type: RateLimit, + Detail: fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/#authorization-failures-per-hostname-per-account", args...), RetryAfter: retryAfter, } } diff --git a/ra/ra.go b/ra/ra.go index b368b9885c6..63ed2137616 100644 --- a/ra/ra.go +++ b/ra/ra.go @@ -404,7 +404,7 @@ func (ra *RegistrationAuthorityImpl) checkRegistrationIPLimit(ctx context.Contex threshold, overrideKey := limit.GetThreshold(ip.String(), noRegistrationID) if count.Count >= threshold { - return berrors.RegistrationsPerIPError(0, "too many registrations for this IP") + return berrors.RegistrationsPerIPAddressError(0, "too many registrations for this IP") } if overrideKey != "" { // We do not support overrides for the NewRegistrationsPerIPRange limit. @@ -598,7 +598,7 @@ func (ra *RegistrationAuthorityImpl) checkInvalidAuthorizationLimit(ctx context. threshold, overrideKey := limit.GetThreshold(noKey, regID) if count.Count >= threshold { ra.log.Infof("Rate limit exceeded, InvalidAuthorizationsByRegID, regID: %d", regID) - return berrors.FailedValidationError(0, "too many failed authorizations recently") + return berrors.FailedAuthorizationsPerDomainPerAccountError(0, "too many failed authorizations recently") } if overrideKey != "" { utilization := float64(count.Count) / float64(threshold) @@ -626,7 +626,7 @@ func (ra *RegistrationAuthorityImpl) checkNewOrdersPerAccountLimit(ctx context.C noKey := "" threshold, overrideKey := limit.GetThreshold(noKey, acctID) if count.Count >= threshold { - return berrors.RateLimitError(0, "too many new orders recently") + return berrors.NewOrdersPerAccountError(0, "too many new orders recently") } if overrideKey != "" { utilization := float64(count.Count+1) / float64(threshold) @@ -1473,12 +1473,12 @@ func (ra *RegistrationAuthorityImpl) checkCertificatesPerNameLimit(ctx context.C for _, name := range namesOutOfLimit { subErrors = append(subErrors, berrors.SubBoulderError{ Identifier: identifier.NewDNS(name), - BoulderError: berrors.RateLimitError(retryAfter, "too many certificates already issued. Retry after %s", retryString).(*berrors.BoulderError), + BoulderError: berrors.NewOrdersPerAccountError(retryAfter, "too many certificates already issued. Retry after %s", retryString).(*berrors.BoulderError), }) } - return berrors.RateLimitError(retryAfter, "too many certificates already issued for multiple names (%q and %d others). Retry after %s", namesOutOfLimit[0], len(namesOutOfLimit), retryString).(*berrors.BoulderError).WithSubErrors(subErrors) + return berrors.NewOrdersPerAccountError(retryAfter, "too many certificates already issued for multiple names (%q and %d others). Retry after %s", namesOutOfLimit[0], len(namesOutOfLimit), retryString).(*berrors.BoulderError).WithSubErrors(subErrors) } - return berrors.RateLimitError(retryAfter, "too many certificates already issued for %q. Retry after %s", namesOutOfLimit[0], retryString) + return berrors.NewOrdersPerAccountError(retryAfter, "too many certificates already issued for %q. Retry after %s", namesOutOfLimit[0], retryString) } return nil @@ -1535,7 +1535,7 @@ func (ra *RegistrationAuthorityImpl) checkCertificatesPerFQDNSetLimit(ctx contex } retryTime := prevIssuances.Timestamps[0].AsTime().Add(time.Duration(nsPerToken)) retryAfter := retryTime.Sub(now) - return berrors.DuplicateCertificateError( + return berrors.CertificatesPerFQDNSetError( retryAfter, "too many certificates (%d) already issued for this exact set of domains in the last %.0f hours: %s, retry after %s", threshold, limit.Window.Duration.Hours(), strings.Join(names, ","), retryTime.Format(time.RFC3339), diff --git a/ra/ra_test.go b/ra/ra_test.go index 6f7681b2422..ca045cb1d00 100644 --- a/ra/ra_test.go +++ b/ra/ra_test.go @@ -1013,7 +1013,7 @@ func TestEarlyOrderRateLimiting(t *testing.T) { test.AssertEquals(t, bErr.RetryAfter, rateLimitDuration) // The err should be the expected rate limit error - expected := "too many certificates already issued for \"early-ratelimit-example.com\". Retry after 2020-03-04T05:05:00Z: see https://letsencrypt.org/docs/rate-limits/" + expected := "too many certificates already issued for \"early-ratelimit-example.com\". Retry after 2020-03-04T05:05:00Z: see https://letsencrypt.org/docs/rate-limits/#new-orders-per-account" test.AssertEquals(t, bErr.Error(), expected) } @@ -1048,7 +1048,7 @@ func TestAuthzFailedRateLimitingNewOrder(t *testing.T) { err := ra.checkInvalidAuthorizationLimits(ctx, Registration.Id, []string{"charlie.brown.com", "all.i.do.is.lose.com"}, limit) test.AssertError(t, err, "checkInvalidAuthorizationLimits did not encounter expected rate limit error") - test.AssertEquals(t, err.Error(), "too many failed authorizations recently: see https://letsencrypt.org/docs/failed-validation-limit/") + test.AssertEquals(t, err.Error(), "too many failed authorizations recently: see https://letsencrypt.org/docs/rate-limits/#authorization-failures-per-hostname-per-account") } type mockSAWithNameCounts struct { @@ -1117,7 +1117,7 @@ func TestCheckCertificatesPerNameLimit(t *testing.T) { // should contain 0 entries with labels matching it. test.AssertMetricWithLabelsEquals(t, ra.rlOverrideUsageGauge, prometheus.Labels{"limit": ratelimit.CertificatesPerName, "override_key": "example.com"}, 0) // Verify it has no sub errors as there is only one bad name - test.AssertEquals(t, err.Error(), "too many certificates already issued for \"example.com\". Retry after 1970-01-01T23:00:00Z: see https://letsencrypt.org/docs/rate-limits/") + test.AssertEquals(t, err.Error(), "too many certificates already issued for \"example.com\". Retry after 1970-01-01T23:00:00Z: see https://letsencrypt.org/docs/rate-limits/#new-orders-per-account") var bErr *berrors.BoulderError test.AssertErrorWraps(t, err, &bErr) test.AssertEquals(t, len(bErr.SubErrors), 0) @@ -1130,7 +1130,7 @@ func TestCheckCertificatesPerNameLimit(t *testing.T) { test.AssertError(t, err, "incorrectly failed to rate limit example.com, other-example.com") test.AssertErrorIs(t, err, berrors.RateLimit) // Verify it has two sub errors as there are two bad names - test.AssertEquals(t, err.Error(), "too many certificates already issued for multiple names (\"example.com\" and 2 others). Retry after 1970-01-01T23:00:00Z: see https://letsencrypt.org/docs/rate-limits/") + test.AssertEquals(t, err.Error(), "too many certificates already issued for multiple names (\"example.com\" and 2 others). Retry after 1970-01-01T23:00:00Z: see https://letsencrypt.org/docs/rate-limits/#new-orders-per-account") test.AssertErrorWraps(t, err, &bErr) test.AssertEquals(t, len(bErr.SubErrors), 2) @@ -1241,7 +1241,7 @@ func TestCheckExactCertificateLimit(t *testing.T) { Name: "FQDN set issuances above limit NS", Domain: "over.example.com", ExpectedErr: fmt.Errorf( - "too many certificates (3) already issued for this exact set of domains in the last 24 hours: over.example.com, retry after %s: see https://letsencrypt.org/docs/duplicate-certificate-limit/", + "too many certificates (3) already issued for this exact set of domains in the last 24 hours: over.example.com, retry after %s: see https://letsencrypt.org/docs/rate-limits/#new-certificates-per-exact-set-of-hostnames", expectRetryAfterNS, ), }, @@ -1249,7 +1249,7 @@ func TestCheckExactCertificateLimit(t *testing.T) { Name: "FQDN set issuances above limit", Domain: "over.example.com", ExpectedErr: fmt.Errorf( - "too many certificates (3) already issued for this exact set of domains in the last 24 hours: over.example.com, retry after %s: see https://letsencrypt.org/docs/duplicate-certificate-limit/", + "too many certificates (3) already issued for this exact set of domains in the last 24 hours: over.example.com, retry after %s: see https://letsencrypt.org/docs/rate-limits/#new-certificates-per-exact-set-of-hostnames", expectRetryAfter, ), }, @@ -2154,7 +2154,7 @@ func TestNewOrderCheckFailedAuthorizationsFirst(t *testing.T) { }) test.AssertError(t, err, "expected error for domain with too many failures") - test.AssertEquals(t, err.Error(), "too many failed authorizations recently: see https://letsencrypt.org/docs/failed-validation-limit/") + test.AssertEquals(t, err.Error(), "too many failed authorizations recently: see https://letsencrypt.org/docs/rate-limits/#authorization-failures-per-hostname-per-account") } // mockSAWithAuthzs has a GetAuthorizations2 method that returns the protobuf diff --git a/ratelimits/limiter.go b/ratelimits/limiter.go index f4eca6c8db5..4b347e0be0a 100644 --- a/ratelimits/limiter.go +++ b/ratelimits/limiter.go @@ -112,7 +112,7 @@ func (d *Decision) Result(now time.Time) error { switch d.transaction.limit.name { case NewRegistrationsPerIPAddress: - return berrors.RegistrationsPerIPError( + return berrors.RegistrationsPerIPAddressError( retryAfter, "too many new registrations (%d) from this IP address in the last %s, retry after %s", d.transaction.limit.Burst, @@ -121,15 +121,15 @@ func (d *Decision) Result(now time.Time) error { ) case NewRegistrationsPerIPv6Range: - return berrors.RateLimitError( + return berrors.RegistrationsPerIPv6RangeError( retryAfter, - "too many new registrations (%d) from this /48 block of IPv6 addresses in the last %s, retry after %s", + "too many new registrations (%d) from this /48 subnet of IPv6 addresses in the last %s, retry after %s", d.transaction.limit.Burst, d.transaction.limit.Period.Duration, retryAfterTs, ) case NewOrdersPerAccount: - return berrors.RateLimitError( + return berrors.NewOrdersPerAccountError( retryAfter, "too many new orders (%d) from this account in the last %s, retry after %s", d.transaction.limit.Burst, @@ -144,7 +144,7 @@ func (d *Decision) Result(now time.Time) error { return berrors.InternalServerError("unrecognized bucket key while generating error") } domain := d.transaction.bucketKey[idx+1:] - return berrors.FailedValidationError( + return berrors.FailedAuthorizationsPerDomainPerAccountError( retryAfter, "too many failed authorizations (%d) for %q in the last %s, retry after %s", d.transaction.limit.Burst, @@ -160,7 +160,7 @@ func (d *Decision) Result(now time.Time) error { return berrors.InternalServerError("unrecognized bucket key while generating error") } domain := d.transaction.bucketKey[idx+1:] - return berrors.RateLimitError( + return berrors.CertificatesPerDomainError( retryAfter, "too many certificates (%d) already issued for %q in the last %s, retry after %s", d.transaction.limit.Burst, @@ -170,7 +170,7 @@ func (d *Decision) Result(now time.Time) error { ) case CertificatesPerFQDNSet: - return berrors.DuplicateCertificateError( + return berrors.CertificatesPerFQDNSetError( retryAfter, "too many certificates (%d) already issued for this exact set of domains in the last %s, retry after %s", d.transaction.limit.Burst, diff --git a/ratelimits/limiter_test.go b/ratelimits/limiter_test.go index 097495fe4ba..e8761bcaeeb 100644 --- a/ratelimits/limiter_test.go +++ b/ratelimits/limiter_test.go @@ -489,7 +489,7 @@ func TestRateLimitError(t *testing.T) { }, }, }, - expectedErr: "too many new registrations (10) from this IP address in the last 1h0m0s, retry after 1970-01-01 00:00:05 UTC", + 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", expectedErrType: berrors.RateLimit, }, { @@ -505,7 +505,23 @@ func TestRateLimitError(t *testing.T) { }, }, }, - expectedErr: "too many new registrations (5) from this /48 block of IPv6 addresses in the last 1h0m0s, retry after 1970-01-01 00:00:10 UTC", + 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", + expectedErrType: berrors.RateLimit, + }, + { + name: "NewOrdersPerAccount limit reached", + decision: &Decision{ + allowed: false, + retryIn: 10 * time.Second, + transaction: Transaction{ + limit: limit{ + name: NewOrdersPerAccount, + Burst: 2, + Period: config.Duration{Duration: time.Hour}, + }, + }, + }, + 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", expectedErrType: berrors.RateLimit, }, { @@ -522,7 +538,7 @@ func TestRateLimitError(t *testing.T) { bucketKey: "4:12345:example.com", }, }, - expectedErr: "too many failed authorizations (7) for \"example.com\" in the last 1h0m0s, retry after 1970-01-01 00:00:15 UTC", + 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", expectedErrType: berrors.RateLimit, }, { @@ -539,7 +555,7 @@ func TestRateLimitError(t *testing.T) { bucketKey: "5:example.org", }, }, - expectedErr: "too many certificates (3) already issued for \"example.org\" in the last 1h0m0s, retry after 1970-01-01 00:00:20 UTC", + 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", expectedErrType: berrors.RateLimit, }, { @@ -556,7 +572,7 @@ func TestRateLimitError(t *testing.T) { bucketKey: "6:12345678:example.net", }, }, - expectedErr: "too many certificates (3) already issued for \"example.net\" in the last 1h0m0s, retry after 1970-01-01 00:00:20 UTC", + 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", expectedErrType: berrors.RateLimit, }, { @@ -583,7 +599,7 @@ func TestRateLimitError(t *testing.T) { test.AssertNotError(t, err, "expected no error") } else { test.AssertError(t, err, "expected an error") - test.AssertContains(t, err.Error(), tc.expectedErr) + test.AssertEquals(t, err.Error(), tc.expectedErr) test.AssertErrorIs(t, err, tc.expectedErrType) } })