Separate statically- and dynamically-configured rate limiters
This commit is contained in:
parent
13e346d4eb
commit
9628f147f1
|
@ -108,6 +108,7 @@ import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||||
import org.whispersystems.textsecuregcm.filters.ContentLengthFilter;
|
import org.whispersystems.textsecuregcm.filters.ContentLengthFilter;
|
||||||
import org.whispersystems.textsecuregcm.filters.RemoteDeprecationFilter;
|
import org.whispersystems.textsecuregcm.filters.RemoteDeprecationFilter;
|
||||||
import org.whispersystems.textsecuregcm.filters.TimestampResponseFilter;
|
import org.whispersystems.textsecuregcm.filters.TimestampResponseFilter;
|
||||||
|
import org.whispersystems.textsecuregcm.limits.DynamicRateLimiters;
|
||||||
import org.whispersystems.textsecuregcm.limits.PreKeyRateLimiter;
|
import org.whispersystems.textsecuregcm.limits.PreKeyRateLimiter;
|
||||||
import org.whispersystems.textsecuregcm.limits.PushChallengeManager;
|
import org.whispersystems.textsecuregcm.limits.PushChallengeManager;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimitChallengeManager;
|
import org.whispersystems.textsecuregcm.limits.RateLimitChallengeManager;
|
||||||
|
@ -472,7 +473,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
PubSubManager pubSubManager = new PubSubManager(pubsubClient, dispatchManager);
|
PubSubManager pubSubManager = new PubSubManager(pubsubClient, dispatchManager);
|
||||||
APNSender apnSender = new APNSender(apnSenderExecutor, accountsManager, config.getApnConfiguration());
|
APNSender apnSender = new APNSender(apnSenderExecutor, accountsManager, config.getApnConfiguration());
|
||||||
GCMSender gcmSender = new GCMSender(gcmSenderExecutor, accountsManager, config.getGcmConfiguration().getApiKey());
|
GCMSender gcmSender = new GCMSender(gcmSenderExecutor, accountsManager, config.getGcmConfiguration().getApiKey());
|
||||||
RateLimiters rateLimiters = new RateLimiters(config.getLimitsConfiguration(), dynamicConfigurationManager, rateLimitersCluster);
|
RateLimiters rateLimiters = new RateLimiters(config.getLimitsConfiguration(), rateLimitersCluster);
|
||||||
|
DynamicRateLimiters dynamicRateLimiters = new DynamicRateLimiters(rateLimitersCluster, dynamicConfigurationManager);
|
||||||
ProvisioningManager provisioningManager = new ProvisioningManager(pubSubManager);
|
ProvisioningManager provisioningManager = new ProvisioningManager(pubSubManager);
|
||||||
IssuedReceiptsManager issuedReceiptsManager = new IssuedReceiptsManager(
|
IssuedReceiptsManager issuedReceiptsManager = new IssuedReceiptsManager(
|
||||||
config.getDynamoDbTables().getIssuedReceipts().getTableName(),
|
config.getDynamoDbTables().getIssuedReceipts().getTableName(),
|
||||||
|
@ -492,8 +494,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
|
|
||||||
RateLimitResetMetricsManager rateLimitResetMetricsManager = new RateLimitResetMetricsManager(metricsCluster, Metrics.globalRegistry);
|
RateLimitResetMetricsManager rateLimitResetMetricsManager = new RateLimitResetMetricsManager(metricsCluster, Metrics.globalRegistry);
|
||||||
|
|
||||||
UnsealedSenderRateLimiter unsealedSenderRateLimiter = new UnsealedSenderRateLimiter(rateLimiters, rateLimitersCluster, dynamicConfigurationManager, rateLimitResetMetricsManager);
|
UnsealedSenderRateLimiter unsealedSenderRateLimiter = new UnsealedSenderRateLimiter(dynamicRateLimiters, rateLimitersCluster, dynamicConfigurationManager, rateLimitResetMetricsManager);
|
||||||
PreKeyRateLimiter preKeyRateLimiter = new PreKeyRateLimiter(rateLimiters, dynamicConfigurationManager, rateLimitResetMetricsManager);
|
PreKeyRateLimiter preKeyRateLimiter = new PreKeyRateLimiter(dynamicRateLimiters, dynamicConfigurationManager, rateLimitResetMetricsManager);
|
||||||
|
|
||||||
ApnFallbackManager apnFallbackManager = new ApnFallbackManager(pushSchedulerCluster, apnSender, accountsManager);
|
ApnFallbackManager apnFallbackManager = new ApnFallbackManager(pushSchedulerCluster, apnSender, accountsManager);
|
||||||
TwilioSmsSender twilioSmsSender = new TwilioSmsSender(config.getTwilioConfiguration(), dynamicConfigurationManager);
|
TwilioSmsSender twilioSmsSender = new TwilioSmsSender(config.getTwilioConfiguration(), dynamicConfigurationManager);
|
||||||
|
@ -510,7 +512,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
TransitionalRecaptchaClient transitionalRecaptchaClient = new TransitionalRecaptchaClient(legacyRecaptchaClient, enterpriseRecaptchaClient);
|
TransitionalRecaptchaClient transitionalRecaptchaClient = new TransitionalRecaptchaClient(legacyRecaptchaClient, enterpriseRecaptchaClient);
|
||||||
PushChallengeManager pushChallengeManager = new PushChallengeManager(apnSender, gcmSender, pushChallengeDynamoDb);
|
PushChallengeManager pushChallengeManager = new PushChallengeManager(apnSender, gcmSender, pushChallengeDynamoDb);
|
||||||
RateLimitChallengeManager rateLimitChallengeManager = new RateLimitChallengeManager(pushChallengeManager,
|
RateLimitChallengeManager rateLimitChallengeManager = new RateLimitChallengeManager(pushChallengeManager,
|
||||||
transitionalRecaptchaClient, preKeyRateLimiter, unsealedSenderRateLimiter, rateLimiters,
|
transitionalRecaptchaClient, preKeyRateLimiter, unsealedSenderRateLimiter, dynamicRateLimiters,
|
||||||
dynamicConfigurationManager);
|
dynamicConfigurationManager);
|
||||||
|
|
||||||
MessagePersister messagePersister = new MessagePersister(messagesCache, messagesManager, accountsManager, dynamicConfigurationManager, Duration.ofMinutes(config.getMessageCacheConfiguration().getPersistDelayMinutes()));
|
MessagePersister messagePersister = new MessagePersister(messagesCache, messagesManager, accountsManager, dynamicConfigurationManager, Duration.ofMinutes(config.getMessageCacheConfiguration().getPersistDelayMinutes()));
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2013-2021 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.limits;
|
||||||
|
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration.CardinalityRateLimitConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration.RateLimitConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
public class DynamicRateLimiters {
|
||||||
|
|
||||||
|
private final FaultTolerantRedisCluster cacheCluster;
|
||||||
|
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
||||||
|
|
||||||
|
private final AtomicReference<CardinalityRateLimiter> unsealedSenderCardinalityLimiter;
|
||||||
|
private final AtomicReference<RateLimiter> unsealedIpLimiter;
|
||||||
|
private final AtomicReference<RateLimiter> rateLimitResetLimiter;
|
||||||
|
private final AtomicReference<RateLimiter> recaptchaChallengeAttemptLimiter;
|
||||||
|
private final AtomicReference<RateLimiter> recaptchaChallengeSuccessLimiter;
|
||||||
|
private final AtomicReference<RateLimiter> pushChallengeAttemptLimiter;
|
||||||
|
private final AtomicReference<RateLimiter> pushChallengeSuccessLimiter;
|
||||||
|
private final AtomicReference<RateLimiter> dailyPreKeysLimiter;
|
||||||
|
|
||||||
|
public DynamicRateLimiters(final FaultTolerantRedisCluster rateLimitCluster,
|
||||||
|
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
|
||||||
|
|
||||||
|
this.cacheCluster = rateLimitCluster;
|
||||||
|
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
||||||
|
|
||||||
|
this.dailyPreKeysLimiter = new AtomicReference<>(
|
||||||
|
createDailyPreKeysLimiter(this.cacheCluster,
|
||||||
|
this.dynamicConfigurationManager.getConfiguration().getLimits().getDailyPreKeys()));
|
||||||
|
|
||||||
|
this.unsealedSenderCardinalityLimiter = new AtomicReference<>(createUnsealedSenderCardinalityLimiter(
|
||||||
|
this.cacheCluster,
|
||||||
|
this.dynamicConfigurationManager.getConfiguration().getLimits().getUnsealedSenderNumber()));
|
||||||
|
|
||||||
|
this.unsealedIpLimiter = new AtomicReference<>(
|
||||||
|
createUnsealedIpLimiter(this.cacheCluster,
|
||||||
|
this.dynamicConfigurationManager.getConfiguration().getLimits().getUnsealedSenderIp()));
|
||||||
|
|
||||||
|
this.rateLimitResetLimiter = new AtomicReference<>(
|
||||||
|
createRateLimitResetLimiter(this.cacheCluster,
|
||||||
|
this.dynamicConfigurationManager.getConfiguration().getLimits().getRateLimitReset()));
|
||||||
|
|
||||||
|
this.recaptchaChallengeAttemptLimiter = new AtomicReference<>(createRecaptchaChallengeAttemptLimiter(
|
||||||
|
this.cacheCluster,
|
||||||
|
this.dynamicConfigurationManager.getConfiguration().getLimits().getRecaptchaChallengeAttempt()));
|
||||||
|
|
||||||
|
this.recaptchaChallengeSuccessLimiter = new AtomicReference<>(createRecaptchaChallengeSuccessLimiter(
|
||||||
|
this.cacheCluster,
|
||||||
|
this.dynamicConfigurationManager.getConfiguration().getLimits().getRecaptchaChallengeSuccess()));
|
||||||
|
|
||||||
|
this.pushChallengeAttemptLimiter = new AtomicReference<>(createPushChallengeAttemptLimiter(this.cacheCluster,
|
||||||
|
this.dynamicConfigurationManager.getConfiguration().getLimits().getPushChallengeAttempt()));
|
||||||
|
|
||||||
|
this.pushChallengeSuccessLimiter = new AtomicReference<>(createPushChallengeSuccessLimiter(this.cacheCluster,
|
||||||
|
this.dynamicConfigurationManager.getConfiguration().getLimits().getPushChallengeSuccess()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardinalityRateLimiter getUnsealedSenderCardinalityLimiter() {
|
||||||
|
CardinalityRateLimitConfiguration currentConfiguration = dynamicConfigurationManager.getConfiguration().getLimits()
|
||||||
|
.getUnsealedSenderNumber();
|
||||||
|
|
||||||
|
return this.unsealedSenderCardinalityLimiter.updateAndGet(rateLimiter -> {
|
||||||
|
if (rateLimiter.hasConfiguration(currentConfiguration)) {
|
||||||
|
return rateLimiter;
|
||||||
|
} else {
|
||||||
|
return createUnsealedSenderCardinalityLimiter(cacheCluster, currentConfiguration);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateLimiter getUnsealedIpLimiter() {
|
||||||
|
return updateAndGetRateLimiter(
|
||||||
|
unsealedIpLimiter,
|
||||||
|
dynamicConfigurationManager.getConfiguration().getLimits().getUnsealedSenderIp(),
|
||||||
|
this::createUnsealedIpLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateLimiter getRateLimitResetLimiter() {
|
||||||
|
return updateAndGetRateLimiter(
|
||||||
|
rateLimitResetLimiter,
|
||||||
|
dynamicConfigurationManager.getConfiguration().getLimits().getRateLimitReset(),
|
||||||
|
this::createRateLimitResetLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateLimiter getRecaptchaChallengeAttemptLimiter() {
|
||||||
|
return updateAndGetRateLimiter(
|
||||||
|
recaptchaChallengeAttemptLimiter,
|
||||||
|
dynamicConfigurationManager.getConfiguration().getLimits().getRecaptchaChallengeAttempt(),
|
||||||
|
this::createRecaptchaChallengeAttemptLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateLimiter getRecaptchaChallengeSuccessLimiter() {
|
||||||
|
return updateAndGetRateLimiter(
|
||||||
|
recaptchaChallengeSuccessLimiter,
|
||||||
|
dynamicConfigurationManager.getConfiguration().getLimits().getRecaptchaChallengeSuccess(),
|
||||||
|
this::createRecaptchaChallengeSuccessLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateLimiter getPushChallengeAttemptLimiter() {
|
||||||
|
return updateAndGetRateLimiter(
|
||||||
|
pushChallengeAttemptLimiter,
|
||||||
|
dynamicConfigurationManager.getConfiguration().getLimits().getPushChallengeAttempt(),
|
||||||
|
this::createPushChallengeAttemptLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateLimiter getPushChallengeSuccessLimiter() {
|
||||||
|
return updateAndGetRateLimiter(
|
||||||
|
pushChallengeSuccessLimiter,
|
||||||
|
dynamicConfigurationManager.getConfiguration().getLimits().getPushChallengeSuccess(),
|
||||||
|
this::createPushChallengeSuccessLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateLimiter getDailyPreKeysLimiter() {
|
||||||
|
return updateAndGetRateLimiter(
|
||||||
|
dailyPreKeysLimiter,
|
||||||
|
dynamicConfigurationManager.getConfiguration().getLimits().getDailyPreKeys(),
|
||||||
|
this::createDailyPreKeysLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RateLimiter updateAndGetRateLimiter(final AtomicReference<RateLimiter> rateLimiter,
|
||||||
|
RateLimitConfiguration currentConfiguration,
|
||||||
|
BiFunction<FaultTolerantRedisCluster, RateLimitConfiguration, RateLimiter> rateLimitFactory) {
|
||||||
|
|
||||||
|
return rateLimiter.updateAndGet(limiter -> {
|
||||||
|
if (limiter.hasConfiguration(currentConfiguration)) {
|
||||||
|
return limiter;
|
||||||
|
} else {
|
||||||
|
return rateLimitFactory.apply(cacheCluster, currentConfiguration);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private CardinalityRateLimiter createUnsealedSenderCardinalityLimiter(FaultTolerantRedisCluster cacheCluster,
|
||||||
|
CardinalityRateLimitConfiguration configuration) {
|
||||||
|
return new CardinalityRateLimiter(cacheCluster, "unsealedSender", configuration.getTtl(),
|
||||||
|
configuration.getMaxCardinality());
|
||||||
|
}
|
||||||
|
|
||||||
|
private RateLimiter createUnsealedIpLimiter(FaultTolerantRedisCluster cacheCluster,
|
||||||
|
RateLimitConfiguration configuration) {
|
||||||
|
return createLimiter(cacheCluster, configuration, "unsealedIp");
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateLimiter createRateLimitResetLimiter(FaultTolerantRedisCluster cacheCluster,
|
||||||
|
RateLimitConfiguration configuration) {
|
||||||
|
return createLimiter(cacheCluster, configuration, "rateLimitReset");
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateLimiter createRecaptchaChallengeAttemptLimiter(FaultTolerantRedisCluster cacheCluster,
|
||||||
|
RateLimitConfiguration configuration) {
|
||||||
|
return createLimiter(cacheCluster, configuration, "recaptchaChallengeAttempt");
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateLimiter createRecaptchaChallengeSuccessLimiter(FaultTolerantRedisCluster cacheCluster,
|
||||||
|
RateLimitConfiguration configuration) {
|
||||||
|
return createLimiter(cacheCluster, configuration, "recaptchaChallengeSuccess");
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateLimiter createPushChallengeAttemptLimiter(FaultTolerantRedisCluster cacheCluster,
|
||||||
|
RateLimitConfiguration configuration) {
|
||||||
|
return createLimiter(cacheCluster, configuration, "pushChallengeAttempt");
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateLimiter createPushChallengeSuccessLimiter(FaultTolerantRedisCluster cacheCluster,
|
||||||
|
RateLimitConfiguration configuration) {
|
||||||
|
return createLimiter(cacheCluster, configuration, "pushChallengeSuccess");
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateLimiter createDailyPreKeysLimiter(FaultTolerantRedisCluster cacheCluster,
|
||||||
|
RateLimitConfiguration configuration) {
|
||||||
|
return createLimiter(cacheCluster, configuration, "dailyPreKeys");
|
||||||
|
}
|
||||||
|
|
||||||
|
private RateLimiter createLimiter(FaultTolerantRedisCluster cacheCluster, RateLimitConfiguration configuration,
|
||||||
|
String name) {
|
||||||
|
return new RateLimiter(cacheCluster, name,
|
||||||
|
configuration.getBucketSize(),
|
||||||
|
configuration.getLeakRatePerMinute());
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,11 +28,11 @@ public class PreKeyRateLimiter {
|
||||||
private static final String RATE_LIMITED_ACCOUNTS_UNENFORCED_HLL_KEY = "PreKeyRateLimiter::rateLimitedAccounts::unenforced";
|
private static final String RATE_LIMITED_ACCOUNTS_UNENFORCED_HLL_KEY = "PreKeyRateLimiter::rateLimitedAccounts::unenforced";
|
||||||
private static final long RATE_LIMITED_ACCOUNTS_HLL_TTL_SECONDS = Duration.days(1).toSeconds();
|
private static final long RATE_LIMITED_ACCOUNTS_HLL_TTL_SECONDS = Duration.days(1).toSeconds();
|
||||||
|
|
||||||
private final RateLimiters rateLimiters;
|
private final DynamicRateLimiters rateLimiters;
|
||||||
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
||||||
private final RateLimitResetMetricsManager metricsManager;
|
private final RateLimitResetMetricsManager metricsManager;
|
||||||
|
|
||||||
public PreKeyRateLimiter(final RateLimiters rateLimiters,
|
public PreKeyRateLimiter(final DynamicRateLimiters rateLimiters,
|
||||||
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager,
|
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager,
|
||||||
final RateLimitResetMetricsManager metricsManager) {
|
final RateLimitResetMetricsManager metricsManager) {
|
||||||
this.rateLimiters = rateLimiters;
|
this.rateLimiters = rateLimiters;
|
||||||
|
|
|
@ -26,7 +26,7 @@ public class RateLimitChallengeManager {
|
||||||
private final PreKeyRateLimiter preKeyRateLimiter;
|
private final PreKeyRateLimiter preKeyRateLimiter;
|
||||||
private final UnsealedSenderRateLimiter unsealedSenderRateLimiter;
|
private final UnsealedSenderRateLimiter unsealedSenderRateLimiter;
|
||||||
|
|
||||||
private final RateLimiters rateLimiters;
|
private final DynamicRateLimiters rateLimiters;
|
||||||
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
||||||
|
|
||||||
public static final String OPTION_RECAPTCHA = "recaptcha";
|
public static final String OPTION_RECAPTCHA = "recaptcha";
|
||||||
|
@ -43,7 +43,7 @@ public class RateLimitChallengeManager {
|
||||||
final RecaptchaClient recaptchaClient,
|
final RecaptchaClient recaptchaClient,
|
||||||
final PreKeyRateLimiter preKeyRateLimiter,
|
final PreKeyRateLimiter preKeyRateLimiter,
|
||||||
final UnsealedSenderRateLimiter unsealedSenderRateLimiter,
|
final UnsealedSenderRateLimiter unsealedSenderRateLimiter,
|
||||||
final RateLimiters rateLimiters,
|
final DynamicRateLimiters rateLimiters,
|
||||||
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
|
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
|
||||||
|
|
||||||
this.pushChallengeManager = pushChallengeManager;
|
this.pushChallengeManager = pushChallengeManager;
|
||||||
|
|
|
@ -5,14 +5,8 @@
|
||||||
package org.whispersystems.textsecuregcm.limits;
|
package org.whispersystems.textsecuregcm.limits;
|
||||||
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration.CardinalityRateLimitConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration.RateLimitConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
|
||||||
|
|
||||||
public class RateLimiters {
|
public class RateLimiters {
|
||||||
|
|
||||||
|
@ -41,22 +35,7 @@ public class RateLimiters {
|
||||||
|
|
||||||
private final RateLimiter checkAccountExistenceLimiter;
|
private final RateLimiter checkAccountExistenceLimiter;
|
||||||
|
|
||||||
private final AtomicReference<CardinalityRateLimiter> unsealedSenderCardinalityLimiter;
|
public RateLimiters(RateLimitsConfiguration config, FaultTolerantRedisCluster cacheCluster) {
|
||||||
private final AtomicReference<RateLimiter> unsealedIpLimiter;
|
|
||||||
private final AtomicReference<RateLimiter> rateLimitResetLimiter;
|
|
||||||
private final AtomicReference<RateLimiter> recaptchaChallengeAttemptLimiter;
|
|
||||||
private final AtomicReference<RateLimiter> recaptchaChallengeSuccessLimiter;
|
|
||||||
private final AtomicReference<RateLimiter> pushChallengeAttemptLimiter;
|
|
||||||
private final AtomicReference<RateLimiter> pushChallengeSuccessLimiter;
|
|
||||||
private final AtomicReference<RateLimiter> dailyPreKeysLimiter;
|
|
||||||
|
|
||||||
private final FaultTolerantRedisCluster cacheCluster;
|
|
||||||
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfig;
|
|
||||||
|
|
||||||
public RateLimiters(RateLimitsConfiguration config, DynamicConfigurationManager<DynamicConfiguration> dynamicConfig, FaultTolerantRedisCluster cacheCluster) {
|
|
||||||
this.cacheCluster = cacheCluster;
|
|
||||||
this.dynamicConfig = dynamicConfig;
|
|
||||||
|
|
||||||
this.smsDestinationLimiter = new RateLimiter(cacheCluster, "smsDestination",
|
this.smsDestinationLimiter = new RateLimiter(cacheCluster, "smsDestination",
|
||||||
config.getSmsDestination().getBucketSize(),
|
config.getSmsDestination().getBucketSize(),
|
||||||
config.getSmsDestination().getLeakRatePerMinute());
|
config.getSmsDestination().getLeakRatePerMinute());
|
||||||
|
@ -132,93 +111,6 @@ public class RateLimiters {
|
||||||
this.checkAccountExistenceLimiter = new RateLimiter(cacheCluster, "checkAccountExistence",
|
this.checkAccountExistenceLimiter = new RateLimiter(cacheCluster, "checkAccountExistence",
|
||||||
config.getCheckAccountExistence().getBucketSize(),
|
config.getCheckAccountExistence().getBucketSize(),
|
||||||
config.getCheckAccountExistence().getLeakRatePerMinute());
|
config.getCheckAccountExistence().getLeakRatePerMinute());
|
||||||
|
|
||||||
this.dailyPreKeysLimiter = new AtomicReference<>(createDailyPreKeysLimiter(cacheCluster, dynamicConfig.getConfiguration().getLimits().getDailyPreKeys()));
|
|
||||||
|
|
||||||
this.unsealedSenderCardinalityLimiter = new AtomicReference<>(createUnsealedSenderCardinalityLimiter(cacheCluster, dynamicConfig.getConfiguration().getLimits().getUnsealedSenderNumber()));
|
|
||||||
this.unsealedIpLimiter = new AtomicReference<>(createUnsealedIpLimiter(cacheCluster, dynamicConfig.getConfiguration().getLimits().getUnsealedSenderIp()));
|
|
||||||
|
|
||||||
this.rateLimitResetLimiter = new AtomicReference<>(
|
|
||||||
createRateLimitResetLimiter(cacheCluster, dynamicConfig.getConfiguration().getLimits().getRateLimitReset()));
|
|
||||||
|
|
||||||
this.recaptchaChallengeAttemptLimiter = new AtomicReference<>(createRecaptchaChallengeAttemptLimiter(cacheCluster, dynamicConfig.getConfiguration().getLimits().getRecaptchaChallengeAttempt()));
|
|
||||||
this.recaptchaChallengeSuccessLimiter = new AtomicReference<>(createRecaptchaChallengeSuccessLimiter(cacheCluster, dynamicConfig.getConfiguration().getLimits().getRecaptchaChallengeSuccess()));
|
|
||||||
this.pushChallengeAttemptLimiter = new AtomicReference<>(createPushChallengeAttemptLimiter(cacheCluster, dynamicConfig.getConfiguration().getLimits().getPushChallengeAttempt()));
|
|
||||||
this.pushChallengeSuccessLimiter = new AtomicReference<>(createPushChallengeSuccessLimiter(cacheCluster, dynamicConfig.getConfiguration().getLimits().getPushChallengeSuccess()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public CardinalityRateLimiter getUnsealedSenderCardinalityLimiter() {
|
|
||||||
CardinalityRateLimitConfiguration currentConfiguration = dynamicConfig.getConfiguration().getLimits().getUnsealedSenderNumber();
|
|
||||||
|
|
||||||
return this.unsealedSenderCardinalityLimiter.updateAndGet(rateLimiter -> {
|
|
||||||
if (rateLimiter.hasConfiguration(currentConfiguration)) {
|
|
||||||
return rateLimiter;
|
|
||||||
} else {
|
|
||||||
return createUnsealedSenderCardinalityLimiter(cacheCluster, currentConfiguration);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public RateLimiter getUnsealedIpLimiter() {
|
|
||||||
return updateAndGetRateLimiter(
|
|
||||||
unsealedIpLimiter,
|
|
||||||
dynamicConfig.getConfiguration().getLimits().getUnsealedSenderIp(),
|
|
||||||
this::createUnsealedIpLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RateLimiter getRateLimitResetLimiter() {
|
|
||||||
return updateAndGetRateLimiter(
|
|
||||||
rateLimitResetLimiter,
|
|
||||||
dynamicConfig.getConfiguration().getLimits().getRateLimitReset(),
|
|
||||||
this::createRateLimitResetLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RateLimiter getRecaptchaChallengeAttemptLimiter() {
|
|
||||||
return updateAndGetRateLimiter(
|
|
||||||
recaptchaChallengeAttemptLimiter,
|
|
||||||
dynamicConfig.getConfiguration().getLimits().getRecaptchaChallengeAttempt(),
|
|
||||||
this::createRecaptchaChallengeAttemptLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RateLimiter getRecaptchaChallengeSuccessLimiter() {
|
|
||||||
return updateAndGetRateLimiter(
|
|
||||||
recaptchaChallengeSuccessLimiter,
|
|
||||||
dynamicConfig.getConfiguration().getLimits().getRecaptchaChallengeSuccess(),
|
|
||||||
this::createRecaptchaChallengeSuccessLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RateLimiter getPushChallengeAttemptLimiter() {
|
|
||||||
return updateAndGetRateLimiter(
|
|
||||||
pushChallengeAttemptLimiter,
|
|
||||||
dynamicConfig.getConfiguration().getLimits().getPushChallengeAttempt(),
|
|
||||||
this::createPushChallengeAttemptLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RateLimiter getPushChallengeSuccessLimiter() {
|
|
||||||
return updateAndGetRateLimiter(
|
|
||||||
pushChallengeSuccessLimiter,
|
|
||||||
dynamicConfig.getConfiguration().getLimits().getPushChallengeSuccess(),
|
|
||||||
this::createPushChallengeSuccessLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RateLimiter getDailyPreKeysLimiter() {
|
|
||||||
return updateAndGetRateLimiter(
|
|
||||||
dailyPreKeysLimiter,
|
|
||||||
dynamicConfig.getConfiguration().getLimits().getDailyPreKeys(),
|
|
||||||
this::createDailyPreKeysLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private RateLimiter updateAndGetRateLimiter(final AtomicReference<RateLimiter> rateLimiter,
|
|
||||||
RateLimitConfiguration currentConfiguration,
|
|
||||||
BiFunction<FaultTolerantRedisCluster, RateLimitConfiguration, RateLimiter> rateLimitFactory) {
|
|
||||||
|
|
||||||
return rateLimiter.updateAndGet(limiter -> {
|
|
||||||
if (limiter.hasConfiguration(currentConfiguration)) {
|
|
||||||
return limiter;
|
|
||||||
} else {
|
|
||||||
return rateLimitFactory.apply(cacheCluster, currentConfiguration);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RateLimiter getAllocateDeviceLimiter() {
|
public RateLimiter getAllocateDeviceLimiter() {
|
||||||
|
@ -296,43 +188,4 @@ public class RateLimiters {
|
||||||
public RateLimiter getCheckAccountExistenceLimiter() {
|
public RateLimiter getCheckAccountExistenceLimiter() {
|
||||||
return checkAccountExistenceLimiter;
|
return checkAccountExistenceLimiter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CardinalityRateLimiter createUnsealedSenderCardinalityLimiter(FaultTolerantRedisCluster cacheCluster, CardinalityRateLimitConfiguration configuration) {
|
|
||||||
return new CardinalityRateLimiter(cacheCluster, "unsealedSender", configuration.getTtl(), configuration.getMaxCardinality());
|
|
||||||
}
|
|
||||||
|
|
||||||
private RateLimiter createUnsealedIpLimiter(FaultTolerantRedisCluster cacheCluster, RateLimitConfiguration configuration)
|
|
||||||
{
|
|
||||||
return createLimiter(cacheCluster, configuration, "unsealedIp");
|
|
||||||
}
|
|
||||||
|
|
||||||
public RateLimiter createRateLimitResetLimiter(FaultTolerantRedisCluster cacheCluster, RateLimitConfiguration configuration) {
|
|
||||||
return createLimiter(cacheCluster, configuration, "rateLimitReset");
|
|
||||||
}
|
|
||||||
|
|
||||||
public RateLimiter createRecaptchaChallengeAttemptLimiter(FaultTolerantRedisCluster cacheCluster, RateLimitConfiguration configuration) {
|
|
||||||
return createLimiter(cacheCluster, configuration, "recaptchaChallengeAttempt");
|
|
||||||
}
|
|
||||||
|
|
||||||
public RateLimiter createRecaptchaChallengeSuccessLimiter(FaultTolerantRedisCluster cacheCluster, RateLimitConfiguration configuration) {
|
|
||||||
return createLimiter(cacheCluster, configuration, "recaptchaChallengeSuccess");
|
|
||||||
}
|
|
||||||
|
|
||||||
public RateLimiter createPushChallengeAttemptLimiter(FaultTolerantRedisCluster cacheCluster, RateLimitConfiguration configuration) {
|
|
||||||
return createLimiter(cacheCluster, configuration, "pushChallengeAttempt");
|
|
||||||
}
|
|
||||||
|
|
||||||
public RateLimiter createPushChallengeSuccessLimiter(FaultTolerantRedisCluster cacheCluster, RateLimitConfiguration configuration) {
|
|
||||||
return createLimiter(cacheCluster, configuration, "pushChallengeSuccess");
|
|
||||||
}
|
|
||||||
|
|
||||||
public RateLimiter createDailyPreKeysLimiter(FaultTolerantRedisCluster cacheCluster, RateLimitConfiguration configuration) {
|
|
||||||
return createLimiter(cacheCluster, configuration, "dailyPreKeys");
|
|
||||||
}
|
|
||||||
|
|
||||||
private RateLimiter createLimiter(FaultTolerantRedisCluster cacheCluster, RateLimitConfiguration configuration, String name) {
|
|
||||||
return new RateLimiter(cacheCluster, name,
|
|
||||||
configuration.getBucketSize(),
|
|
||||||
configuration.getLeakRatePerMinute());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import org.whispersystems.textsecuregcm.util.Util;
|
||||||
|
|
||||||
public class UnsealedSenderRateLimiter {
|
public class UnsealedSenderRateLimiter {
|
||||||
|
|
||||||
private final RateLimiters rateLimiters;
|
private final DynamicRateLimiters rateLimiters;
|
||||||
private final FaultTolerantRedisCluster rateLimitCluster;
|
private final FaultTolerantRedisCluster rateLimitCluster;
|
||||||
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
||||||
private final RateLimitResetMetricsManager metricsManager;
|
private final RateLimitResetMetricsManager metricsManager;
|
||||||
|
@ -37,7 +37,7 @@ public class UnsealedSenderRateLimiter {
|
||||||
private static final long RATE_LIMITED_ACCOUNTS_HLL_TTL_SECONDS = Duration.days(1).toSeconds();
|
private static final long RATE_LIMITED_ACCOUNTS_HLL_TTL_SECONDS = Duration.days(1).toSeconds();
|
||||||
|
|
||||||
|
|
||||||
public UnsealedSenderRateLimiter(final RateLimiters rateLimiters,
|
public UnsealedSenderRateLimiter(final DynamicRateLimiters rateLimiters,
|
||||||
final FaultTolerantRedisCluster rateLimitCluster,
|
final FaultTolerantRedisCluster rateLimitCluster,
|
||||||
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager,
|
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager,
|
||||||
final RateLimitResetMetricsManager metricsManager) {
|
final RateLimitResetMetricsManager metricsManager) {
|
||||||
|
|
|
@ -26,12 +26,12 @@ class PreKeyRateLimiterTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
final RateLimiters rateLimiters = mock(RateLimiters.class);
|
final DynamicRateLimiters rateLimiters = mock(DynamicRateLimiters.class);
|
||||||
|
|
||||||
dailyPreKeyLimiter = mock(RateLimiter.class);
|
dailyPreKeyLimiter = mock(RateLimiter.class);
|
||||||
when(rateLimiters.getDailyPreKeysLimiter()).thenReturn(dailyPreKeyLimiter);
|
when(rateLimiters.getDailyPreKeysLimiter()).thenReturn(dailyPreKeyLimiter);
|
||||||
|
|
||||||
final DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
||||||
rateLimitChallengeConfiguration = mock(DynamicRateLimitChallengeConfiguration.class);
|
rateLimitChallengeConfiguration = mock(DynamicRateLimitChallengeConfiguration.class);
|
||||||
final DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
|
final DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ class RateLimitChallengeManagerTest {
|
||||||
private PreKeyRateLimiter preKeyRateLimiter;
|
private PreKeyRateLimiter preKeyRateLimiter;
|
||||||
private UnsealedSenderRateLimiter unsealedSenderRateLimiter;
|
private UnsealedSenderRateLimiter unsealedSenderRateLimiter;
|
||||||
private DynamicRateLimitChallengeConfiguration rateLimitChallengeConfiguration;
|
private DynamicRateLimitChallengeConfiguration rateLimitChallengeConfiguration;
|
||||||
private RateLimiters rateLimiters;
|
private DynamicRateLimiters rateLimiters;
|
||||||
|
|
||||||
private RateLimitChallengeManager rateLimitChallengeManager;
|
private RateLimitChallengeManager rateLimitChallengeManager;
|
||||||
|
|
||||||
|
@ -45,9 +45,9 @@ class RateLimitChallengeManagerTest {
|
||||||
recaptchaClient = mock(RecaptchaClient.class);
|
recaptchaClient = mock(RecaptchaClient.class);
|
||||||
preKeyRateLimiter = mock(PreKeyRateLimiter.class);
|
preKeyRateLimiter = mock(PreKeyRateLimiter.class);
|
||||||
unsealedSenderRateLimiter = mock(UnsealedSenderRateLimiter.class);
|
unsealedSenderRateLimiter = mock(UnsealedSenderRateLimiter.class);
|
||||||
rateLimiters = mock(RateLimiters.class);
|
rateLimiters = mock(DynamicRateLimiters.class);
|
||||||
|
|
||||||
final DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
||||||
final DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
|
final DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
|
||||||
rateLimitChallengeConfiguration = mock(DynamicRateLimitChallengeConfiguration.class);
|
rateLimitChallengeConfiguration = mock(DynamicRateLimitChallengeConfiguration.class);
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ class UnsealedSenderRateLimiterTest {
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() throws Exception {
|
void setUp() throws Exception {
|
||||||
|
|
||||||
final RateLimiters rateLimiters = mock(RateLimiters.class);
|
final DynamicRateLimiters rateLimiters = mock(DynamicRateLimiters.class);
|
||||||
final CardinalityRateLimiter cardinalityRateLimiter =
|
final CardinalityRateLimiter cardinalityRateLimiter =
|
||||||
new CardinalityRateLimiter(REDIS_CLUSTER_EXTENSION.getRedisCluster(), "test", Duration.ofDays(1), 1);
|
new CardinalityRateLimiter(REDIS_CLUSTER_EXTENSION.getRedisCluster(), "test", Duration.ofDays(1), 1);
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@ import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration.Ra
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicRateLimitsConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicRateLimitsConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.limits.CardinalityRateLimiter;
|
import org.whispersystems.textsecuregcm.limits.CardinalityRateLimiter;
|
||||||
|
import org.whispersystems.textsecuregcm.limits.DynamicRateLimiters;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
|
||||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class DynamicRateLimitsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUnchangingConfiguration() {
|
void testUnchangingConfiguration() {
|
||||||
RateLimiters rateLimiters = new RateLimiters(new RateLimitsConfiguration(), dynamicConfig, redisCluster);
|
DynamicRateLimiters rateLimiters = new DynamicRateLimiters(redisCluster, dynamicConfig);
|
||||||
|
|
||||||
RateLimiter limiter = rateLimiters.getUnsealedIpLimiter();
|
RateLimiter limiter = rateLimiters.getUnsealedIpLimiter();
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ class DynamicRateLimitsTest {
|
||||||
|
|
||||||
when(dynamicConfig.getConfiguration()).thenReturn(configuration);
|
when(dynamicConfig.getConfiguration()).thenReturn(configuration);
|
||||||
|
|
||||||
RateLimiters rateLimiters = new RateLimiters(new RateLimitsConfiguration(), dynamicConfig, redisCluster);
|
DynamicRateLimiters rateLimiters = new DynamicRateLimiters(redisCluster, dynamicConfig);
|
||||||
|
|
||||||
CardinalityRateLimiter limiter = rateLimiters.getUnsealedSenderCardinalityLimiter();
|
CardinalityRateLimiter limiter = rateLimiters.getUnsealedSenderCardinalityLimiter();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue