Remove X-Forwarded-For from RemoteAddressFilter
This commit is contained in:
parent
39fd955f13
commit
05a92494bb
|
@ -332,10 +332,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
|
|
||||||
MetricsUtil.configureRegistries(config, environment, dynamicConfigurationManager);
|
MetricsUtil.configureRegistries(config, environment, dynamicConfigurationManager);
|
||||||
|
|
||||||
final boolean useRemoteAddress = Optional.ofNullable(
|
|
||||||
System.getenv("SIGNAL_USE_REMOTE_ADDRESS"))
|
|
||||||
.isPresent();
|
|
||||||
|
|
||||||
if (config.getServerFactory() instanceof DefaultServerFactory defaultServerFactory) {
|
if (config.getServerFactory() instanceof DefaultServerFactory defaultServerFactory) {
|
||||||
defaultServerFactory.getApplicationConnectors()
|
defaultServerFactory.getApplicationConnectors()
|
||||||
.forEach(connectorFactory -> {
|
.forEach(connectorFactory -> {
|
||||||
|
@ -823,7 +819,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
|
|
||||||
final List<Filter> filters = new ArrayList<>();
|
final List<Filter> filters = new ArrayList<>();
|
||||||
filters.add(remoteDeprecationFilter);
|
filters.add(remoteDeprecationFilter);
|
||||||
filters.add(new RemoteAddressFilter(useRemoteAddress));
|
filters.add(new RemoteAddressFilter());
|
||||||
|
|
||||||
for (Filter filter : filters) {
|
for (Filter filter : filters) {
|
||||||
environment.servlets()
|
environment.servlets()
|
||||||
|
|
|
@ -6,15 +6,12 @@
|
||||||
package org.whispersystems.textsecuregcm.filters;
|
package org.whispersystems.textsecuregcm.filters;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.servlet.Filter;
|
import javax.servlet.Filter;
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.util.HttpServletRequestUtil;
|
import org.whispersystems.textsecuregcm.util.HttpServletRequestUtil;
|
||||||
|
@ -22,19 +19,15 @@ import org.whispersystems.textsecuregcm.util.HttpServletRequestUtil;
|
||||||
/**
|
/**
|
||||||
* Sets a {@link HttpServletRequest} attribute (that will also be available as a
|
* Sets a {@link HttpServletRequest} attribute (that will also be available as a
|
||||||
* {@link javax.ws.rs.container.ContainerRequestContext} property) with the remote address of the connection, using
|
* {@link javax.ws.rs.container.ContainerRequestContext} property) with the remote address of the connection, using
|
||||||
* either the {@link HttpServletRequest#getRemoteAddr()} or the {@code X-Forwarded-For} HTTP header value, depending on
|
* {@link HttpServletRequest#getRemoteAddr()}.
|
||||||
* whether {@link #preferRemoteAddress} is {@code true}.
|
|
||||||
*/
|
*/
|
||||||
public class RemoteAddressFilter implements Filter {
|
public class RemoteAddressFilter implements Filter {
|
||||||
|
|
||||||
public static final String REMOTE_ADDRESS_ATTRIBUTE_NAME = RemoteAddressFilter.class.getName() + ".remoteAddress";
|
public static final String REMOTE_ADDRESS_ATTRIBUTE_NAME = RemoteAddressFilter.class.getName() + ".remoteAddress";
|
||||||
private static final Logger logger = LoggerFactory.getLogger(RemoteAddressFilter.class);
|
private static final Logger logger = LoggerFactory.getLogger(RemoteAddressFilter.class);
|
||||||
|
|
||||||
private final boolean preferRemoteAddress;
|
|
||||||
|
|
||||||
|
public RemoteAddressFilter() {
|
||||||
public RemoteAddressFilter(boolean preferRemoteAddress) {
|
|
||||||
this.preferRemoteAddress = preferRemoteAddress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -43,16 +36,7 @@ public class RemoteAddressFilter implements Filter {
|
||||||
|
|
||||||
if (request instanceof HttpServletRequest httpServletRequest) {
|
if (request instanceof HttpServletRequest httpServletRequest) {
|
||||||
|
|
||||||
final String remoteAddress;
|
final String remoteAddress = HttpServletRequestUtil.getRemoteAddress(httpServletRequest);
|
||||||
|
|
||||||
if (preferRemoteAddress) {
|
|
||||||
remoteAddress = HttpServletRequestUtil.getRemoteAddress(httpServletRequest);
|
|
||||||
} else {
|
|
||||||
final String forwardedFor = httpServletRequest.getHeader(com.google.common.net.HttpHeaders.X_FORWARDED_FOR);
|
|
||||||
remoteAddress = getMostRecentProxy(forwardedFor)
|
|
||||||
.orElseGet(() -> HttpServletRequestUtil.getRemoteAddress(httpServletRequest));
|
|
||||||
}
|
|
||||||
|
|
||||||
request.setAttribute(REMOTE_ADDRESS_ATTRIBUTE_NAME, remoteAddress);
|
request.setAttribute(REMOTE_ADDRESS_ATTRIBUTE_NAME, remoteAddress);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -62,23 +46,4 @@ public class RemoteAddressFilter implements Filter {
|
||||||
chain.doFilter(request, response);
|
chain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the most recent proxy in a chain described by an {@code X-Forwarded-For} header.
|
|
||||||
*
|
|
||||||
* @param forwardedFor the value of an X-Forwarded-For header
|
|
||||||
* @return the IP address of the most recent proxy in the forwarding chain, or empty if none was found or
|
|
||||||
* {@code forwardedFor} was null
|
|
||||||
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For">X-Forwarded-For - HTTP |
|
|
||||||
* MDN</a>
|
|
||||||
*/
|
|
||||||
public static Optional<String> getMostRecentProxy(@Nullable final String forwardedFor) {
|
|
||||||
return Optional.ofNullable(forwardedFor)
|
|
||||||
.map(ff -> {
|
|
||||||
final int idx = forwardedFor.lastIndexOf(',') + 1;
|
|
||||||
return idx < forwardedFor.length()
|
|
||||||
? forwardedFor.substring(idx).trim()
|
|
||||||
: null;
|
|
||||||
})
|
|
||||||
.filter(StringUtils::isNotBlank);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,11 @@ import java.net.InetSocketAddress;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.signal.libsignal.protocol.ecc.ECKeyPair;
|
import org.signal.libsignal.protocol.ecc.ECKeyPair;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.filters.RemoteAddressFilter;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.ClientPublicKeysManager;
|
import org.whispersystems.textsecuregcm.storage.ClientPublicKeysManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -113,7 +114,7 @@ class WebsocketHandshakeCompleteHandler extends ChannelInboundHandlerAdapter {
|
||||||
if (trustForwardedFor && handshakeCompleteEvent.requestHeaders().contains(FORWARDED_FOR_HEADER)) {
|
if (trustForwardedFor && handshakeCompleteEvent.requestHeaders().contains(FORWARDED_FOR_HEADER)) {
|
||||||
final String forwardedFor = handshakeCompleteEvent.requestHeaders().get(FORWARDED_FOR_HEADER);
|
final String forwardedFor = handshakeCompleteEvent.requestHeaders().get(FORWARDED_FOR_HEADER);
|
||||||
|
|
||||||
return RemoteAddressFilter.getMostRecentProxy(forwardedFor).map(mostRecentProxy -> {
|
return getMostRecentProxy(forwardedFor).map(mostRecentProxy -> {
|
||||||
try {
|
try {
|
||||||
return InetAddresses.forString(mostRecentProxy);
|
return InetAddresses.forString(mostRecentProxy);
|
||||||
} catch (final IllegalArgumentException e) {
|
} catch (final IllegalArgumentException e) {
|
||||||
|
@ -131,4 +132,25 @@ class WebsocketHandshakeCompleteHandler extends ChannelInboundHandlerAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the most recent proxy in a chain described by an {@code X-Forwarded-For} header.
|
||||||
|
*
|
||||||
|
* @param forwardedFor the value of an X-Forwarded-For header
|
||||||
|
* @return the IP address of the most recent proxy in the forwarding chain, or empty if none was found or
|
||||||
|
* {@code forwardedFor} was null
|
||||||
|
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For">X-Forwarded-For - HTTP |
|
||||||
|
* MDN</a>
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
static Optional<String> getMostRecentProxy(@Nullable final String forwardedFor) {
|
||||||
|
return Optional.ofNullable(forwardedFor)
|
||||||
|
.map(ff -> {
|
||||||
|
final int idx = forwardedFor.lastIndexOf(',') + 1;
|
||||||
|
return idx < forwardedFor.length()
|
||||||
|
? forwardedFor.substring(idx).trim()
|
||||||
|
: null;
|
||||||
|
})
|
||||||
|
.filter(StringUtils::isNotBlank);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,6 @@ package org.whispersystems.textsecuregcm;
|
||||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.reset;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
import static org.whispersystems.textsecuregcm.filters.RemoteAddressFilter.REMOTE_ADDRESS_ATTRIBUTE_NAME;
|
import static org.whispersystems.textsecuregcm.filters.RemoteAddressFilter.REMOTE_ADDRESS_ATTRIBUTE_NAME;
|
||||||
|
|
||||||
import io.dropwizard.core.Application;
|
import io.dropwizard.core.Application;
|
||||||
|
@ -77,10 +75,10 @@ public class WebsocketResourceProviderIntegrationTest {
|
||||||
|
|
||||||
environment.jersey().register(testController);
|
environment.jersey().register(testController);
|
||||||
environment.servlets()
|
environment.servlets()
|
||||||
.addFilter("RemoteAddressFilter", new RemoteAddressFilter(true))
|
.addFilter("RemoteAddressFilter", new RemoteAddressFilter())
|
||||||
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
|
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
|
||||||
webSocketEnvironment.jersey().register(testController);
|
webSocketEnvironment.jersey().register(testController);
|
||||||
webSocketEnvironment.jersey().register(new RemoteAddressFilter(true));
|
webSocketEnvironment.jersey().register(new RemoteAddressFilter());
|
||||||
webSocketEnvironment.setAuthenticator(upgradeRequest ->
|
webSocketEnvironment.setAuthenticator(upgradeRequest ->
|
||||||
ReusableAuth.authenticated(mock(AuthenticatedAccount.class), PrincipalSupplier.forImmutablePrincipal()));
|
ReusableAuth.authenticated(mock(AuthenticatedAccount.class), PrincipalSupplier.forImmutablePrincipal()));
|
||||||
|
|
||||||
|
|
|
@ -95,10 +95,10 @@ public class WebsocketReuseAuthIntegrationTest {
|
||||||
|
|
||||||
environment.jersey().register(testController);
|
environment.jersey().register(testController);
|
||||||
environment.servlets()
|
environment.servlets()
|
||||||
.addFilter("RemoteAddressFilter", new RemoteAddressFilter(true))
|
.addFilter("RemoteAddressFilter", new RemoteAddressFilter())
|
||||||
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
|
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
|
||||||
webSocketEnvironment.jersey().register(testController);
|
webSocketEnvironment.jersey().register(testController);
|
||||||
webSocketEnvironment.jersey().register(new RemoteAddressFilter(true));
|
webSocketEnvironment.jersey().register(new RemoteAddressFilter());
|
||||||
webSocketEnvironment.setAuthenticator(upgradeRequest -> ReusableAuth.authenticated(ACCOUNT, PRINCIPAL_SUPPLIER));
|
webSocketEnvironment.setAuthenticator(upgradeRequest -> ReusableAuth.authenticated(ACCOUNT, PRINCIPAL_SUPPLIER));
|
||||||
|
|
||||||
webSocketEnvironment.jersey().property(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE);
|
webSocketEnvironment.jersey().property(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE);
|
||||||
|
|
|
@ -118,9 +118,9 @@ class PhoneNumberChangeRefreshRequirementProviderTest {
|
||||||
environment.jersey().register(testController);
|
environment.jersey().register(testController);
|
||||||
webSocketEnvironment.jersey().register(testController);
|
webSocketEnvironment.jersey().register(testController);
|
||||||
environment.servlets()
|
environment.servlets()
|
||||||
.addFilter("RemoteAddressFilter", new RemoteAddressFilter(true))
|
.addFilter("RemoteAddressFilter", new RemoteAddressFilter())
|
||||||
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
|
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
|
||||||
webSocketEnvironment.jersey().register(new RemoteAddressFilter(true));
|
webSocketEnvironment.jersey().register(new RemoteAddressFilter());
|
||||||
webSocketEnvironment.jersey()
|
webSocketEnvironment.jersey()
|
||||||
.register(new WebsocketRefreshApplicationEventListener(ACCOUNTS_MANAGER, CLIENT_PRESENCE));
|
.register(new WebsocketRefreshApplicationEventListener(ACCOUNTS_MANAGER, CLIENT_PRESENCE));
|
||||||
environment.jersey()
|
environment.jersey()
|
||||||
|
|
|
@ -8,7 +8,6 @@ package org.whispersystems.textsecuregcm.filters;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||||
|
|
||||||
import com.google.common.net.HttpHeaders;
|
|
||||||
import io.dropwizard.core.Application;
|
import io.dropwizard.core.Application;
|
||||||
import io.dropwizard.core.Configuration;
|
import io.dropwizard.core.Configuration;
|
||||||
import io.dropwizard.core.setup.Environment;
|
import io.dropwizard.core.setup.Environment;
|
||||||
|
@ -39,7 +38,6 @@ import javax.ws.rs.core.Context;
|
||||||
import org.eclipse.jetty.util.HostPort;
|
import org.eclipse.jetty.util.HostPort;
|
||||||
import org.eclipse.jetty.websocket.api.Session;
|
import org.eclipse.jetty.websocket.api.Session;
|
||||||
import org.eclipse.jetty.websocket.api.WebSocketListener;
|
import org.eclipse.jetty.websocket.api.WebSocketListener;
|
||||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
|
||||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||||
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
|
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
@ -47,7 +45,6 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.CsvSource;
|
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||||
import org.whispersystems.websocket.WebSocketResourceProviderFactory;
|
import org.whispersystems.websocket.WebSocketResourceProviderFactory;
|
||||||
|
@ -62,7 +59,6 @@ class RemoteAddressFilterIntegrationTest {
|
||||||
|
|
||||||
private static final String WEBSOCKET_PREFIX = "/websocket";
|
private static final String WEBSOCKET_PREFIX = "/websocket";
|
||||||
private static final String REMOTE_ADDRESS_PATH = "/remoteAddress";
|
private static final String REMOTE_ADDRESS_PATH = "/remoteAddress";
|
||||||
private static final String FORWARDED_FOR_PATH = "/forwardedFor";
|
|
||||||
private static final String WS_REQUEST_PATH = "/wsRequest";
|
private static final String WS_REQUEST_PATH = "/wsRequest";
|
||||||
|
|
||||||
// The Grizzly test container does not match the Jetty container used in real deployments, and JettyTestContainerFactory
|
// The Grizzly test container does not match the Jetty container used in real deployments, and JettyTestContainerFactory
|
||||||
|
@ -92,22 +88,6 @@ class RemoteAddressFilterIntegrationTest {
|
||||||
|
|
||||||
assertEquals(ip, response.remoteAddress());
|
assertEquals(ip, response.remoteAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@CsvSource(value = {"127.0.0.1, 192.168.1.1 \t 192.168.1.1",
|
|
||||||
"127.0.0.1, fe80:1:1:1:1:1:1:1 \t fe80:1:1:1:1:1:1:1"}, delimiterString = "\t")
|
|
||||||
void testForwardedFor(String forwardedFor, String expectedIp) {
|
|
||||||
|
|
||||||
Client client = EXTENSION.client();
|
|
||||||
|
|
||||||
final RemoteAddressFilterIntegrationTest.TestResponse response = client.target(
|
|
||||||
String.format("http://localhost:%d%s", EXTENSION.getLocalPort(), FORWARDED_FOR_PATH))
|
|
||||||
.request("application/json")
|
|
||||||
.header(HttpHeaders.X_FORWARDED_FOR, forwardedFor)
|
|
||||||
.get(RemoteAddressFilterIntegrationTest.TestResponse.class);
|
|
||||||
|
|
||||||
assertEquals(expectedIp, response.remoteAddress());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
@ -149,28 +129,6 @@ class RemoteAddressFilterIntegrationTest {
|
||||||
|
|
||||||
assertEquals(ip, response.remoteAddress());
|
assertEquals(ip, response.remoteAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@CsvSource(value = {"127.0.0.1, 192.168.1.1 \t 192.168.1.1",
|
|
||||||
"127.0.0.1, fe80:1:1:1:1:1:1:1 \t fe80:1:1:1:1:1:1:1"}, delimiterString = "\t")
|
|
||||||
void testForwardedFor(String forwardedFor, String expectedIp) throws Exception {
|
|
||||||
|
|
||||||
final ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
|
||||||
upgradeRequest.setHeader(HttpHeaders.X_FORWARDED_FOR, forwardedFor);
|
|
||||||
|
|
||||||
final CompletableFuture<byte[]> responseFuture = new CompletableFuture<>();
|
|
||||||
|
|
||||||
client.connect(new ClientEndpoint(WS_REQUEST_PATH, responseFuture),
|
|
||||||
URI.create(
|
|
||||||
String.format("ws://localhost:%d%s", EXTENSION.getLocalPort(), WEBSOCKET_PREFIX + FORWARDED_FOR_PATH)),
|
|
||||||
upgradeRequest);
|
|
||||||
|
|
||||||
final byte[] responseBytes = responseFuture.get(1, TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
final TestResponse response = SystemMapper.jsonMapper().readValue(responseBytes, TestResponse.class);
|
|
||||||
|
|
||||||
assertEquals(expectedIp, response.remoteAddress());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ClientEndpoint implements WebSocketListener {
|
private static class ClientEndpoint implements WebSocketListener {
|
||||||
|
@ -233,11 +191,6 @@ class RemoteAddressFilterIntegrationTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path(FORWARDED_FOR_PATH)
|
|
||||||
public static class TestForwardedForController extends TestController {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Path(WS_REQUEST_PATH)
|
@Path(WS_REQUEST_PATH)
|
||||||
public static class TestWebSocketController extends TestController {
|
public static class TestWebSocketController extends TestController {
|
||||||
|
|
||||||
|
@ -253,17 +206,11 @@ class RemoteAddressFilterIntegrationTest {
|
||||||
public void run(final Configuration configuration,
|
public void run(final Configuration configuration,
|
||||||
final Environment environment) throws Exception {
|
final Environment environment) throws Exception {
|
||||||
|
|
||||||
// 2 filters, to cover useRemoteAddress = {true, false}
|
environment.servlets().addFilter("RemoteAddressFilterRemoteAddress", new RemoteAddressFilter())
|
||||||
// each has explicit (not wildcard) path matching
|
|
||||||
environment.servlets().addFilter("RemoteAddressFilterRemoteAddress", new RemoteAddressFilter(true))
|
|
||||||
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, REMOTE_ADDRESS_PATH,
|
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, REMOTE_ADDRESS_PATH,
|
||||||
WEBSOCKET_PREFIX + REMOTE_ADDRESS_PATH);
|
WEBSOCKET_PREFIX + REMOTE_ADDRESS_PATH);
|
||||||
environment.servlets().addFilter("RemoteAddressFilterForwardedFor", new RemoteAddressFilter(false))
|
|
||||||
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, FORWARDED_FOR_PATH,
|
|
||||||
WEBSOCKET_PREFIX + FORWARDED_FOR_PATH);
|
|
||||||
|
|
||||||
environment.jersey().register(new TestRemoteAddressController());
|
environment.jersey().register(new TestRemoteAddressController());
|
||||||
environment.jersey().register(new TestForwardedForController());
|
|
||||||
|
|
||||||
// WebSocket set up
|
// WebSocket set up
|
||||||
final WebSocketConfiguration webSocketConfiguration = new WebSocketConfiguration();
|
final WebSocketConfiguration webSocketConfiguration = new WebSocketConfiguration();
|
||||||
|
@ -279,9 +226,6 @@ class RemoteAddressFilterIntegrationTest {
|
||||||
webSocketEnvironment, TestPrincipal.class, webSocketConfiguration,
|
webSocketEnvironment, TestPrincipal.class, webSocketConfiguration,
|
||||||
RemoteAddressFilter.REMOTE_ADDRESS_ATTRIBUTE_NAME);
|
RemoteAddressFilter.REMOTE_ADDRESS_ATTRIBUTE_NAME);
|
||||||
|
|
||||||
// 2 servlets, because the filter only runs for the Upgrade request
|
|
||||||
environment.servlets().addServlet("WebSocketForwardedFor", webSocketServlet)
|
|
||||||
.addMapping(WEBSOCKET_PREFIX + FORWARDED_FOR_PATH);
|
|
||||||
environment.servlets().addServlet("WebSocketRemoteAddress", webSocketServlet)
|
environment.servlets().addServlet("WebSocketRemoteAddress", webSocketServlet)
|
||||||
.addMapping(WEBSOCKET_PREFIX + REMOTE_ADDRESS_PATH);
|
.addMapping(WEBSOCKET_PREFIX + REMOTE_ADDRESS_PATH);
|
||||||
|
|
||||||
|
|
|
@ -5,24 +5,17 @@
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.filters;
|
package org.whispersystems.textsecuregcm.filters;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import com.google.common.net.HttpHeaders;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
|
||||||
import org.junit.jupiter.params.provider.CsvSource;
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
|
||||||
|
|
||||||
class RemoteAddressFilterTest {
|
class RemoteAddressFilterTest {
|
||||||
|
|
||||||
|
@ -36,7 +29,7 @@ class RemoteAddressFilterTest {
|
||||||
final HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
|
final HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
|
||||||
when(httpServletRequest.getRemoteAddr()).thenReturn(remoteAddr);
|
when(httpServletRequest.getRemoteAddr()).thenReturn(remoteAddr);
|
||||||
|
|
||||||
final RemoteAddressFilter filter = new RemoteAddressFilter(true);
|
final RemoteAddressFilter filter = new RemoteAddressFilter();
|
||||||
|
|
||||||
final FilterChain filterChain = mock(FilterChain.class);
|
final FilterChain filterChain = mock(FilterChain.class);
|
||||||
filter.doFilter(httpServletRequest, mock(ServletResponse.class), filterChain);
|
filter.doFilter(httpServletRequest, mock(ServletResponse.class), filterChain);
|
||||||
|
@ -45,41 +38,4 @@ class RemoteAddressFilterTest {
|
||||||
verify(filterChain).doFilter(any(ServletRequest.class), any(ServletResponse.class));
|
verify(filterChain).doFilter(any(ServletRequest.class), any(ServletResponse.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@CsvSource(value = {
|
|
||||||
"192.168.1.1, 127.0.0.1 \t 127.0.0.1",
|
|
||||||
"192.168.1.1, 0:0:0:0:0:0:0:1 \t 0:0:0:0:0:0:0:1"
|
|
||||||
}, delimiterString = "\t")
|
|
||||||
void testGetRemoteAddressFromHeader(final String forwardedFor, final String expectedRemoteAddr) throws Exception {
|
|
||||||
final HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
|
|
||||||
when(httpServletRequest.getHeader(HttpHeaders.X_FORWARDED_FOR)).thenReturn(forwardedFor);
|
|
||||||
|
|
||||||
final RemoteAddressFilter filter = new RemoteAddressFilter(false);
|
|
||||||
|
|
||||||
final FilterChain filterChain = mock(FilterChain.class);
|
|
||||||
filter.doFilter(httpServletRequest, mock(ServletResponse.class), filterChain);
|
|
||||||
|
|
||||||
verify(httpServletRequest).setAttribute(RemoteAddressFilter.REMOTE_ADDRESS_ATTRIBUTE_NAME, expectedRemoteAddr);
|
|
||||||
verify(filterChain).doFilter(any(ServletRequest.class), any(ServletResponse.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("argumentsForGetMostRecentProxy")
|
|
||||||
void getMostRecentProxy(final String forwardedFor, final Optional<String> expectedMostRecentProxy) {
|
|
||||||
assertEquals(expectedMostRecentProxy, RemoteAddressFilter.getMostRecentProxy(forwardedFor));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Stream<Arguments> argumentsForGetMostRecentProxy() {
|
|
||||||
return Stream.of(
|
|
||||||
arguments(null, Optional.empty()),
|
|
||||||
arguments("", Optional.empty()),
|
|
||||||
arguments(" ", Optional.empty()),
|
|
||||||
arguments("203.0.113.195,", Optional.empty()),
|
|
||||||
arguments("203.0.113.195, ", Optional.empty()),
|
|
||||||
arguments("203.0.113.195", Optional.of("203.0.113.195")),
|
|
||||||
arguments("203.0.113.195, 70.41.3.18, 150.172.238.178", Optional.of("150.172.238.178"))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,17 +4,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
import com.google.common.net.InetAddresses;
|
import com.google.common.net.InetAddresses;
|
||||||
import com.vdurmont.semver4j.Semver;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
import io.netty.channel.local.LocalAddress;
|
import io.netty.channel.local.LocalAddress;
|
||||||
import io.netty.handler.codec.http.DefaultHttpHeaders;
|
import io.netty.handler.codec.http.DefaultHttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
|
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
@ -22,7 +21,8 @@ import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
@ -32,8 +32,6 @@ import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.signal.libsignal.protocol.ecc.Curve;
|
import org.signal.libsignal.protocol.ecc.Curve;
|
||||||
import org.whispersystems.textsecuregcm.storage.ClientPublicKeysManager;
|
import org.whispersystems.textsecuregcm.storage.ClientPublicKeysManager;
|
||||||
import org.whispersystems.textsecuregcm.util.ua.ClientPlatform;
|
|
||||||
import org.whispersystems.textsecuregcm.util.ua.UserAgent;
|
|
||||||
|
|
||||||
class WebsocketHandshakeCompleteHandlerTest extends AbstractLeakDetectionTest {
|
class WebsocketHandshakeCompleteHandlerTest extends AbstractLeakDetectionTest {
|
||||||
|
|
||||||
|
@ -199,4 +197,23 @@ class WebsocketHandshakeCompleteHandlerTest extends AbstractLeakDetectionTest {
|
||||||
null)
|
null)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("argumentsForGetMostRecentProxy")
|
||||||
|
void getMostRecentProxy(final String forwardedFor, final Optional<String> expectedMostRecentProxy) {
|
||||||
|
assertEquals(expectedMostRecentProxy, WebsocketHandshakeCompleteHandler.getMostRecentProxy(forwardedFor));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<Arguments> argumentsForGetMostRecentProxy() {
|
||||||
|
return Stream.of(
|
||||||
|
arguments(null, Optional.empty()),
|
||||||
|
arguments("", Optional.empty()),
|
||||||
|
arguments(" ", Optional.empty()),
|
||||||
|
arguments("203.0.113.195,", Optional.empty()),
|
||||||
|
arguments("203.0.113.195, ", Optional.empty()),
|
||||||
|
arguments("203.0.113.195", Optional.of("203.0.113.195")),
|
||||||
|
arguments("203.0.113.195, 70.41.3.18, 150.172.238.178", Optional.of("150.172.238.178"))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -246,7 +246,7 @@ class MetricsHttpChannelListenerIntegrationTest {
|
||||||
metricsHttpChannelListener.configure(environment);
|
metricsHttpChannelListener.configure(environment);
|
||||||
environment.lifecycle().addEventListener(new TestListener(COUNT_DOWN_LATCH_FUTURE_REFERENCE));
|
environment.lifecycle().addEventListener(new TestListener(COUNT_DOWN_LATCH_FUTURE_REFERENCE));
|
||||||
|
|
||||||
environment.servlets().addFilter("RemoteAddressFilter", new RemoteAddressFilter(true))
|
environment.servlets().addFilter("RemoteAddressFilter", new RemoteAddressFilter())
|
||||||
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
|
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
|
||||||
|
|
||||||
environment.jersey().register(new TestResource());
|
environment.jersey().register(new TestResource());
|
||||||
|
|
Loading…
Reference in New Issue