Skip to content

Commit

Permalink
install RateLimitByIpFilter in soft-enforcement mode
Browse files Browse the repository at this point in the history
  • Loading branch information
jkt-signal authored Sep 18, 2024
1 parent 8cb9c60 commit aa60fae
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@
import org.whispersystems.textsecuregcm.limits.CardinalityEstimator;
import org.whispersystems.textsecuregcm.limits.MessageDeliveryLoopMonitor;
import org.whispersystems.textsecuregcm.limits.PushChallengeManager;
import org.whispersystems.textsecuregcm.limits.RateLimitByIpFilter;
import org.whispersystems.textsecuregcm.limits.RateLimitChallengeManager;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.mappers.CompletionExceptionMapper;
Expand Down Expand Up @@ -1003,6 +1004,7 @@ protected void configureServer(final ServerBuilder<?> serverBuilder) {

environment.jersey().register(new BufferingInterceptor());
environment.jersey().register(new VirtualExecutorServiceProvider("managed-async-virtual-thread-"));
environment.jersey().register(new RateLimitByIpFilter(rateLimiters, true));
environment.jersey().register(new RequestStatisticsFilter(TrafficSource.HTTP));
environment.jersey().register(MultiRecipientMessageProvider.class);
environment.jersey().register(new AuthDynamicFeature(accountAuthFilter));
Expand All @@ -1021,11 +1023,13 @@ protected void configureServer(final ServerBuilder<?> serverBuilder) {
clientReleaseManager, messageDeliveryLoopMonitor));
webSocketEnvironment.jersey()
.register(new WebsocketRefreshApplicationEventListener(accountsManager, clientPresenceManager));
webSocketEnvironment.jersey().register(new RateLimitByIpFilter(rateLimiters, true));
webSocketEnvironment.jersey().register(new RequestStatisticsFilter(TrafficSource.WEBSOCKET));
webSocketEnvironment.jersey().register(MultiRecipientMessageProvider.class);
webSocketEnvironment.jersey().register(new MetricsApplicationEventListener(TrafficSource.WEBSOCKET, clientReleaseManager));
webSocketEnvironment.jersey().register(new KeepAliveController(clientPresenceManager));


final List<SpamFilter> spamFilters = ServiceLoader.load(SpamFilter.class)
.stream()
.map(ServiceLoader.Provider::get)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
import org.whispersystems.textsecuregcm.filters.RemoteAddressFilter;
import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;

import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;

public class RateLimitByIpFilter implements ContainerRequestFilter {

Expand All @@ -33,11 +38,18 @@ public class RateLimitByIpFilter implements ContainerRequestFilter {

private static final ExceptionMapper<RateLimitExceededException> EXCEPTION_MAPPER = new RateLimitExceededExceptionMapper();

private static final String NO_IP_COUNTER_NAME = MetricsUtil.name(RateLimitByIpFilter.class, "noIpAddress");

private final RateLimiters rateLimiters;
private final boolean softEnforcement;

public RateLimitByIpFilter(final RateLimiters rateLimiters, final boolean softEnforcement) {
this.rateLimiters = requireNonNull(rateLimiters);
this.softEnforcement = softEnforcement;
}

public RateLimitByIpFilter(final RateLimiters rateLimiters) {
this.rateLimiters = requireNonNull(rateLimiters);
this(rateLimiters, false);
}

@Override
Expand Down Expand Up @@ -65,10 +77,19 @@ public void filter(final ContainerRequestContext requestContext) throws IOExcept

// checking if we failed to extract the most recent IP for any reason
if (remoteAddress.isEmpty()) {
Metrics.counter(
NO_IP_COUNTER_NAME,
Tags.of(
Tag.of("limiter", handle.id()),
Tag.of("fail", String.valueOf(annotation.failOnUnresolvedIp()))))
.increment();

// checking if annotation is configured to fail when the most recent IP is not resolved
if (annotation.failOnUnresolvedIp()) {
logger.error("Remote address was null");
throw INVALID_HEADER_EXCEPTION;
if (!softEnforcement) {
throw INVALID_HEADER_EXCEPTION;
}
}
// otherwise, allow request
return;
Expand All @@ -78,7 +99,9 @@ public void filter(final ContainerRequestContext requestContext) throws IOExcept
rateLimiter.validate(remoteAddress.get());
} catch (RateLimitExceededException e) {
final Response response = EXCEPTION_MAPPER.toResponse(e);
throw new ClientErrorException(response);
if (!softEnforcement) {
throw new ClientErrorException(response);
}
}
}
}

0 comments on commit aa60fae

Please sign in to comment.