Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Block registration for unacceptable IP addresses or subnet #1555

Open
wants to merge 2 commits into
base: 2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eureka-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies {
api 'jakarta.inject:jakarta.inject-api:2.0.1'
api 'com.thoughtworks.xstream:xstream:1.4.20'
implementation 'com.google.guava:guava:33.0.0-jre'
implementation 'commons-net:commons-net:3.11.1'

// These dependencies are marked 'compileOnly' in the client, but we need them always on the server
api "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:${jacksonVersion}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -707,4 +707,17 @@ public int getHealthStatusMinNumberOfAvailablePeers() {
public int getInitialCapacityOfResponseCache() {
return configInstance.getIntProperty(namespace + "initialCapacityOfResponseCache", 1000).get();
}

@Nullable
@Override
public Set<String> getBlockingIpAddresses() {
DynamicStringProperty blockingIpAddresses = configInstance.getStringProperty(namespace + "blockingIpAddresses", null);
if (null == blockingIpAddresses || null == blockingIpAddresses.get()) {
return null;
} else {
String blockingIpAddressesStr = blockingIpAddresses.get();
String[] whitelistEntries = blockingIpAddressesStr.split(",");
return new HashSet<String>(Arrays.asList(whitelistEntries));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -705,4 +705,11 @@ public interface EurekaServerConfig {
* @return the capacity of responseCache.
*/
int getInitialCapacityOfResponseCache();

/**
*
* @return A set of IP addresses that have to be blocked
*/
@Nullable
Set<String> getBlockingIpAddresses();
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
import com.netflix.discovery.util.SpectatorUtil;
import com.netflix.eureka.transport.EurekaServerHttpClientFactory;
import jakarta.annotation.Nullable;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.AbstractQueue;
import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -57,6 +59,7 @@
import com.netflix.eureka.registry.rule.InstanceStatusOverrideRule;
import com.netflix.eureka.resources.ServerCodecs;
import com.netflix.eureka.util.MeasuredRate;
import org.apache.commons.net.util.SubnetUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -204,6 +207,10 @@ public Map<String, InstanceStatus> overriddenInstanceStatusesSnapshot() {
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
read.lock();
try {
if (isRegistrantDeniedByIpBlacklist(registrant)) {
return;
}

Map<String, Lease<InstanceInfo>> gMap = registry.get(registrant.getAppName());
REGISTER.increment(isReplication);
if (gMap == null) {
Expand Down Expand Up @@ -281,6 +288,33 @@ public void register(InstanceInfo registrant, int leaseDuration, boolean isRepli
}
}

private boolean isRegistrantDeniedByIpBlacklist(InstanceInfo registrant) {
try {
Set<String> blockingIpAddresses = serverConfig.getBlockingIpAddresses();
if (blockingIpAddresses == null) {
return false;
}

InetAddress address = InetAddress.getByName(registrant.getIPAddr());
if (blockingIpAddresses.contains(address.getHostAddress())) {
return true;
}

for (String subnet: blockingIpAddresses) {
if (subnet.contains("/")) {
SubnetUtils utils = new SubnetUtils(subnet);
if (utils.getInfo().isInRange(registrant.getIPAddr())) {
return true;
}
}
}
} catch (UnknownHostException e) {
logger.error("Unable to determine whether this host is IP blocked for id {}", registrant.getId());
}

return false;
}

/**
* Cancels the registration of an instance.
*
Expand Down
12 changes: 12 additions & 0 deletions eureka-tests/src/test/java/com/netflix/eureka/AbstractTester.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;

Expand Down Expand Up @@ -147,6 +148,7 @@ public void tearDown() throws Exception {
remoteRegionAppsDelta.clear();
ConfigurationManager.getConfigInstance().clearProperty("eureka.remoteRegionUrls");
ConfigurationManager.getConfigInstance().clearProperty("eureka.deltaRetentionTimerIntervalInMs");
ConfigurationManager.getConfigInstance().clearProperty("eureka.blockingIpAddresses");
}

private static Application createRemoteApps() {
Expand Down Expand Up @@ -210,6 +212,12 @@ private static InstanceInfo createLocalInstanceWithStatus(String hostname, Insta
return instanceBuilder.build();
}

protected static InstanceInfo createLocalInstanceWithBlockingIpAddresses(String blockingIpAddresses) {
ConfigurationManager.getConfigInstance().clearProperty("eureka.blockingIpAddresses");
ConfigurationManager.getConfigInstance().setProperty("eureka.blockingIpAddresses", blockingIpAddresses);
return createLocalInstance(LOCAL_REGION_INSTANCE_1_HOSTNAME);
}

private static AmazonInfo getAmazonInfo(@Nullable String availabilityZone, String instanceHostName) {
AmazonInfo.Builder azBuilder = AmazonInfo.Builder.newBuilder();
azBuilder.addMetadata(AmazonInfo.MetaDataKey.availabilityZone, null == availabilityZone ? "us-east-1a" : availabilityZone);
Expand Down Expand Up @@ -260,6 +268,10 @@ protected void registerInstanceLocallyWithLeaseDurationInSecs(InstanceInfo remot
registeredApps.add(new Pair<String, String>(LOCAL_REGION_APP_NAME, remoteInstance.getId()));
}

protected void verifyInstanceNotRegistered(String appName, String id) {
InstanceInfo instance = registry.getInstanceByAppAndId(appName, id);
assertNull(instance);
}

/**
* Send renewal request to Eureka server to renew lease for 45 instances.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,4 +316,19 @@ public void testCircularQueue() throws Exception {
Assert.assertEquals(0, queue.size());
Assert.assertEquals(Collections.emptyList(), new ArrayList<>(queue));
}

@Test
public void testBlockingSpecificIpAddress() {
InstanceInfo blockedInstance = createLocalInstanceWithBlockingIpAddresses("10.10.101.1,192.168.0.1/32");
registry.register(blockedInstance, 10000000, false);
verifyInstanceNotRegistered(blockedInstance.getAppName(), blockedInstance.getId());
}

@Test
public void testBlockingIpAddressInSubnet() {
InstanceInfo blockedInstance = createLocalInstanceWithBlockingIpAddresses("10.10.101.0/24");
registry.register(blockedInstance, 10000000, false);
verifyInstanceNotRegistered(blockedInstance.getAppName(), blockedInstance.getId());
}

}