Skip to content

Commit 7791262

Browse files
authored
Rate limit deactivating pending authzs (#7835)
When a client deactivates a pending authorization, count that towards their FailedAuthorizationsPerDomainPerAccount and FailedAuthorizationsForPausingPerDomainPerAccount rate limits. This should help curb the few clients which constantly create new orders and authzs, deactivate those pending authzs preventing reuse of them or their orders, and then rinse and repeat. Fixes #7834
1 parent 53f3cb9 commit 7791262

File tree

2 files changed

+88
-9
lines changed

2 files changed

+88
-9
lines changed

ra/ra.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2425,7 +2425,7 @@ func (ra *RegistrationAuthorityImpl) DeactivateRegistration(ctx context.Context,
24252425

24262426
// DeactivateAuthorization deactivates a currently valid authorization
24272427
func (ra *RegistrationAuthorityImpl) DeactivateAuthorization(ctx context.Context, req *corepb.Authorization) (*emptypb.Empty, error) {
2428-
if req == nil || req.Id == "" || req.Status == "" {
2428+
if core.IsAnyNilOrZero(req, req.Id, req.Status, req.RegistrationID) {
24292429
return nil, errIncompleteGRPCRequest
24302430
}
24312431
authzID, err := strconv.ParseInt(req.Id, 10, 64)
@@ -2435,6 +2435,17 @@ func (ra *RegistrationAuthorityImpl) DeactivateAuthorization(ctx context.Context
24352435
if _, err := ra.SA.DeactivateAuthorization2(ctx, &sapb.AuthorizationID2{Id: authzID}); err != nil {
24362436
return nil, err
24372437
}
2438+
if req.Status == string(core.StatusPending) {
2439+
// Some clients deactivate pending authorizations without attempting them.
2440+
// We're not sure exactly when this happens but it's most likely due to
2441+
// internal errors in the client. From our perspective this uses storage
2442+
// resources similar to how failed authorizations do, so we increment the
2443+
// failed authorizations limit.
2444+
err = ra.countFailedValidations(ctx, req.RegistrationID, identifier.NewDNS(req.DnsName))
2445+
if err != nil {
2446+
return nil, fmt.Errorf("failed to update rate limits: %w", err)
2447+
}
2448+
}
24382449
return &emptypb.Empty{}, nil
24392450
}
24402451

ra/ra_test.go

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -865,13 +865,13 @@ func (msa *mockSAPaused) FinalizeAuthorization2(ctx context.Context, req *sapb.F
865865
}
866866

867867
func TestPerformValidation_FailedValidationsTriggerPauseIdentifiersRatelimit(t *testing.T) {
868-
if !strings.Contains(os.Getenv("BOULDER_CONFIG_DIR"), "test/config-next") {
869-
t.Skip()
870-
}
871-
872868
va, sa, ra, redisSrc, fc, cleanUp := initAuthorities(t)
873869
defer cleanUp()
874870

871+
if ra.limiter == nil {
872+
t.Skip("no redis limiter configured")
873+
}
874+
875875
features.Set(features.Config{AutomaticallyPauseZombieClients: true})
876876
defer features.Reset()
877877

@@ -988,13 +988,13 @@ func TestPerformValidation_FailedValidationsTriggerPauseIdentifiersRatelimit(t *
988988
}
989989

990990
func TestPerformValidation_FailedThenSuccessfulValidationResetsPauseIdentifiersRatelimit(t *testing.T) {
991-
if !strings.Contains(os.Getenv("BOULDER_CONFIG_DIR"), "test/config-next") {
992-
t.Skip()
993-
}
994-
995991
va, sa, ra, redisSrc, fc, cleanUp := initAuthorities(t)
996992
defer cleanUp()
997993

994+
if ra.limiter == nil {
995+
t.Skip("no redis limiter configured")
996+
}
997+
998998
features.Set(features.Config{AutomaticallyPauseZombieClients: true})
999999
defer features.Reset()
10001000

@@ -1778,6 +1778,74 @@ func TestDeactivateAuthorization(t *testing.T) {
17781778
test.AssertEquals(t, deact.Status, string(core.StatusDeactivated))
17791779
}
17801780

1781+
type mockSARecordingPauses struct {
1782+
sapb.StorageAuthorityClient
1783+
recv *sapb.PauseRequest
1784+
}
1785+
1786+
func (sa *mockSARecordingPauses) PauseIdentifiers(ctx context.Context, req *sapb.PauseRequest, _ ...grpc.CallOption) (*sapb.PauseIdentifiersResponse, error) {
1787+
sa.recv = req
1788+
return &sapb.PauseIdentifiersResponse{Paused: int64(len(req.Identifiers))}, nil
1789+
}
1790+
1791+
func (sa *mockSARecordingPauses) DeactivateAuthorization2(_ context.Context, _ *sapb.AuthorizationID2, _ ...grpc.CallOption) (*emptypb.Empty, error) {
1792+
return nil, nil
1793+
}
1794+
1795+
func TestDeactivateAuthorization_Pausing(t *testing.T) {
1796+
_, _, ra, _, _, cleanUp := initAuthorities(t)
1797+
defer cleanUp()
1798+
1799+
if ra.limiter == nil {
1800+
t.Skip("no redis limiter configured")
1801+
}
1802+
1803+
msa := mockSARecordingPauses{}
1804+
ra.SA = &msa
1805+
1806+
features.Set(features.Config{AutomaticallyPauseZombieClients: true})
1807+
defer features.Reset()
1808+
1809+
// Override the default ratelimits to only allow one failed validation.
1810+
txnBuilder, err := ratelimits.NewTransactionBuilder("testdata/one-failed-validation-before-pausing.yml", "")
1811+
test.AssertNotError(t, err, "making transaction composer")
1812+
ra.txnBuilder = txnBuilder
1813+
1814+
// The first deactivation of a pending authz should work and nothing should
1815+
// get paused.
1816+
_, err = ra.DeactivateAuthorization(ctx, &corepb.Authorization{
1817+
Id: "1",
1818+
RegistrationID: 1,
1819+
DnsName: "example.com",
1820+
Status: string(core.StatusPending),
1821+
})
1822+
test.AssertNotError(t, err, "mock deactivation should work")
1823+
test.AssertBoxedNil(t, msa.recv, "shouldn't be a pause request yet")
1824+
1825+
// Deactivating a valid authz shouldn't increment any limits or pause anything.
1826+
_, err = ra.DeactivateAuthorization(ctx, &corepb.Authorization{
1827+
Id: "2",
1828+
RegistrationID: 1,
1829+
DnsName: "example.com",
1830+
Status: string(core.StatusValid),
1831+
})
1832+
test.AssertNotError(t, err, "mock deactivation should work")
1833+
test.AssertBoxedNil(t, msa.recv, "deactivating valid authz should never pause")
1834+
1835+
// Deactivating a second pending authz should surpass the limit and result
1836+
// in a pause request.
1837+
_, err = ra.DeactivateAuthorization(ctx, &corepb.Authorization{
1838+
Id: "3",
1839+
RegistrationID: 1,
1840+
DnsName: "example.com",
1841+
Status: string(core.StatusPending),
1842+
})
1843+
test.AssertNotError(t, err, "mock deactivation should work")
1844+
test.AssertNotNil(t, msa.recv, "should have recorded a pause request")
1845+
test.AssertEquals(t, msa.recv.RegistrationID, int64(1))
1846+
test.AssertEquals(t, msa.recv.Identifiers[0].Value, "example.com")
1847+
}
1848+
17811849
func TestDeactivateRegistration(t *testing.T) {
17821850
_, _, ra, _, _, cleanUp := initAuthorities(t)
17831851
defer cleanUp()

0 commit comments

Comments
 (0)