diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/HddsServerUtil.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/HddsServerUtil.java index e9050346931..49a71025c76 100644 --- a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/HddsServerUtil.java +++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/HddsServerUtil.java @@ -86,7 +86,6 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.tuple.Pair; -import org.apache.commons.validator.routines.InetAddressValidator; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hdds.HddsConfigKeys; import org.apache.hadoop.hdds.HddsUtils; @@ -143,10 +142,6 @@ public final class HddsServerUtil { public static final String OZONE_RATIS_SNAPSHOT_COMPLETE_FLAG_NAME = "OZONE_RATIS_SNAPSHOT_COMPLETE"; - // List of ip's not recommended to be added to CSR. - private static final Set INVALID_IPS = new HashSet<>(Arrays.asList( - "0.0.0.0", "127.0.0.1")); - private HddsServerUtil() { } @@ -164,8 +159,9 @@ public static void addPBProtocol(Configuration conf, Class protocol, } /** - * Iterates through network interfaces and return all valid ip's not - * listed in {@link #INVALID_IPS}. + * Iterates through network interfaces and returns all IP addresses that are valid to + * add to a certificate's SAN extension, as determined by + * {@link #isValidInetForCsr(InetAddress)}. * * @return List * @throws IOException if no network interface are found or if an error @@ -174,7 +170,6 @@ public static void addPBProtocol(Configuration conf, Class protocol, public static List getValidInetsForCurrentHost() throws IOException { List hostIps = new ArrayList<>(); - InetAddressValidator ipValidator = InetAddressValidator.getInstance(); Enumeration enumNI = NetworkInterface.getNetworkInterfaces(); @@ -189,13 +184,11 @@ public static List getValidInetsForCurrentHost() while (enumAdds.hasMoreElements()) { InetAddress addr = enumAdds.nextElement(); - String hostAddress = addr.getHostAddress(); - if (!INVALID_IPS.contains(hostAddress) && ipValidator.isValid(hostAddress) - && !isScopedOrMaskingIPv6Address(addr)) { - LOG.info("Adding ip:{},host:{}", hostAddress, addr.getHostName()); + if (isValidInetForCsr(addr)) { + LOG.info("Adding ip:{},host:{}", addr.getHostAddress(), addr.getHostName()); hostIps.add(addr); } else { - LOG.info("ip:{} not returned.", hostAddress); + LOG.info("ip:{} not returned.", addr.getHostAddress()); } } } @@ -204,6 +197,24 @@ public static List getValidInetsForCurrentHost() return hostIps; } + /** + * Determines whether the supplied address is valid to add to a certificate's + * SAN extension. Wildcard/unspecified (0.0.0.0, ::) and loopback + * (127.0.0.0/8, ::1) addresses are excluded for both IPv4 and IPv6, along + * with scoped or masked IPv6 addresses (see + * {@link #isScopedOrMaskingIPv6Address(InetAddress)}). Using the + * {@link InetAddress} predicates rather than a fixed set of address strings + * ensures the IPv6 forms are excluded, not just their IPv4 equivalents. + * + * @param addr the InetAddress to check + * @return true if the address should be added to the CSR + */ + public static boolean isValidInetForCsr(InetAddress addr) { + return !addr.isAnyLocalAddress() + && !addr.isLoopbackAddress() + && !isScopedOrMaskingIPv6Address(addr); + } + /** * Determines if the supplied address is an IPv6 address, with a defined scope-id and/or with a defined prefix length. *

diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/TestHddsServerUtils.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/TestHddsServerUtils.java index 3d6e94e0a87..b19b59320c7 100644 --- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/TestHddsServerUtils.java +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/TestHddsServerUtils.java @@ -24,12 +24,15 @@ import static org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_NAMES; import static org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_STALENODE_INTERVAL; import static org.apache.hadoop.ozone.OzoneConsts.OZONE_SCM_DATANODE_ID_FILE_DEFAULT; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; +import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.util.concurrent.TimeUnit; import org.apache.commons.io.FileUtils; import org.apache.hadoop.hdds.HddsConfigKeys; @@ -190,6 +193,29 @@ public void testNoScmDbDirConfigured() { () -> ServerUtils.getScmDbDir(new OzoneConfiguration())); } + /** + * Wildcard and loopback addresses must be excluded from a certificate's SAN + * extension for both IPv4 and IPv6. See HDDS-9894. + */ + @Test + public void testInvalidInetsExcludedFromCsr() throws UnknownHostException { + // IPv4 wildcard and loopback + assertThat(HddsServerUtil.isValidInetForCsr(InetAddress.getByName("0.0.0.0"))).isFalse(); + assertThat(HddsServerUtil.isValidInetForCsr(InetAddress.getByName("127.0.0.1"))).isFalse(); + // IPv6 equivalents: unspecified (::) and loopback (::1) + assertThat(HddsServerUtil.isValidInetForCsr(InetAddress.getByName("::"))).isFalse(); + assertThat(HddsServerUtil.isValidInetForCsr(InetAddress.getByName("::1"))).isFalse(); + } + + /** + * Regular routable addresses stay eligible for the SAN extension. + */ + @Test + public void testValidInetsIncludedInCsr() throws UnknownHostException { + assertThat(HddsServerUtil.isValidInetForCsr(InetAddress.getByName("1.2.3.4"))).isTrue(); + assertThat(HddsServerUtil.isValidInetForCsr(InetAddress.getByName("2001:db8::1"))).isTrue(); + } + @Test public void testGetStaleNodeInterval() { final OzoneConfiguration conf = new OzoneConfiguration();