Skip to content

Commit 0b53714

Browse files
committed
Root CA: support multiple management network CIDRs with comma separated
1 parent 35ac91e commit 0b53714

2 files changed

Lines changed: 88 additions & 3 deletions

File tree

plugins/ca/root-ca/src/main/java/org/apache/cloudstack/ca/provider/RootCAProvider.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -401,15 +401,21 @@ private boolean loadManagementKeyStore() {
401401

402402
protected void addConfiguredManagementIp(List<String> ipList) {
403403
String msNetworkCidr = configDao.getValue(Config.ManagementNetwork.key());
404+
if (StringUtils.isEmpty(msNetworkCidr)) {
405+
return;
406+
}
404407
try {
405408
logger.debug(String.format("Trying to find management IP in CIDR range [%s].", msNetworkCidr));
406409
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
407410

408411
networkInterfaces.asIterator().forEachRemaining(networkInterface -> {
409412
networkInterface.getInetAddresses().asIterator().forEachRemaining(inetAddress -> {
410-
if (NetUtils.isIpWithInCidrRange(inetAddress.getHostAddress(), msNetworkCidr)) {
411-
ipList.add(inetAddress.getHostAddress());
412-
logger.debug(String.format("Added IP [%s] to the list of IPs in the management server's certificate.", inetAddress.getHostAddress()));
413+
String[] msNetworkCidrs = msNetworkCidr.split(",");
414+
for (String cidr : msNetworkCidrs) {
415+
if (NetUtils.isIpWithInCidrRange(inetAddress.getHostAddress(), cidr)) {
416+
ipList.add(inetAddress.getHostAddress());
417+
logger.debug(String.format("Added IP [%s] to the list of IPs in the management server's certificate.", inetAddress.getHostAddress()));
418+
}
413419
}
414420
});
415421
});

plugins/ca/root-ca/src/test/java/org/apache/cloudstack/ca/provider/RootCAProviderTest.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
package org.apache.cloudstack.ca.provider;
2121

2222
import java.lang.reflect.Field;
23+
import java.net.NetworkInterface;
24+
import java.net.SocketException;
2325
import java.security.InvalidKeyException;
2426
import java.security.KeyPair;
2527
import java.security.NoSuchAlgorithmException;
@@ -38,6 +40,7 @@
3840

3941
import org.apache.cloudstack.framework.ca.Certificate;
4042
import org.apache.cloudstack.framework.config.ConfigKey;
43+
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
4144
import org.apache.cloudstack.utils.security.CertUtils;
4245
import org.apache.cloudstack.utils.security.SSLUtils;
4346
import org.bouncycastle.asn1.x509.GeneralName;
@@ -47,10 +50,14 @@
4750
import org.junit.Before;
4851
import org.junit.Test;
4952
import org.junit.runner.RunWith;
53+
import org.mockito.MockedStatic;
5054
import org.mockito.Mockito;
5155
import org.mockito.junit.MockitoJUnitRunner;
5256
import org.springframework.test.util.ReflectionTestUtils;
5357

58+
import com.cloud.configuration.Config;
59+
import com.cloud.utils.exception.CloudRuntimeException;
60+
5461

5562
@RunWith(MockitoJUnitRunner.class)
5663
public class RootCAProviderTest {
@@ -208,4 +215,76 @@ public void testIsManagementCertificateMatch() {
208215
Assert.fail(String.format("Exception occurred: %s", e.getMessage()));
209216
}
210217
}
218+
219+
// ---------------------------------------------------------------
220+
// Tests for addConfiguredManagementIp
221+
// ---------------------------------------------------------------
222+
223+
private ConfigurationDao mockConfigDao(String cidr) throws Exception {
224+
ConfigurationDao mockDao = Mockito.mock(ConfigurationDao.class);
225+
Mockito.when(mockDao.getValue(Config.ManagementNetwork.key())).thenReturn(cidr);
226+
addField(provider, "configDao", mockDao);
227+
return mockDao;
228+
}
229+
230+
@Test
231+
public void testAddConfiguredManagementIpWithMatchingCidr() throws Exception {
232+
// 127.0.0.0/8 covers the loopback address (127.0.0.1) that is always
233+
// present on a Linux host, so the method must add it to the list.
234+
mockConfigDao("127.0.0.0/8");
235+
236+
List<String> ipList = new ArrayList<>();
237+
provider.addConfiguredManagementIp(ipList);
238+
239+
Assert.assertTrue("127.0.0.1 should be included for CIDR 127.0.0.0/8",
240+
ipList.contains("127.0.0.1"));
241+
}
242+
243+
@Test
244+
public void testAddConfiguredManagementIpWithNonMatchingCidr() throws Exception {
245+
// 192.0.2.0/24 is TEST-NET-1 (RFC 5737) and is never assigned to a real
246+
// interface, so nothing should be added to the list.
247+
mockConfigDao("192.0.2.0/24");
248+
249+
List<String> ipList = new ArrayList<>();
250+
provider.addConfiguredManagementIp(ipList);
251+
252+
Assert.assertTrue("No IP should be added when no interface matches the CIDR",
253+
ipList.isEmpty());
254+
}
255+
256+
@Test
257+
public void testAddConfiguredManagementIpWithMultipleCidrs() throws Exception {
258+
// First CIDR is a non-matching TEST-NET; second covers loopback.
259+
// The method splits on "," and checks each CIDR individually, so
260+
// 127.0.0.1 must still be found via the second CIDR.
261+
mockConfigDao("192.0.2.0/24,127.0.0.0/8");
262+
263+
List<String> ipList = new ArrayList<>();
264+
provider.addConfiguredManagementIp(ipList);
265+
266+
Assert.assertTrue("127.0.0.1 should be included when the second comma-separated CIDR matches",
267+
ipList.contains("127.0.0.1"));
268+
}
269+
270+
@Test
271+
public void testAddConfiguredManagementIpSocketException() throws Exception {
272+
mockConfigDao("127.0.0.0/8");
273+
274+
try (MockedStatic<NetworkInterface> networkInterfaceMock =
275+
Mockito.mockStatic(NetworkInterface.class)) {
276+
networkInterfaceMock.when(NetworkInterface::getNetworkInterfaces)
277+
.thenThrow(new SocketException("simulated network error"));
278+
279+
try {
280+
provider.addConfiguredManagementIp(new ArrayList<>());
281+
Assert.fail("Expected CloudRuntimeException to be thrown on SocketException");
282+
} catch (CloudRuntimeException e) {
283+
Assert.assertTrue("Exception message should describe the failure",
284+
e.getMessage().contains("Exception while trying to gather the management server's network interfaces."));
285+
Assert.assertTrue("Cause should be the original SocketException",
286+
e.getCause() instanceof SocketException);
287+
}
288+
}
289+
}
211290
}

0 commit comments

Comments
 (0)