diff --git a/pom.xml b/pom.xml index 8a3b7f0..f69885a 100644 --- a/pom.xml +++ b/pom.xml @@ -45,13 +45,13 @@ com.aws.greengrass nucleus - 2.2.0-SNAPSHOT + 2.12.0-SNAPSHOT provided com.aws.greengrass nucleus - 2.2.0-SNAPSHOT + 2.12.0-SNAPSHOT test-jar test diff --git a/src/main/java/com/aws/greengrass/detector/config/Config.java b/src/main/java/com/aws/greengrass/detector/config/Config.java index c914a58..9075856 100644 --- a/src/main/java/com/aws/greengrass/detector/config/Config.java +++ b/src/main/java/com/aws/greengrass/detector/config/Config.java @@ -6,11 +6,15 @@ package com.aws.greengrass.detector.config; import com.aws.greengrass.componentmanager.KernelConfigResolver; +import com.aws.greengrass.config.Topic; import com.aws.greengrass.config.Topics; import com.aws.greengrass.logging.api.Logger; import com.aws.greengrass.logging.impl.LogManager; import com.aws.greengrass.util.Coerce; +import lombok.Getter; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -19,6 +23,7 @@ public class Config { static final String INCLUDE_IPV4_LOOPBACK_ADDRESSES_CONFIG_KEY = "includeIPv4LoopbackAddrs"; static final String INCLUDE_IPV4_LINK_LOCAL_ADDRESSES_CONFIG_KEY = "includeIPv4LinkLocalAddrs"; + static final String EXCLUDED_IP_ADDRESSES_CONFIG_KEY = "excludedIPAddresses"; static final String DEFAULT_PORT_CONFIG_KEY = "defaultPort"; static final boolean DEFAULT_INCLUDE_IPV4_LOOPBACK_ADDRESSES = false; static final boolean DEFAULT_INCLUDE_IPV4_LINK_LOCAL_ADDRESSES = false; @@ -27,6 +32,8 @@ public class Config { private AtomicInteger defaultPort = new AtomicInteger(DEFAULT_PORT); private AtomicBoolean includeIPv4LoopbackAddrs = new AtomicBoolean(DEFAULT_INCLUDE_IPV4_LOOPBACK_ADDRESSES); private AtomicBoolean includeIPv4LinkLocalAddrs = new AtomicBoolean(DEFAULT_INCLUDE_IPV4_LINK_LOCAL_ADDRESSES); + @Getter + private final List excludedIPAddresses = new ArrayList<>(); /** * Config constructor. @@ -39,6 +46,7 @@ public Config(Topics topics) { if (configurationTopics.isEmpty()) { this.includeIPv4LoopbackAddrs = new AtomicBoolean(DEFAULT_INCLUDE_IPV4_LOOPBACK_ADDRESSES); this.includeIPv4LinkLocalAddrs = new AtomicBoolean(DEFAULT_INCLUDE_IPV4_LINK_LOCAL_ADDRESSES); + this.excludedIPAddresses.clear(); this.defaultPort = new AtomicInteger(DEFAULT_PORT); return; } @@ -57,9 +65,20 @@ public Config(Topics topics) { Coerce.toInt( configurationTopics.findOrDefault(DEFAULT_PORT, DEFAULT_PORT_CONFIG_KEY))); + Topic excludedIPTopic = configurationTopics.find(EXCLUDED_IP_ADDRESSES_CONFIG_KEY); + if (excludedIPTopic != null) { + if (excludedIPTopic.getOnce() instanceof List) { + this.excludedIPAddresses.clear(); + this.excludedIPAddresses.addAll(Coerce.toStringList(excludedIPTopic.getOnce())); + } else { + logger.atWarn().kv("value", excludedIPTopic.getOnce()).log("Invalid config value for" + + " excludedIPAddresses. The config must be input as a list"); + } + } logger.atInfo().kv("includeIPv4LoopbackAddrs", includeIPv4LoopbackAddrs.get()) .kv("includeIPv4LinkLocalAddrs", includeIPv4LinkLocalAddrs.get()) + .kv("excludedIPAddresses", excludedIPAddresses) .kv("defaultPort", defaultPort.get()) .log("Configuration updated"); }); diff --git a/src/main/java/com/aws/greengrass/detector/detector/IpDetector.java b/src/main/java/com/aws/greengrass/detector/detector/IpDetector.java index 343dea6..5acae14 100644 --- a/src/main/java/com/aws/greengrass/detector/detector/IpDetector.java +++ b/src/main/java/com/aws/greengrass/detector/detector/IpDetector.java @@ -44,6 +44,9 @@ List getIpAddresses(Enumeration interfaces, Confi for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) { InetAddress address = interfaceAddress.getAddress(); + if (config.getExcludedIPAddresses().contains(address.getHostAddress())) { + continue; + } if (address instanceof Inet6Address) { continue; } diff --git a/src/test/java/com/aws/greengrass/detector/config/ConfigTest.java b/src/test/java/com/aws/greengrass/detector/config/ConfigTest.java index 50f6cfd..3b421f3 100644 --- a/src/test/java/com/aws/greengrass/detector/config/ConfigTest.java +++ b/src/test/java/com/aws/greengrass/detector/config/ConfigTest.java @@ -7,14 +7,20 @@ import com.aws.greengrass.config.ChildChanged; +import com.aws.greengrass.config.Topic; import com.aws.greengrass.config.Topics; import com.aws.greengrass.util.Coerce; +import com.aws.greengrass.utils.TestConstants; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.mockito.stubbing.Answer; +import java.util.Collections; +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -30,8 +36,11 @@ class ConfigTest { public void GIVEN_config_topics_WHEN_initialize_THEN_configuration_created() { Topics topics = Mockito.mock(Topics.class); Topics configTopics = Mockito.mock(Topics.class); + Topic excludedIpTopic = Mockito.mock(Topic.class); String mockIncludeIPv4LoopbackAddrsConfig = "true"; String mockIncludeIPv4LinkLocalAddrsConfig = "true"; + String mockExcludeIPsConfig = String.format("[%s]", TestConstants.IP_1); + List mockList = Collections.singletonList(TestConstants.IP_1); int mockPortValue = 9000; // stub subscribe() to call just the callback method without adding watcher @@ -49,6 +58,8 @@ public void GIVEN_config_topics_WHEN_initialize_THEN_configuration_created() { Mockito.doReturn(mockPortValue) .when(configTopics).findOrDefault(anyInt(), eq(Config.DEFAULT_PORT_CONFIG_KEY)); + Mockito.doReturn(excludedIpTopic).when(configTopics).find(Config.EXCLUDED_IP_ADDRESSES_CONFIG_KEY); + Mockito.doReturn(mockList).when(excludedIpTopic).getOnce(); Mockito.doReturn(configTopics).when(topics).lookupTopics(anyString()); config = new Config(topics); @@ -56,6 +67,7 @@ public void GIVEN_config_topics_WHEN_initialize_THEN_configuration_created() { assertEquals(mockPortValue, config.getDefaultPort()); assertEquals(Coerce.toBoolean(mockIncludeIPv4LoopbackAddrsConfig), config.isIncludeIPv4LoopbackAddrs()); assertEquals(Coerce.toBoolean(mockIncludeIPv4LinkLocalAddrsConfig), config.isIncludeIPv4LinkLocalAddrs()); + assertEquals(Coerce.toStringList(mockExcludeIPsConfig), config.getExcludedIPAddresses()); } @Test @@ -78,5 +90,29 @@ public void GIVEN_empty_config_topics_WHEN_initialize_THEN_default_configuration assertEquals(Config.DEFAULT_INCLUDE_IPV4_LOOPBACK_ADDRESSES, config.isIncludeIPv4LoopbackAddrs()); assertEquals(Config.DEFAULT_INCLUDE_IPV4_LINK_LOCAL_ADDRESSES, config.isIncludeIPv4LinkLocalAddrs()); assertEquals(Config.DEFAULT_PORT, config.getDefaultPort()); + assertTrue(config.getExcludedIPAddresses().isEmpty()); + } + + @Test + public void GIVEN_invalid_excluded_ips_list_WHEN_initialize_THEN_default_configuration_created() { + Topics topics = Mockito.mock(Topics.class); + Topics configTopics = Mockito.mock(Topics.class); + + // stub subscribe() to call just the callback method without adding watcher + doAnswer((Answer) invocation -> { + ChildChanged childChanged = invocation.getArgument(0); + childChanged.childChanged(null, null); + return null; + }).when(configTopics).subscribe(any()); + + Mockito.doReturn(false).when(configTopics).isEmpty(); + Mockito.doReturn(configTopics).when(topics).lookupTopics(anyString()); + Topic excludedIpTopic = Mockito.mock(Topic.class); + Mockito.doReturn(excludedIpTopic).when(configTopics).find(Config.EXCLUDED_IP_ADDRESSES_CONFIG_KEY); + Mockito.doReturn("bad-config").when(excludedIpTopic).getOnce(); + config = new Config(topics); + + assertNotNull(config); + assertTrue(config.getExcludedIPAddresses().isEmpty()); } } diff --git a/src/test/java/com/aws/greengrass/detector/detector/IpDetectorTest.java b/src/test/java/com/aws/greengrass/detector/detector/IpDetectorTest.java index b47caa1..38acc52 100644 --- a/src/test/java/com/aws/greengrass/detector/detector/IpDetectorTest.java +++ b/src/test/java/com/aws/greengrass/detector/detector/IpDetectorTest.java @@ -22,8 +22,10 @@ import java.util.Collections; import java.util.Enumeration; import java.util.List; +import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith({MockitoExtension.class}) @@ -98,6 +100,34 @@ public void GIVEN_network_down_WHEN_get_ipAddresses_THEN_null_returned() throws assertTrue(ipAddresses.isEmpty()); } + @Test + public void GIVEN_excludeIPAddresses_WHEN_get_ipAddresses_THEN_excludeIPs_filtered() throws SocketException { + // exclude IP_1 (0.61.124.18) + NetworkInterface networkInterface = Mockito.mock(NetworkInterface.class); + Config config = Mockito.mock(Config.class); + + List networkInterfaces = new ArrayList<>(); + List interfaceAddresses = getAllAddresses(); + + Mockito.doReturn(interfaceAddresses).when(networkInterface).getInterfaceAddresses(); + Mockito.doReturn(true).when(networkInterface).isUp(); + // Exclude IPv4 Loopback addresses and Link-Local addresses + Mockito.doReturn(true).when(config).isIncludeIPv4LoopbackAddrs(); + Mockito.doReturn(true).when(config).isIncludeIPv4LinkLocalAddrs(); + Mockito.doReturn(Collections.singletonList(TestConstants.IP_1)).when(config).getExcludedIPAddresses(); + + networkInterfaces.add(networkInterface); + Enumeration enumeration = Collections.enumeration(networkInterfaces); + ipDetector = new IpDetector(); + List ipAddresses = ipDetector.getIpAddresses(enumeration, config); + + assertEquals(2, ipAddresses.size()); + assertFalse(ipAddresses.stream().map(InetAddress::getHostAddress) + .collect(Collectors.joining()).contains(TestConstants.IP_1)); + assertEquals(TestConstants.IPV4_LOOPBACK, ipAddresses.get(0).getHostAddress()); + assertEquals(TestConstants.IPV4_LINK_LOCAL, ipAddresses.get(1).getHostAddress()); + } + private List getAllAddresses() { List interfaceAddresses = new ArrayList<>(); InterfaceAddress interfaceAddress1 = Mockito.mock(InterfaceAddress.class);