From d91ce8f93ececc9237eb61006c60c36bf5b26548 Mon Sep 17 00:00:00 2001 From: G1 Date: Thu, 22 Aug 2024 00:45:15 +0900 Subject: [PATCH 1/2] test: Block registration for unacceptable IP addresses or subnet --- .../java/com/netflix/eureka/AbstractTester.java | 12 ++++++++++++ .../eureka/registry/InstanceRegistryTest.java | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/eureka-tests/src/test/java/com/netflix/eureka/AbstractTester.java b/eureka-tests/src/test/java/com/netflix/eureka/AbstractTester.java index ce6204a7bf..ae581dbfa9 100644 --- a/eureka-tests/src/test/java/com/netflix/eureka/AbstractTester.java +++ b/eureka-tests/src/test/java/com/netflix/eureka/AbstractTester.java @@ -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; @@ -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() { @@ -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); @@ -260,6 +268,10 @@ protected void registerInstanceLocallyWithLeaseDurationInSecs(InstanceInfo remot registeredApps.add(new Pair(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. diff --git a/eureka-tests/src/test/java/com/netflix/eureka/registry/InstanceRegistryTest.java b/eureka-tests/src/test/java/com/netflix/eureka/registry/InstanceRegistryTest.java index 1ac7eb3d83..944e45a640 100644 --- a/eureka-tests/src/test/java/com/netflix/eureka/registry/InstanceRegistryTest.java +++ b/eureka-tests/src/test/java/com/netflix/eureka/registry/InstanceRegistryTest.java @@ -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()); + } + } From c1f2be2bb4ff084ca9b46563a907c5b7a2ad6a27 Mon Sep 17 00:00:00 2001 From: G1 Date: Thu, 22 Aug 2024 00:45:22 +0900 Subject: [PATCH 2/2] feat: Block registration for unacceptable IP addresses or subnet --- eureka-core/build.gradle | 1 + .../eureka/DefaultEurekaServerConfig.java | 13 +++++++ .../netflix/eureka/EurekaServerConfig.java | 7 ++++ .../registry/AbstractInstanceRegistry.java | 34 +++++++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/eureka-core/build.gradle b/eureka-core/build.gradle index 0f408571fd..9c2703bda1 100644 --- a/eureka-core/build.gradle +++ b/eureka-core/build.gradle @@ -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}" diff --git a/eureka-core/src/main/java/com/netflix/eureka/DefaultEurekaServerConfig.java b/eureka-core/src/main/java/com/netflix/eureka/DefaultEurekaServerConfig.java index 9ef6db6f13..058662d62c 100644 --- a/eureka-core/src/main/java/com/netflix/eureka/DefaultEurekaServerConfig.java +++ b/eureka-core/src/main/java/com/netflix/eureka/DefaultEurekaServerConfig.java @@ -707,4 +707,17 @@ public int getHealthStatusMinNumberOfAvailablePeers() { public int getInitialCapacityOfResponseCache() { return configInstance.getIntProperty(namespace + "initialCapacityOfResponseCache", 1000).get(); } + + @Nullable + @Override + public Set 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(Arrays.asList(whitelistEntries)); + } + } } diff --git a/eureka-core/src/main/java/com/netflix/eureka/EurekaServerConfig.java b/eureka-core/src/main/java/com/netflix/eureka/EurekaServerConfig.java index cf8f9cc621..85f73fc2d8 100644 --- a/eureka-core/src/main/java/com/netflix/eureka/EurekaServerConfig.java +++ b/eureka-core/src/main/java/com/netflix/eureka/EurekaServerConfig.java @@ -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 getBlockingIpAddresses(); } diff --git a/eureka-core/src/main/java/com/netflix/eureka/registry/AbstractInstanceRegistry.java b/eureka-core/src/main/java/com/netflix/eureka/registry/AbstractInstanceRegistry.java index 74c908fabd..f14004c002 100644 --- a/eureka-core/src/main/java/com/netflix/eureka/registry/AbstractInstanceRegistry.java +++ b/eureka-core/src/main/java/com/netflix/eureka/registry/AbstractInstanceRegistry.java @@ -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; @@ -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; @@ -204,6 +207,10 @@ public Map overriddenInstanceStatusesSnapshot() { public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) { read.lock(); try { + if (isRegistrantDeniedByIpBlacklist(registrant)) { + return; + } + Map> gMap = registry.get(registrant.getAppName()); REGISTER.increment(isReplication); if (gMap == null) { @@ -281,6 +288,33 @@ public void register(InstanceInfo registrant, int leaseDuration, boolean isRepli } } + private boolean isRegistrantDeniedByIpBlacklist(InstanceInfo registrant) { + try { + Set 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. *