Allow privateips on console proxy (#6808)

Co-authored-by: Rodrigo D. Lopez <19981369+RodrigoDLopez@users.noreply.github.com>
Co-authored-by: Stephan Krug <stekrug@icloud.com>
Co-authored-by: Gabriel Ortiga Fernandes <gabriel.fernandes@scclouds.com.br>
This commit is contained in:
GaOrtiga 2022-12-22 12:43:45 -03:00 committed by GitHub
parent 9f8533eaf0
commit 91645349b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 366 additions and 81 deletions

View File

@ -244,7 +244,7 @@ public enum Config {
String.class,
"network.loadbalancer.haproxy.stats.auth",
"admin1:AdMiN123",
"Load Balancer(haproxy) authetication string in the format username:password",
"Load Balancer(haproxy) authentication string in the format username:password",
null),
NetworkLBHaproxyStatsPort(
"Network",
@ -426,7 +426,7 @@ public enum Config {
"Console proxy command port that is used to communicate with management server",
null),
ConsoleProxyRestart("Console Proxy", AgentManager.class, Boolean.class, "consoleproxy.restart", "true", "Console proxy restart flag, defaulted to true", null),
ConsoleProxyUrlDomain("Console Proxy", AgentManager.class, String.class, "consoleproxy.url.domain", "", "Console proxy url domain", "domainName"),
ConsoleProxyUrlDomain("Console Proxy", AgentManager.class, String.class, "consoleproxy.url.domain", "", "Console proxy url domain", "domainName", "privateip"),
ConsoleProxySessionMax(
"Console Proxy",
AgentManager.class,
@ -1783,7 +1783,7 @@ public enum Config {
private final String _name;
private final String _defaultValue;
private final String _description;
private final String _range;
private final String[] _range;
private final String _scope; // Parameter can be at different levels (Zone/cluster/pool/account), by default every parameter is at global
private static final HashMap<String, List<Config>> s_scopeLevelConfigsMap = new HashMap<String, List<Config>>();
@ -1833,7 +1833,7 @@ public enum Config {
}
}
private Config(String category, Class<?> componentClass, Class<?> type, String name, String defaultValue, String description, String range) {
private Config(String category, Class<?> componentClass, Class<?> type, String name, String defaultValue, String description, String... range) {
_category = category;
_componentClass = componentClass;
_type = type;
@ -1896,7 +1896,7 @@ public enum Config {
}
}
public String getRange() {
public String[] getRange() {
return _range;
}

View File

@ -1103,8 +1103,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
}
Class<?> type = null;
final Config c = Config.getConfig(name);
if (c == null) {
final Config configuration = Config.getConfig(name);
if (configuration == null) {
s_logger.warn("Did not find configuration " + name + " in Config.java. Perhaps moved to ConfigDepot");
final ConfigKey<?> configKey = _configDepot.get(name);
if(configKey == null) {
@ -1113,7 +1113,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
type = configKey.type();
} else {
type = c.getType();
type = configuration.getType();
}
//no need to validate further if a
//config can have null value.
@ -1222,96 +1222,145 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
}
if(c == null ) {
if (configuration == null ) {
//range validation has to be done per case basis, for now
//return in case of Configkey parameters
return null;
}
final String range = c.getRange();
final String[] range = configuration.getRange();
if (range == null) {
return null;
}
if (type.equals(String.class)) {
if (range.equals("privateip")) {
try {
if (!NetUtils.isSiteLocalAddress(value)) {
s_logger.error("privateip range " + value + " is not a site local address for configuration variable " + name);
return "Please enter a site local IP address.";
}
} catch (final NullPointerException e) {
s_logger.error("Error parsing ip address for " + name);
throw new InvalidParameterValueException("Error parsing ip address");
}
} else if (range.equals("netmask")) {
if (!NetUtils.isValidIp4Netmask(value)) {
s_logger.error("netmask " + value + " is not a valid net mask for configuration variable " + name);
return "Please enter a valid netmask.";
}
} else if (range.equals("hypervisorList")) {
final String[] hypervisors = value.split(",");
if (hypervisors == null) {
return "Please enter hypervisor list, separated by comma";
}
for (final String hypervisor : hypervisors) {
if (HypervisorType.getType(hypervisor) == HypervisorType.Any || HypervisorType.getType(hypervisor) == HypervisorType.None) {
return "Please enter a valid hypervisor type";
}
}
} else if (range.equalsIgnoreCase("instanceName")) {
if (!NetUtils.verifyInstanceName(value)) {
return "Instance name can not contain hyphen, space or plus sign";
}
} else if (range.equalsIgnoreCase("domainName")) {
String domainName = value;
if (value.startsWith("*")) {
domainName = value.substring(2); //skip the "*."
}
//max length for FQDN is 253 + 2, code adds xxx-xxx-xxx-xxx to domain name when creating URL
if (domainName.length() >= 238 || !domainName.matches(DOMAIN_NAME_PATTERN)) {
return "Please enter a valid string for domain name, prefixed with '*.' if applicable";
}
} else if (range.equals("routes")) {
final String[] routes = value.split(",");
for (final String route : routes) {
if (route != null) {
final String routeToVerify = route.trim();
if (!NetUtils.isValidIp4Cidr(routeToVerify)) {
throw new InvalidParameterValueException("Invalid value for route: " + route + " in deny list. Valid format is list"
+ " of cidrs separated by coma. Example: 10.1.1.0/24,192.168.0.0/24");
}
}
}
} else {
final String[] options = range.split(",");
for (final String option : options) {
if (option.trim().equalsIgnoreCase(value)) {
return null;
}
}
s_logger.error("configuration value for " + name + " is invalid");
return "Please enter : " + range;
}
return validateIfStringValueIsInRange(name, value, range);
} else if (type.equals(Integer.class)) {
final String[] options = range.split("-");
if (options.length != 2) {
final String msg = "configuration range " + range + " for " + name + " is invalid";
s_logger.error(msg);
return msg;
return validateIfIntValueIsInRange(name, value, range[0]);
}
return String.format("Invalid value for configuration [%s].", name);
}
/**
* A valid value should be an integer between min and max (the values from the range).
*/
protected String validateIfIntValueIsInRange(String name, String value, String range) {
final String[] options = range.split("-");
final int min = Integer.parseInt(options[0]);
final int max = Integer.parseInt(options[1]);
final int val = Integer.parseInt(value);
if (val < min || val > max) {
s_logger.error(String.format("Invalid value for configuration [%s]. Please enter a value in the range [%s].", name, range));
return String.format("The provided value is not valid for this configuration. Please enter an integer in the range: [%s]", range);
}
return null;
}
/**
* Checks if the value for the configuration is valid for any of the ranges selected.
*/
protected String validateIfStringValueIsInRange(String name, String value, String... range) {
List<String> message = new ArrayList<String>();
String errMessage = "";
for (String rangeOption : range) {
switch (rangeOption) {
case "privateip":
errMessage = validateRangePrivateIp(name, value);
break;
case "hypervisorList":
errMessage = validateRangeHypervisorList(value);
break;
case "instanceName":
errMessage = validateRangeInstanceName(value);
break;
case "domainName":
errMessage = validateRangeDomainName(value);
break;
default:
errMessage = validateRangeOther(name, value, rangeOption);
}
final int min = Integer.parseInt(options[0]);
final int max = Integer.parseInt(options[1]);
final int val = Integer.parseInt(value);
if (val < min || val > max) {
s_logger.error("configuration value for " + name + " is invalid");
return "Please enter : " + range;
if (StringUtils.isEmpty(errMessage)) {
return null;
}
message.add(errMessage);
}
if (message.size() == 1) {
return String.format("The provided value is not %s.", message.get(0));
}
return String.format("The provided value is neither %s.", String.join(" NOR ", message));
}
/**
* Checks if the value is a private IP according to {@link NetUtils#isSiteLocalAddress(String)}.
*/
protected String validateRangePrivateIp(String name, String value) {
try {
if (NetUtils.isSiteLocalAddress(value)) {
return null;
}
s_logger.error(String.format("Value [%s] is not a valid private IP range for configuration [%s].", value, name));
} catch (final NullPointerException e) {
s_logger.error(String.format("Error while parsing IP address for [%s].", name));
}
return "a valid site local IP address";
}
/**
* Valid values are XenServer, KVM, VMware, Hyperv, VirtualBox, Parralels, BareMetal, Simulator, Ovm, Ovm3, LXC.
* Inputting "Any" will return the hypervisor type Any, other inputs will result in the hypervisor type none.
* Both of these are invalid values and will return an error message.
*/
protected String validateRangeHypervisorList(String value) {
final String[] hypervisors = value.split(",");
for (final String hypervisor : hypervisors) {
if (HypervisorType.getType(hypervisor) == HypervisorType.Any || HypervisorType.getType(hypervisor) == HypervisorType.None) {
return "a valid hypervisor type";
}
}
return null;
}
/**
* Valid values are instance names, the only restriction is that they may not have hyphens, spaces or plus signs.
*/
protected String validateRangeInstanceName(String value) {
if (NetUtils.verifyInstanceName(value)) {
return null;
}
return "a valid instance name (instance names cannot contain hyphens, spaces or plus signs)";
}
/**
* Verifies if the value is a valid domain name. If it starts with "*.", these two symbols are ignored and do not count towards the character limit.
* Max length for FQDN is 253 + 2, code adds xxx-xxx-xxx-xxx to domain name when creating URL.
*/
protected String validateRangeDomainName(String value) {
String domainName = value;
if (value.startsWith("*")) {
domainName = value.substring(2);
}
if (domainName.length() >= 238 || !domainName.matches(DOMAIN_NAME_PATTERN)) {
return "a valid domain name";
}
return null;
}
/**
* In configurations where this type of range is used, a list of possible values is passed as argument in the creation of the configuration,
* a valid value is any option within this list.
*/
protected String validateRangeOther(String name, String value, String rangeOption) {
final String[] options = rangeOption.split(",");
for (final String option : options) {
if (option.trim().equalsIgnoreCase(value)) {
return null;
}
}
s_logger.error(String.format("Invalid value for configuration [%s].", name));
return String.format("a valid value for this configuration (Options are: [%s])", rangeOption);
}
private boolean podHasAllocatedPrivateIPs(final long podId) {
final HostPodVO pod = _podDao.findById(podId);
final int count = _privateIpAddressDao.countIPs(podId, pod.getDataCenterId(), true);

View File

@ -0,0 +1,42 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.configuration;
import org.junit.Test;
public class ConfigTest {
@Test
public void configTestIntegerRange() {
for (Config configuration : Config.values()) {
if (configuration.getType().equals(Integer.class) && configuration.getRange() != null) {
try {
final String[] options = configuration.getRange()[0].split("-");
final int min = Integer.parseInt(options[0]);
final int max = Integer.parseInt(options[1]);
if (options.length != 2) {
throw new AssertionError(String.format("Invalid range for configuration [%s], a valid value for the range should be two integers separated by [-].", configuration.toString()));
}
if (min > max) {
throw new AssertionError(String.format("Invalid range for configuration [%s], the second value should be greater than the first.", configuration.toString()));
}
} catch (java.lang.NumberFormatException e) {
throw new AssertionError(String.format("Invalid range for configuration [%s], a valid value for the range should be two integers separated by [-].", configuration.toString()));
}
}
}
}
}

View File

@ -0,0 +1,194 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.configuration;
import com.cloud.utils.net.NetUtils;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.List;
@RunWith(PowerMockRunner.class)
@PrepareForTest(NetUtils.class)
public class ConfigurationManagerImplTest {
ConfigurationManagerImpl configurationManagerImplSpy = Mockito.spy(new ConfigurationManagerImpl());
@Test
public void validateIfIntValueIsInRangeTestValidValueReturnNull() {
String testVariable = configurationManagerImplSpy.validateIfIntValueIsInRange("String name", "3", "1-5");
Assert.assertNull(testVariable);
}
@Test
public void validateIfIntValueIsInRangeTestInvalidValueReturnString() {
String testVariable = configurationManagerImplSpy.validateIfIntValueIsInRange("String name", "9", "1-5");
Assert.assertNotNull(testVariable);
}
@Test
public void validateIfStringValueIsInRangeTestValidValuesReturnNull() {
String testVariable = "";
List<String> methods = List.of("privateip", "hypervisorList", "instanceName", "domainName", "default");
Mockito.doReturn(null).when(configurationManagerImplSpy).validateRangePrivateIp(Mockito.anyString(), Mockito.anyString());
Mockito.doReturn(null).when(configurationManagerImplSpy).validateRangeHypervisorList(Mockito.anyString());
Mockito.doReturn(null).when(configurationManagerImplSpy).validateRangeInstanceName(Mockito.anyString());
Mockito.doReturn(null).when(configurationManagerImplSpy).validateRangeDomainName(Mockito.anyString());
Mockito.doReturn(null).when(configurationManagerImplSpy).validateRangeOther(Mockito.anyString(), Mockito.anyString(), Mockito.anyString());
for (String method : methods) {
testVariable = configurationManagerImplSpy.validateIfStringValueIsInRange("name", "value", method);
Assert.assertNull(testVariable);
}
}
@Test
public void validateIfStringValueIsInRangeTestInvalidValuesReturnString() {
String testVariable = "";
List<String> methods = List.of("privateip", "hypervisorList", "instanceName", "domainName", "default");
Mockito.doReturn("returnMsg").when(configurationManagerImplSpy).validateRangePrivateIp(Mockito.anyString(), Mockito.anyString());
Mockito.doReturn("returnMsg").when(configurationManagerImplSpy).validateRangeHypervisorList(Mockito.anyString());
Mockito.doReturn("returnMsg").when(configurationManagerImplSpy).validateRangeInstanceName(Mockito.anyString());
Mockito.doReturn("returnMsg").when(configurationManagerImplSpy).validateRangeDomainName(Mockito.anyString());
Mockito.doReturn("returnMsg").when(configurationManagerImplSpy).validateRangeOther(Mockito.anyString(), Mockito.anyString(), Mockito.anyString());
for (String method : methods) {
testVariable = configurationManagerImplSpy.validateIfStringValueIsInRange("name", "value", method);
Assert.assertEquals("The provided value is not returnMsg.", testVariable);
}
}
@Test
public void validateIfStringValueIsInRangeTestMultipleRangesValidValueReturnNull() {
Mockito.doReturn("returnMsg1").when(configurationManagerImplSpy).validateRangePrivateIp(Mockito.anyString(), Mockito.anyString());
Mockito.doReturn(null).when(configurationManagerImplSpy).validateRangeInstanceName(Mockito.anyString());
Mockito.doReturn("returnMsg2").when(configurationManagerImplSpy).validateRangeOther(Mockito.anyString(), Mockito.anyString(), Mockito.anyString());
String testVariable = configurationManagerImplSpy.validateIfStringValueIsInRange("name", "value", "privateip", "instanceName", "default");
Assert.assertNull(testVariable);
}
@Test
public void validateIfStringValueIsInRangeTestMultipleRangesInvalidValueReturnMessages() {
Mockito.doReturn("returnMsg1").when(configurationManagerImplSpy).validateRangePrivateIp(Mockito.anyString(), Mockito.anyString());
Mockito.doReturn("returnMsg2").when(configurationManagerImplSpy).validateRangeInstanceName(Mockito.anyString());
Mockito.doReturn("returnMsg3").when(configurationManagerImplSpy).validateRangeOther(Mockito.anyString(), Mockito.anyString(), Mockito.anyString());
String testVariable = configurationManagerImplSpy.validateIfStringValueIsInRange("name", "value", "privateip", "instanceName", "default");
Assert.assertEquals("The provided value is neither returnMsg1 NOR returnMsg2 NOR returnMsg3.", testVariable);
}
@Test
public void validateRangePrivateIpTestValidValueReturnNull() {
PowerMockito.mockStatic(NetUtils.class);
PowerMockito.when(NetUtils.isSiteLocalAddress(Mockito.anyString())).thenReturn(true);
String testVariable = configurationManagerImplSpy.validateRangePrivateIp("name", "value");
Assert.assertNull(testVariable);
}
@Test
public void validateRangePrivateIpTestInvalidValueReturnString() {
PowerMockito.mockStatic(NetUtils.class);
PowerMockito.when(NetUtils.isSiteLocalAddress(Mockito.anyString())).thenReturn(false);
String testVariable = configurationManagerImplSpy.validateRangePrivateIp("name", "value");
Assert.assertEquals("a valid site local IP address", testVariable);
}
@Test
public void validateRangeHypervisorListTestValidValueReturnNull() {
String testVariable = configurationManagerImplSpy.validateRangeHypervisorList("Ovm3,VirtualBox,KVM,VMware");
Assert.assertNull(testVariable);
}
@Test
public void validateRangeHypervisorListTestInvalidValueReturnString() {
String testVariable = configurationManagerImplSpy.validateRangeHypervisorList("Ovm3,VirtualBox,KVM,VMware,Any,InvalidHypervisorName");
Assert.assertEquals("a valid hypervisor type", testVariable);
}
@Test
public void validateRangeInstanceNameTestValidValueReturnNull() {
PowerMockito.mockStatic(NetUtils.class);
PowerMockito.when(NetUtils.verifyInstanceName(Mockito.anyString())).thenReturn(true);
String testVariable = configurationManagerImplSpy.validateRangeInstanceName("ThisStringShouldBeValid");
Assert.assertNull(testVariable);
}
@Test
public void validateRangeInstanceNameTestInvalidValueReturnString() {
PowerMockito.mockStatic(NetUtils.class);
PowerMockito.when(NetUtils.verifyInstanceName(Mockito.anyString())).thenReturn(false);
String testVariable = configurationManagerImplSpy.validateRangeInstanceName("This string should not be valid.");
Assert.assertEquals("a valid instance name (instance names cannot contain hyphen, space or plus sign)", testVariable);
}
@Test
public void validateRangeDomainNameTestValueDoesNotStartWithStarAndIsAValidValueReturnNull() {
String testVariable = configurationManagerImplSpy.validateRangeDomainName("ThisStringShould.Work");
Assert.assertNull(testVariable);
}
@Test
public void validateRangeDomainNameTestValueDoesNotStartWithStarAndIsAValidValueButIsOver238charactersLongReturnString() {
String testVariable = configurationManagerImplSpy.validateRangeDomainName("ThisStringDoesNotStartWithStarAndIsOverTwoHundredAndForty.CharactersLongWithAtLeast" +
"OnePeriodEverySixtyFourLetters.ThisShouldCauseAnErrorBecauseItIsTooLong.TheRestOfThisAreRandomlyGeneratedCharacters.gNXhNOBNTNAoMCQqJMzcvFSBwHUhmWHftjfTNUaHR");
Assert.assertEquals("a valid domain name", testVariable);
}
@Test
public void validateRangeDomainNameTestValueDoesNotStartWithStarAndIsNotAValidValueReturnString() {
String testVariable = configurationManagerImplSpy.validateRangeDomainName("ThisStringDoesNotMatchThePatternFor.DomainNamesSinceItHas1NumberInTheLastPartOfTheString");
Assert.assertEquals("a valid domain name", testVariable);
}
@Test
public void validateRangeDomainNameTestValueStartsWithStarAndIsAValidValueReturnNull() {
String testVariable = configurationManagerImplSpy.validateRangeDomainName("*.ThisStringStartsWithAStarAndAPeriod.ThisShouldWorkEvenThoughItIsOverTwoHundredAnd" +
"ThirtyEight.CharactersLong.BecauseTheFirstTwoCharactersAreIgnored.TheRestOfThisStringWasRandomlyGenerated.MgTUerXPlLyMaUpKTjAhxasFYRCfNCXmtWDwqSDOcTjASWlAXS");
Assert.assertNull(testVariable);
}
@Test
public void validateRangeDomainNameTestValueStartsWithStarAndIsAValidValueButIsOver238charactersLongReturnString() {
String testVariable = configurationManagerImplSpy.validateRangeDomainName("*.ThisStringStartsWithStarAndIsOverTwoHundredAndForty.CharactersLongWithAtLeastOnePeriod" +
"EverySixtyFourLetters.ThisShouldCauseAnErrorBecauseItIsTooLong.TheRestOfThisAreRandomlyGeneratedCharacters.gNXNOBNTNAoMChQqJMzcvFSBwHUhmWHftjfTNUaHRKVyXm");
Assert.assertEquals("a valid domain name", testVariable);
}
@Test
public void validateRangeDomainNameTestValueStartsWithStarAndIsNotAValidValueReturnString() {
String testVariable = configurationManagerImplSpy.validateRangeDomainName("*.ThisStringDoesNotMatchThePatternFor.DomainNamesSinceItHas1NumberInTheLastPartOfTheString");
Assert.assertEquals("a valid domain name", testVariable);
}
@Test
public void validateRangeOtherTestValidValueReturnNull() {
String testVariable = configurationManagerImplSpy.validateRangeOther("NameTest1", "SoShouldThis", "ThisShouldWork,ThisShouldAlsoWork,SoShouldThis");
Assert.assertNull(testVariable);
}
@Test
public void validateRangeOtherTestInvalidValueReturnString() {
String testVariable = configurationManagerImplSpy.validateRangeOther("NameTest1", "ThisShouldNotWork", "ThisShouldWork,ThisShouldAlsoWork,SoShouldThis");
Assert.assertNotNull(testVariable);
}
}