Add settings to mark cryptographic algorithms in vpn customer gateways as excluded or obsolete (#12193)

This PR introduces several configuration settings using which an operator can mark certain cryptographic algorithms and parameters as excluded or obsolete for VPN Customer Gateway creation for Site-to-Site VPN.

Cloud providers following modern security frameworks (e.g., ISO 27001/27017) are required to enforce and communicate approved cryptographic standards. CloudStack currently accepts several weak or deprecated algorithms without guidance to users. This PR closes that gap by giving operators explicit control over what is disallowed vs discouraged, improving security posture without breaking existing deployments.

These settings are:

1. vpn.customer.gateway.excluded.encryption.algorithms
2. vpn.customer.gateway.excluded.hashing.algorithms
3. vpn.customer.gateway.excluded.ike.versions
4. vpn.customer.gateway.excluded.dh.group
5. vpn.customer.gateway.obsolete.encryption.algorithms
6. vpn.customer.gateway.obsolete.hashing.algorithms
7. vpn.customer.gateway.obsolete.ike.versions
8. vpn.customer.gateway.obsolete.dh.group
This commit is contained in:
Abhisar Sinha 2026-01-19 13:18:37 +05:30 committed by GitHub
parent b31c2f4cae
commit 002d9768b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 2122 additions and 357 deletions

View File

@ -503,6 +503,7 @@ public class EventTypes {
public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_CREATE = "VPN.S2S.CUSTOMER.GATEWAY.CREATE";
public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_DELETE = "VPN.S2S.CUSTOMER.GATEWAY.DELETE";
public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_UPDATE = "VPN.S2S.CUSTOMER.GATEWAY.UPDATE";
public static final String EVENT_S2S_VPN_GATEWAY_OBSOLETE_PARAMS = "VPN.S2S.GATEWAY.OBSOLETE.PARAMS";
public static final String EVENT_S2S_VPN_CONNECTION_CREATE = "VPN.S2S.CONNECTION.CREATE";
public static final String EVENT_S2S_VPN_CONNECTION_DELETE = "VPN.S2S.CONNECTION.DELETE";
public static final String EVENT_S2S_VPN_CONNECTION_RESET = "VPN.S2S.CONNECTION.RESET";
@ -1151,6 +1152,7 @@ public class EventTypes {
entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_CREATE, Site2SiteCustomerGateway.class);
entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_DELETE, Site2SiteCustomerGateway.class);
entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_UPDATE, Site2SiteCustomerGateway.class);
entityEventDetails.put(EVENT_S2S_VPN_GATEWAY_OBSOLETE_PARAMS, Site2SiteCustomerGateway.class);
entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_CREATE, Site2SiteVpnConnection.class);
entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_DELETE, Site2SiteVpnConnection.class);
entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_RESET, Site2SiteVpnConnection.class);

View File

@ -74,6 +74,7 @@ public interface AlertService {
public static final AlertType ALERT_TYPE_VR_PUBLIC_IFACE_MTU = new AlertType((short)32, "ALERT.VR.PUBLIC.IFACE.MTU", true);
public static final AlertType ALERT_TYPE_VR_PRIVATE_IFACE_MTU = new AlertType((short)32, "ALERT.VR.PRIVATE.IFACE.MTU", true);
public static final AlertType ALERT_TYPE_EXTENSION_PATH_NOT_READY = new AlertType((short)33, "ALERT.TYPE.EXTENSION.PATH.NOT.READY", true);
public static final AlertType ALERT_TYPE_VPN_GATEWAY_OBSOLETE_PARAMETERS = new AlertType((short)34, "ALERT.S2S.VPN.GATEWAY.OBSOLETE.PARAMETERS", true);
public static final AlertType ALERT_TYPE_BACKUP_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_BACKUP_STORAGE, "ALERT.STORAGE.BACKUP", true);
public static final AlertType ALERT_TYPE_OBJECT_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_OBJECT_STORAGE, "ALERT.STORAGE.OBJECT", true);

View File

@ -1364,6 +1364,10 @@ public class ApiConstants {
public static final String RECURSIVE_DOMAINS = "recursivedomains";
public static final String VPN_CUSTOMER_GATEWAY_PARAMETERS = "vpncustomergatewayparameters";
public static final String OBSOLETE_PARAMETERS = "obsoleteparameters";
public static final String EXCLUDED_PARAMETERS = "excludedparameters";
/**
* This enum specifies IO Drivers, each option controls specific policies on I/O.
* Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0).

View File

@ -21,7 +21,9 @@ import java.util.Map;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.CapabilitiesResponse;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.config.ApiServiceConfiguration;
import com.cloud.user.Account;
@ -30,12 +32,22 @@ import com.cloud.user.Account;
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class ListCapabilitiesCmd extends BaseCmd {
@Parameter(name = ApiConstants.DOMAIN_ID,
type = CommandType.UUID,
entityType = DomainResponse.class,
description = "the domain for listing capabilities.",
since = "4.23.0")
private Long domainId;
@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
public Long getDomainId() {
return domainId;
}
@Override
public void execute() {
Map<String, Object> capabilities = _mgr.listCapabilities(this);
@ -76,6 +88,10 @@ public class ListCapabilitiesCmd extends BaseCmd {
response.setExtensionsPath((String)capabilities.get(ApiConstants.EXTENSIONS_PATH));
response.setDynamicScalingEnabled((Boolean) capabilities.get(ApiConstants.DYNAMIC_SCALING_ENABLED));
response.setAdditionalConfigEnabled((Boolean) capabilities.get(ApiConstants.ADDITONAL_CONFIG_ENABLED));
if (capabilities.containsKey(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS)) {
Map<String, Object> vpnCustomerGatewayParameters = (Map<String, Object>) capabilities.get(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS);
response.setVpnCustomerGatewayParameters(vpnCustomerGatewayParameters);
}
response.setObjectName("capability");
response.setResponseName(getCommandName());
this.setResponseObject(response);

View File

@ -16,6 +16,8 @@
// under the License.
package org.apache.cloudstack.api.response;
import java.util.Map;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
@ -153,6 +155,10 @@ public class CapabilitiesResponse extends BaseResponse {
@Param(description = "true if additional configurations or extraconfig can be passed to Instances", since = "4.20.2")
private Boolean additionalConfigEnabled;
@SerializedName(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS)
@Param(description = "Excluded and obsolete VPN customer gateway cryptographic parameters")
private Map<String, Object> vpnCustomerGatewayParameters;
public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) {
this.securityGroupsEnabled = securityGroupsEnabled;
}
@ -280,4 +286,8 @@ public class CapabilitiesResponse extends BaseResponse {
public void setAdditionalConfigEnabled(Boolean additionalConfigEnabled) {
this.additionalConfigEnabled = additionalConfigEnabled;
}
public void setVpnCustomerGatewayParameters(Map<String, Object> vpnCustomerGatewayParameters) {
this.vpnCustomerGatewayParameters = vpnCustomerGatewayParameters;
}
}

View File

@ -114,6 +114,14 @@ public class Site2SiteCustomerGatewayResponse extends BaseResponseWithAnnotation
@Param(description = "Which IKE Version to use, one of ike (autoselect), IKEv1, or IKEv2. Defaults to ike")
private String ikeVersion;
@SerializedName(ApiConstants.OBSOLETE_PARAMETERS)
@Param(description = "Contains the list of obsolete/insecure cryptographic parameters that the vpn customer gateway is using.", since = "4.23.0")
private String obsoleteParameters;
@SerializedName(ApiConstants.EXCLUDED_PARAMETERS)
@Param(description = "Contains the list of excluded/not allowed cryptographic parameters that the vpn customer gateway is using.", since = "4.23.0")
private String excludedParameters;
public void setId(String id) {
this.id = id;
}
@ -202,4 +210,12 @@ public class Site2SiteCustomerGatewayResponse extends BaseResponseWithAnnotation
this.domainPath = domainPath;
}
public void setContainsObsoleteParameters(String obsoleteParameters) {
this.obsoleteParameters = obsoleteParameters;
}
public void setContainsExcludedParameters(String excludedParameters) {
this.excludedParameters = excludedParameters;
}
}

View File

@ -66,7 +66,7 @@ public class ConfigKeyScheduledExecutionWrapper implements Runnable {
this.unit = unit;
}
protected ConfigKeyScheduledExecutionWrapper(ScheduledExecutorService executorService, Runnable command,
public ConfigKeyScheduledExecutionWrapper(ScheduledExecutorService executorService, Runnable command,
ConfigKey<?> configKey, int enableIntervalSeconds, TimeUnit unit) {
validateArgs(executorService, command, configKey);
this.executorService = executorService;

View File

@ -112,7 +112,8 @@ public class AlertManagerImpl extends ManagerBase implements AlertManager, Confi
, AlertType.ALERT_TYPE_OOBM_AUTH_ERROR
, AlertType.ALERT_TYPE_HA_ACTION
, AlertType.ALERT_TYPE_CA_CERT
, AlertType.ALERT_TYPE_EXTENSION_PATH_NOT_READY);
, AlertType.ALERT_TYPE_EXTENSION_PATH_NOT_READY
, AlertType.ALERT_TYPE_VPN_GATEWAY_OBSOLETE_PARAMETERS);
private static final long INITIAL_CAPACITY_CHECK_DELAY = 30L * 1000L; // Thirty seconds expressed in milliseconds.

View File

@ -50,6 +50,7 @@ import com.cloud.dc.dao.ASNumberRangeDao;
import com.cloud.dc.dao.VlanDetailsDao;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.network.vpc.VpcGateway;
import com.cloud.network.vpn.Site2SiteVpnManager;
import com.cloud.storage.BucketVO;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
@ -528,6 +529,8 @@ public class ApiResponseHelper implements ResponseGenerator {
@Inject
RoutedIpv4Manager routedIpv4Manager;
@Inject
Site2SiteVpnManager site2SiteVpnManager;
@Inject
ResourceIconManager resourceIconManager;
public static String getPrettyDomainPath(String path) {
@ -3884,6 +3887,16 @@ public class ApiResponseHelper implements ResponseGenerator {
response.setRemoved(result.getRemoved());
response.setIkeVersion(result.getIkeVersion());
response.setSplitConnections(result.getSplitConnections());
Set<String> obsoleteParameters = site2SiteVpnManager.getObsoleteVpnGatewayParameters(result);
if (CollectionUtils.isNotEmpty(obsoleteParameters)) {
response.setContainsObsoleteParameters(obsoleteParameters.toString());
}
Set<String> excludedParameters = site2SiteVpnManager.getExcludedVpnGatewayParameters(result);
if (CollectionUtils.isNotEmpty(excludedParameters)) {
response.setContainsExcludedParameters(excludedParameters.toString());
}
response.setObjectName("vpncustomergateway");
response.setHasAnnotation(annotationDao.hasAnnotations(result.getUuid(), AnnotationService.EntityType.VPN_CUSTOMER_GATEWAY.name(),
_accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId())));

View File

@ -17,11 +17,17 @@
package com.cloud.network.vpn;
import java.util.List;
import java.util.Set;
import com.cloud.network.Site2SiteCustomerGateway;
import com.cloud.network.dao.Site2SiteVpnConnectionVO;
import com.cloud.vm.DomainRouterVO;
public interface Site2SiteVpnManager extends Site2SiteVpnService {
Set<String> getExcludedVpnGatewayParameters(Site2SiteCustomerGateway customerGw);
Set<String> getObsoleteVpnGatewayParameters(Site2SiteCustomerGateway customerGw);
boolean cleanupVpnConnectionByVpc(long vpcId);
boolean cleanupVpnGatewayByVpc(long vpcId);

View File

@ -17,15 +17,22 @@
package com.cloud.network.vpn;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;
import org.apache.cloudstack.alert.AlertService;
import org.apache.cloudstack.annotation.AnnotationService;
import org.apache.cloudstack.annotation.dao.AnnotationDao;
import org.apache.cloudstack.api.command.user.vpn.CreateVpnConnectionCmd;
@ -41,9 +48,16 @@ import org.apache.cloudstack.api.command.user.vpn.ResetVpnConnectionCmd;
import org.apache.cloudstack.api.command.user.vpn.UpdateVpnCustomerGatewayCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.ConfigKeyScheduledExecutionWrapper;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.alert.AlertManager;
import com.cloud.configuration.Config;
import com.cloud.event.ActionEvent;
import com.cloud.event.ActionEventUtils;
import com.cloud.event.EventTypes;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
@ -72,9 +86,11 @@ import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
import com.cloud.projects.Project.ListProjectResourcesCriteria;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.User;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.StringUtils;
import com.cloud.utils.Ternary;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.db.DB;
@ -88,7 +104,52 @@ import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.dao.DomainRouterDao;
@Component
public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpnManager {
public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpnManager, Configurable {
// Configuration keys for VPN gateway cryptographic parameter controls
public static final ConfigKey<String> VpnCustomerGatewayExcludedEncryptionAlgorithms = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.excluded.encryption.algorithms", "",
"Comma-separated list of encryption algorithms that are excluded and cannot be selected by end users for VPN Customer Gateways." +
"Applies to both IKE and ESP phases. Allowed values are aes128, aes192 and aes256 and 3des.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<String> VpnCustomerGatewayExcludedHashingAlgorithms = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.excluded.hashing.algorithms", "",
"Comma-separated list of hashing algorithms that are excluded and cannot be selected by end users for VPN Customer Gateways." +
"Applies to both IKE and ESP phases. Allowed values are sha1, sha256, sha384 and sha512 and md5.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<String> VpnCustomerGatewayExcludedIkeVersions = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.excluded.ike.versions", "",
"Comma-separated list of IKE versions that are excluded and cannot be selected by end users for VPN Customer Gateways. Allowed values are ikev, ikev1 and ikev2.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<String> VpnCustomerGatewayExcludedDhGroup = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.excluded.dh.group", "",
"Comma-separated list of Diffie-Hellman groups that are excluded and cannot be selected by end users for VPN Customer Gateways." +
"Applies to both IKE and ESP phases. Allowed values are modp1024, modp1536, modp2048, modp3072, modp4096, modp6144 and modp8192.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<String> VpnCustomerGatewayObsoleteEncryptionAlgorithms = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.obsolete.encryption.algorithms", "",
"Comma-separated list of encryption algorithms that are marked as obsolete/insecure for VPN Customer Gateways." +
"Applies to both IKE and ESP phases. Allowed values are aes128, aes192 and aes256 and 3des.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<String> VpnCustomerGatewayObsoleteHashingAlgorithms = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.obsolete.hashing.algorithms", "",
"Comma-separated list of hashing algorithms that are marked as obsolete/insecure for VPN Customer Gateways." +
"Applies to both IKE and ESP phases. Allowed values are sha1, sha256, sha384 and sha512 and md5.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<String> VpnCustomerGatewayObsoleteIkeVersions = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.obsolete.ike.versions", "",
"Comma-separated list of IKE versions that are marked as obsolete/insecure for VPN Customer Gateways. Allowed values are ikev, ikev1 and ikev2.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<String> VpnCustomerGatewayObsoleteDhGroup = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.obsolete.dh.group", "",
"Comma-separated list of Diffie-Hellman groups that are marked as obsolete/insecure for VPN Customer Gateways." +
"Applies to both IKE and ESP phases. Allowed values are modp1024, modp1536, modp2048, modp3072, modp4096, modp6144 and modp8192.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<Long> VpnCustomerGatewayObsoleteCheckInterval = new ConfigKey<Long>(
ConfigKey.CATEGORY_NETWORK, Long.class, "vpn.customer.gateway.obsolete.check.interval", "0",
"Interval in hours to periodically check VPN customer gateways for obsolete/excluded parameters and generate events and alerts. " +
"Set to 0 to disable. Default: 0 (disabled).",
true, ConfigKey.Scope.Global);
List<Site2SiteVpnServiceProvider> _s2sProviders;
@Inject
@ -117,9 +178,12 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
private IpAddressManager ipAddressManager;
@Inject
private VpcManager vpcManager;
@Inject
private AlertManager _alertMgr;
int _connLimit;
int _subnetsLimit;
private ScheduledExecutorService _vpnCheckExecutor;
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
@ -127,6 +191,7 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
_connLimit = NumbersUtil.parseInt(configs.get(Config.Site2SiteVpnConnectionPerVpnGatewayLimit.key()), 4);
_subnetsLimit = NumbersUtil.parseInt(configs.get(Config.Site2SiteVpnSubnetsPerCustomerGatewayLimit.key()), 10);
assert (_s2sProviders.iterator().hasNext()) : "Did not get injected with a list of S2S providers!";
_vpnCheckExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("VpnCustomerGateway-ExcludedAndObsoleteCheck"));
return true;
}
@ -146,7 +211,7 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
}
Site2SiteVpnGatewayVO gws = _vpnGatewayDao.findByVpcId(vpcId);
if (gws != null) {
throw new InvalidParameterValueException(String.format("The VPN gateway of VPC %s already existed!", vpc));
throw new InvalidParameterValueException(String.format("The VPN gateway of VPC %s already exists!", vpc));
}
IPAddressVO requestedIp = _ipAddressDao.findById(cmd.getIpAddressId());
@ -187,6 +252,113 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
}
}
private void validateVpnCryptographicParameters(String ikePolicy, String espPolicy, String ikeVersion, Long domainId) {
String excludedEncryption = VpnCustomerGatewayExcludedEncryptionAlgorithms.valueIn(domainId);
String excludedHashing = VpnCustomerGatewayExcludedHashingAlgorithms.valueIn(domainId);
String excludedIkeVersions = VpnCustomerGatewayExcludedIkeVersions.valueIn(domainId);
String excludedDhGroup = VpnCustomerGatewayExcludedDhGroup.valueIn(domainId);
Set<String> excludedParameters = getVpnGatewayParametersInBlockedList(ikePolicy, espPolicy, ikeVersion,
excludedEncryption, excludedHashing, excludedIkeVersions, excludedDhGroup);
if (!excludedParameters.isEmpty()) {
throw new InvalidParameterValueException("The following excluded cryptographic parameter(s) cannot be used in a VPN Customer Gateway: " + excludedParameters.toString());
}
}
@Override
public Set<String> getExcludedVpnGatewayParameters(Site2SiteCustomerGateway customerGw) {
Long domainId = customerGw.getDomainId();
String excludedEncryption = VpnCustomerGatewayExcludedEncryptionAlgorithms.valueIn(domainId);
String excludedHashing = VpnCustomerGatewayExcludedHashingAlgorithms.valueIn(domainId);
String excludedIkeVersions = VpnCustomerGatewayExcludedIkeVersions.valueIn(domainId);
String excludedDhGroup = VpnCustomerGatewayExcludedDhGroup.valueIn(domainId);
return getVpnGatewayParametersInBlockedList(customerGw.getIkePolicy(), customerGw.getEspPolicy(), customerGw.getIkeVersion(),
excludedEncryption, excludedHashing, excludedIkeVersions, excludedDhGroup);
}
@Override
public Set<String> getObsoleteVpnGatewayParameters(Site2SiteCustomerGateway customerGw) {
Long domainId = customerGw.getDomainId();
String obsoleteEncryption = VpnCustomerGatewayObsoleteEncryptionAlgorithms.valueIn(domainId);
String obsoleteHashing = VpnCustomerGatewayObsoleteHashingAlgorithms.valueIn(domainId);
String obsoleteIkeVersions = VpnCustomerGatewayObsoleteIkeVersions.valueIn(domainId);
String obsoleteDhGroup = VpnCustomerGatewayObsoleteDhGroup.valueIn(domainId);
return getVpnGatewayParametersInBlockedList(customerGw.getIkePolicy(), customerGw.getEspPolicy(), customerGw.getIkeVersion(),
obsoleteEncryption, obsoleteHashing, obsoleteIkeVersions, obsoleteDhGroup);
}
private Set<String> getVpnGatewayParametersInBlockedList(String ikePolicy, String espPolicy, String ikeVersion,
String blockedEncryptionList, String blockedHashingList,
String blockedIkeVersionList, String blockedDhGroupList) {
Set<String> blockedParameters = new HashSet<>();
if (StringUtils.isEmpty(blockedEncryptionList)
&& StringUtils.isEmpty(blockedHashingList)
&& StringUtils.isEmpty(blockedIkeVersionList)
&& StringUtils.isEmpty(blockedDhGroupList)) {
return blockedParameters;
}
if (isParameterInList(ikeVersion, blockedIkeVersionList)) {
blockedParameters.add(ikeVersion);
}
Set<String> ikePolicyResult = getVpnGatewayPolicyParametersInBlockedList(ikePolicy, "IKE", blockedEncryptionList, blockedHashingList, blockedDhGroupList);
if (CollectionUtils.isNotEmpty(ikePolicyResult)) {
blockedParameters.addAll(ikePolicyResult);
}
Set<String> espPolicyResult = getVpnGatewayPolicyParametersInBlockedList(espPolicy, "ESP", blockedEncryptionList, blockedHashingList, blockedDhGroupList);
if (CollectionUtils.isNotEmpty(espPolicyResult)) {
blockedParameters.addAll(espPolicyResult);
}
return blockedParameters;
}
private Set<String> getVpnGatewayPolicyParametersInBlockedList(String policy, String policyType, String blockedEncryptionList, String blockedHashingList, String blockedDhGroupList) {
String trimmedPolicy = policy.trim();
String cipherHash = trimmedPolicy.split(";")[0];
String[] parts = cipherHash.split("-");
String encryption = parts[0].trim();
String hashing = parts.length > 1 ? parts[1].trim() : "";
Set<String> blockedParameters = new HashSet<>();
if (isParameterInList(encryption, blockedEncryptionList)) {
blockedParameters.add(encryption);
}
if (isParameterInList(hashing, blockedHashingList)) {
blockedParameters.add(hashing);
}
if (!trimmedPolicy.equals(cipherHash)) {
String dhGroup = trimmedPolicy.split(";")[1].trim();
if (isParameterInList(dhGroup, blockedDhGroupList)) {
blockedParameters.add(dhGroup);
}
}
return blockedParameters;
}
private boolean isParameterInList(String parameter, String list) {
if (StringUtils.isEmpty(list) || StringUtils.isEmpty(parameter)) {
return false;
}
String[] entries = list.split(",");
for (String item : entries) {
if (item != null && item.trim().equalsIgnoreCase(parameter.trim())) {
return true;
}
}
return false;
}
protected void checkCustomerGatewayCidrList(String guestCidrList) {
String[] cidrList = guestCidrList.split(",");
if (cidrList.length > _subnetsLimit) {
@ -235,6 +407,13 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
if (!NetUtils.isValidS2SVpnPolicy("esp", espPolicy)) {
throw new InvalidParameterValueException("The customer gateway ESP policy " + espPolicy + " is invalid!");
}
String ikeVersion = cmd.getIkeVersion();
if (ikeVersion == null) {
ikeVersion = "ike";
}
validateVpnCryptographicParameters(ikePolicy, espPolicy, ikeVersion, owner.getDomainId());
Long ikeLifetime = cmd.getIkeLifetime();
if (ikeLifetime == null) {
// Default value of lifetime is 1 day
@ -264,7 +443,7 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
long accountId = owner.getAccountId();
if (_customerGatewayDao.findByNameAndAccountId(name, accountId) != null) {
throw new InvalidParameterValueException("The customer gateway with name " + name + " already existed!");
throw new InvalidParameterValueException("The customer gateway with name " + name + " already exists!");
}
Boolean splitConnections = cmd.getSplitConnections();
@ -272,11 +451,6 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
splitConnections = false;
}
String ikeVersion = cmd.getIkeVersion();
if (ikeVersion == null) {
ikeVersion = "ike";
}
checkCustomerGatewayCidrList(peerCidrList);
Site2SiteCustomerGatewayVO gw =
@ -374,7 +548,7 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
private void validateVpnConnectionDoesntExist(Site2SiteCustomerGateway customerGateway, Site2SiteVpnGateway vpnGateway) {
if (_vpnConnectionDao.findByVpnGatewayIdAndCustomerGatewayId(vpnGateway.getId(), customerGateway.getId()) != null) {
throw new InvalidParameterValueException(String.format("The vpn connection with customer gateway %s and vpn gateway %s already existed!", customerGateway, vpnGateway));
throw new InvalidParameterValueException(String.format("The vpn connection with customer gateway %s and vpn gateway %s already exists!", customerGateway, vpnGateway));
}
}
@ -521,6 +695,10 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
if (!NetUtils.isValidS2SVpnPolicy("esp", espPolicy)) {
throw new InvalidParameterValueException("The customer gateway ESP policy" + espPolicy + " is invalid!");
}
String ikeVersion = cmd.getIkeVersion();
validateVpnCryptographicParameters(ikePolicy, espPolicy, ikeVersion, gw.getDomainId());
Long ikeLifetime = cmd.getIkeLifetime();
if (ikeLifetime == null) {
// Default value of lifetime is 1 day
@ -550,14 +728,12 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
Boolean splitConnections = cmd.getSplitConnections();
String ikeVersion = cmd.getIkeVersion();
checkCustomerGatewayCidrList(guestCidrList);
long accountId = gw.getAccountId();
Site2SiteCustomerGatewayVO existedGw = _customerGatewayDao.findByNameAndAccountId(name, accountId);
if (existedGw != null && existedGw.getId() != gw.getId()) {
throw new InvalidParameterValueException("The customer gateway with name " + name + " already existed!");
throw new InvalidParameterValueException("The customer gateway with name " + name + " already exists!");
}
gw.setName(name);
@ -977,4 +1153,83 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
return _vpnGatewayDao.findById(id);
}
@Override
public boolean start() {
ConfigKeyScheduledExecutionWrapper runner = new ConfigKeyScheduledExecutionWrapper(
_vpnCheckExecutor,
new CheckVpnCustomerGatewayObsoleteParametersTask(),
VpnCustomerGatewayObsoleteCheckInterval,
3600,
TimeUnit.HOURS);
runner.start();
return true;
}
@Override
public boolean stop() {
if (_vpnCheckExecutor != null) {
_vpnCheckExecutor.shutdownNow();
}
return true;
}
@Override
public String getConfigComponentName() {
return Site2SiteVpnManager.class.getSimpleName();
}
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] { VpnCustomerGatewayExcludedEncryptionAlgorithms, VpnCustomerGatewayExcludedHashingAlgorithms,
VpnCustomerGatewayExcludedIkeVersions, VpnCustomerGatewayExcludedDhGroup, VpnCustomerGatewayObsoleteEncryptionAlgorithms,
VpnCustomerGatewayObsoleteHashingAlgorithms, VpnCustomerGatewayObsoleteIkeVersions, VpnCustomerGatewayObsoleteDhGroup,
VpnCustomerGatewayObsoleteCheckInterval};
}
protected class CheckVpnCustomerGatewayObsoleteParametersTask extends ManagedContextRunnable {
@Override
protected void runInContext() {
List<Site2SiteCustomerGatewayVO> allGateways = _customerGatewayDao.listAll();
int obsoleteCount = 0;
int excludedCount = 0;
for (Site2SiteCustomerGatewayVO gateway : allGateways) {
Set<String> excludedParameters = getExcludedVpnGatewayParameters(gateway);
Set<String> obsoleteParameters = getObsoleteVpnGatewayParameters(gateway);
List<String> message = new ArrayList<>();
if (CollectionUtils.isNotEmpty(excludedParameters)) {
excludedCount++;
message.add("excluded parameter(s) " + excludedParameters.toString());
}
if (CollectionUtils.isNotEmpty(obsoleteParameters)) {
obsoleteCount++;
message.add("obsolete parameter(s) " + obsoleteParameters.toString());
}
if (CollectionUtils.isNotEmpty(message)) {
Account account = _accountDao.findById(gateway.getAccountId());
String description = String.format("VPN customer gateway '%s' (Account: %s) contains %s.",
gateway.getName(), account.getAccountName(), String.join(" and ", message));
ActionEventUtils.onActionEvent(User.UID_SYSTEM, gateway.getAccountId(), gateway.getDomainId(),
EventTypes.EVENT_S2S_VPN_GATEWAY_OBSOLETE_PARAMS, description,
gateway.getId(), Site2SiteCustomerGateway.class.getSimpleName());
}
}
List<String> message = new ArrayList<>();
if (excludedCount > 0) {
message.add("excluded parameters: " + excludedCount);
}
if (obsoleteCount > 0) {
message.add("obsolete parameters: " + obsoleteCount);
}
if (CollectionUtils.isNotEmpty(message)) {
String subject = String.format("VPN customer gateways using " + String.join(", ", message));
_alertMgr.sendAlert(AlertService.AlertType.ALERT_TYPE_VPN_GATEWAY_OBSOLETE_PARAMETERS, 0L, 0L, subject, null);
}
}
}
}

View File

@ -720,6 +720,7 @@ import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentPlanner;
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.deploy.DeploymentPlanningManager;
import com.cloud.domain.Domain;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.event.ActionEvent;
@ -761,6 +762,7 @@ import com.cloud.network.IpAddressManagerImpl;
import com.cloud.network.Network;
import com.cloud.network.NetworkModel;
import com.cloud.network.Networks;
import com.cloud.network.vpn.Site2SiteVpnManagerImpl;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.LoadBalancerDao;
@ -4778,6 +4780,14 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
final Map<String, Object> capabilities = new HashMap<>();
final Account caller = getCaller();
Long domainId = cmd.getDomainId();
if (domainId == null) {
domainId = caller.getDomainId();
} else {
Domain domain = _domainDao.findById(domainId);
_accountService.checkAccess(caller, domain);
}
final boolean isCallerRootAdmin = _accountService.isRootAdmin(caller.getId());
final boolean isCallerAdmin = isCallerRootAdmin || _accountService.isAdmin(caller.getId());
boolean securityGroupsEnabled = false;
@ -4812,7 +4822,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
final boolean allowUserExpungeRecoverVolume = (VolumeApiServiceImpl.AllowUserExpungeRecoverVolume.valueIn(caller.getId()) | isCallerAdmin);
final boolean allowUserForceStopVM = (UserVmManager.AllowUserForceStopVm.valueIn(caller.getId()) | isCallerAdmin);
final boolean allowUserViewAllDomainAccounts = (QueryService.AllowUserViewAllDomainAccounts.valueIn(caller.getDomainId()));
final boolean allowUserViewAllDomainAccounts = (QueryService.AllowUserViewAllDomainAccounts.valueIn(domainId));
final boolean kubernetesServiceEnabled = Boolean.parseBoolean(_configDao.getValue("cloud.kubernetes.service.enabled"));
final boolean kubernetesClusterExperimentalFeaturesEnabled = Boolean.parseBoolean(_configDao.getValue("cloud.kubernetes.cluster.experimental.features.enabled"));
@ -4865,9 +4875,53 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
}
capabilities.put(ApiConstants.ADDITONAL_CONFIG_ENABLED, UserVmManager.EnableAdditionalVmConfig.valueIn(caller.getId()));
Map<String, Object> vpnParams = getVpnCustomerGatewayParameters(domainId);
if (!vpnParams.isEmpty()) {
capabilities.put(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS, vpnParams);
}
return capabilities;
}
private Map<String, Object> getVpnCustomerGatewayParameters(Long domainId) {
Map<String, Object> vpnParams = new HashMap<>();
String excludedEncryption = Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms.valueIn(domainId);
String excludedHashing = Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms.valueIn(domainId);
String excludedIkeVersions = Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions.valueIn(domainId);
String excludedDhGroup = Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup.valueIn(domainId);
String obsoleteEncryption = Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms.valueIn(domainId);
String obsoleteHashing = Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms.valueIn(domainId);
String obsoleteIkeVersions = Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions.valueIn(domainId);
String obsoleteDhGroup = Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup.valueIn(domainId);
if (!excludedEncryption.isEmpty()) {
vpnParams.put("excludedencryptionalgorithms", excludedEncryption);
}
if (!obsoleteEncryption.isEmpty()) {
vpnParams.put("obsoleteencryptionalgorithms", obsoleteEncryption);
}
if (!excludedHashing.isEmpty()) {
vpnParams.put("excludedhashingalgorithms", excludedHashing);
}
if (!obsoleteHashing.isEmpty()) {
vpnParams.put("obsoletehashingalgorithms", obsoleteHashing);
}
if (!excludedIkeVersions.isEmpty()) {
vpnParams.put("excludedikeversions", excludedIkeVersions);
}
if (!obsoleteIkeVersions.isEmpty()) {
vpnParams.put("obsoleteikeversions", obsoleteIkeVersions);
}
if (!excludedDhGroup.isEmpty()) {
vpnParams.put("excludeddhgroups", excludedDhGroup);
}
if (!obsoleteDhGroup.isEmpty()) {
vpnParams.put("obsoletedhgroups", obsoleteDhGroup);
}
return vpnParams;
}
@Override
public GuestOSVO getGuestOs(final Long guestOsId) {
return _guestOSDao.findById(guestOsId);

View File

@ -0,0 +1,944 @@
/*
* 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.network.vpn;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.Site2SiteVpnConnection;
import com.cloud.network.Site2SiteVpnConnection.State;
import com.cloud.network.Site2SiteVpnGateway;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.Site2SiteCustomerGatewayDao;
import com.cloud.network.dao.Site2SiteCustomerGatewayVO;
import com.cloud.network.dao.Site2SiteVpnConnectionDao;
import com.cloud.network.dao.Site2SiteVpnConnectionVO;
import com.cloud.network.dao.Site2SiteVpnGatewayDao;
import com.cloud.network.dao.Site2SiteVpnGatewayVO;
import com.cloud.network.element.Site2SiteVpnServiceProvider;
import com.cloud.network.vpc.VpcManager;
import com.cloud.network.vpc.VpcVO;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.User;
import com.cloud.user.UserVO;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.DomainRouterVO;
import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.annotation.dao.AnnotationDao;
import org.apache.cloudstack.api.command.user.vpn.CreateVpnConnectionCmd;
import org.apache.cloudstack.api.command.user.vpn.CreateVpnCustomerGatewayCmd;
import org.apache.cloudstack.api.command.user.vpn.CreateVpnGatewayCmd;
import org.apache.cloudstack.api.command.user.vpn.DeleteVpnConnectionCmd;
import org.apache.cloudstack.api.command.user.vpn.DeleteVpnCustomerGatewayCmd;
import org.apache.cloudstack.api.command.user.vpn.DeleteVpnGatewayCmd;
import org.apache.cloudstack.api.command.user.vpn.ResetVpnConnectionCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.Silent.class)
public class Site2SiteVpnManagerImplTest {
@Mock
private Site2SiteCustomerGatewayDao _customerGatewayDao;
@Mock
private Site2SiteVpnGatewayDao _vpnGatewayDao;
@Mock
private Site2SiteVpnConnectionDao _vpnConnectionDao;
@Mock
private VpcDao _vpcDao;
@Mock
private IPAddressDao _ipAddressDao;
@Mock
private VpcManager _vpcMgr;
@Mock
private AccountManager _accountMgr;
@Mock
private AnnotationDao annotationDao;
@Mock
private List<Site2SiteVpnServiceProvider> _s2sProviders;
@Mock
VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
@InjectMocks
private Site2SiteVpnManagerImpl site2SiteVpnManager;
private AccountVO account;
private UserVO user;
private VpcVO vpc;
private IPAddressVO ipAddress;
private Site2SiteVpnGatewayVO vpnGateway;
private Site2SiteCustomerGatewayVO customerGateway;
private Site2SiteVpnConnectionVO vpnConnection;
private static final Long ACCOUNT_ID = 1L;
private static final Long DOMAIN_ID = 2L;
private static final Long VPC_ID = 3L;
private static final Long VPN_GATEWAY_ID = 4L;
private static final Long CUSTOMER_GATEWAY_ID = 5L;
private static final Long VPN_CONNECTION_ID = 6L;
private static final Long IP_ADDRESS_ID = 7L;
@Before
public void setUp() throws Exception {
account = new AccountVO("testaccount", DOMAIN_ID, "networkdomain", Account.Type.NORMAL, UUID.randomUUID().toString());
account.setId(ACCOUNT_ID);
user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone",
UUID.randomUUID().toString(), User.Source.UNKNOWN);
CallContext.register(user, account);
vpc = mock(VpcVO.class);
when(vpc.getId()).thenReturn(VPC_ID);
when(vpc.getAccountId()).thenReturn(ACCOUNT_ID);
when(vpc.getDomainId()).thenReturn(DOMAIN_ID);
when(vpc.getCidr()).thenReturn("10.0.0.0/16");
ipAddress = mock(IPAddressVO.class);
when(ipAddress.getId()).thenReturn(IP_ADDRESS_ID);
when(ipAddress.getVpcId()).thenReturn(VPC_ID);
vpnGateway = mock(Site2SiteVpnGatewayVO.class);
when(vpnGateway.getId()).thenReturn(VPN_GATEWAY_ID);
when(vpnGateway.getVpcId()).thenReturn(VPC_ID);
when(vpnGateway.getAccountId()).thenReturn(ACCOUNT_ID);
when(vpnGateway.getDomainId()).thenReturn(DOMAIN_ID);
customerGateway = mock(Site2SiteCustomerGatewayVO.class);
when(customerGateway.getId()).thenReturn(CUSTOMER_GATEWAY_ID);
when(customerGateway.getAccountId()).thenReturn(ACCOUNT_ID);
when(customerGateway.getDomainId()).thenReturn(DOMAIN_ID);
when(customerGateway.getGuestCidrList()).thenReturn("192.168.1.0/24");
when(customerGateway.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(customerGateway.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(customerGateway.getIkeVersion()).thenReturn("ike");
vpnConnection = new Site2SiteVpnConnectionVO(ACCOUNT_ID, DOMAIN_ID, VPN_GATEWAY_ID, CUSTOMER_GATEWAY_ID, false);
vpnConnection.setState(State.Pending);
when(_accountMgr.getAccount(ACCOUNT_ID)).thenReturn(account);
doNothing().when(_accountMgr).checkAccess(any(Account.class), nullable(SecurityChecker.AccessType.class), anyBoolean(), any());
}
@After
public void tearDown() throws Exception {
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions);
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms);
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms);
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup);
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions);
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms);
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms);
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup);
CallContext.unregister();
}
private void setConfigKeyValue(ConfigKey<String> configKey, String value) {
try {
Field valueField = ConfigKey.class.getDeclaredField("_value");
valueField.setAccessible(true);
valueField.set(configKey, value);
Field dynamicField = ConfigKey.class.getDeclaredField("_isDynamic");
dynamicField.setAccessible(true);
dynamicField.setBoolean(configKey, false);
} catch (IllegalAccessException | NoSuchFieldException e) {
throw new RuntimeException("Failed to set ConfigKey value", e);
}
}
private void resetConfigKeyValue(ConfigKey<String> configKey) {
try {
Field valueField = ConfigKey.class.getDeclaredField("_value");
valueField.setAccessible(true);
valueField.set(configKey, null);
Field dynamicField = ConfigKey.class.getDeclaredField("_isDynamic");
dynamicField.setAccessible(true);
dynamicField.setBoolean(configKey, true);
} catch (IllegalAccessException | NoSuchFieldException e) {
throw new RuntimeException("Failed to reset ConfigKey value", e);
}
}
@Test
public void testCreateVpnGatewaySuccess() {
CreateVpnGatewayCmd cmd = mock(CreateVpnGatewayCmd.class);
when(cmd.getVpcId()).thenReturn(VPC_ID);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
when(cmd.isDisplay()).thenReturn(true);
when(_vpcDao.findById(VPC_ID)).thenReturn(vpc);
when(_vpnGatewayDao.findByVpcId(VPC_ID)).thenReturn(null);
when(_ipAddressDao.listByAssociatedVpc(VPC_ID, true)).thenReturn(List.of(ipAddress));
when(_vpnGatewayDao.persist(any(Site2SiteVpnGatewayVO.class))).thenReturn(vpnGateway);
Site2SiteVpnGateway result = site2SiteVpnManager.createVpnGateway(cmd);
assertNotNull(result);
verify(_vpnGatewayDao).persist(any(Site2SiteVpnGatewayVO.class));
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateVpnGatewayInvalidVpc() {
CreateVpnGatewayCmd cmd = mock(CreateVpnGatewayCmd.class);
when(cmd.getVpcId()).thenReturn(VPC_ID);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
when(_vpcDao.findById(VPC_ID)).thenReturn(null);
site2SiteVpnManager.createVpnGateway(cmd);
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateVpnGatewayAlreadyExists() {
CreateVpnGatewayCmd cmd = mock(CreateVpnGatewayCmd.class);
when(cmd.getVpcId()).thenReturn(VPC_ID);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
when(_vpcDao.findById(VPC_ID)).thenReturn(vpc);
when(_vpnGatewayDao.findByVpcId(VPC_ID)).thenReturn(vpnGateway);
site2SiteVpnManager.createVpnGateway(cmd);
}
@Test(expected = CloudRuntimeException.class)
public void testCreateVpnGatewayNoSourceNatIp() {
CreateVpnGatewayCmd cmd = mock(CreateVpnGatewayCmd.class);
when(cmd.getVpcId()).thenReturn(VPC_ID);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
when(_vpcDao.findById(VPC_ID)).thenReturn(vpc);
when(_vpnGatewayDao.findByVpcId(VPC_ID)).thenReturn(null);
when(_ipAddressDao.listByAssociatedVpc(VPC_ID, true)).thenReturn(new ArrayList<>());
site2SiteVpnManager.createVpnGateway(cmd);
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayInvalidIp() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("invalid-ip");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("invalid-ip")).thenReturn(false);
netUtilsMock.when(() -> NetUtils.verifyDomainName("invalid-ip")).thenReturn(false);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayInvalidCidrList() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("invalid-cidr");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList("invalid-cidr")).thenReturn(false);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayInvalidIkePolicy() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("192.168.1.0/24");
when(cmd.getIkePolicy()).thenReturn("invalid-policy");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList("192.168.1.0/24")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "invalid-policy")).thenReturn(false);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayInvalidEspPolicy() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("192.168.1.0/24");
when(cmd.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEspPolicy()).thenReturn("invalid-policy");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList("192.168.1.0/24")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "aes128-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("esp", "invalid-policy")).thenReturn(false);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayWithExcludedParameters() throws Exception {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getName()).thenReturn("test-gateway");
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("192.168.1.0/24");
when(cmd.getIpsecPsk()).thenReturn("test-psk");
when(cmd.getIkePolicy()).thenReturn("3des-sha256;modp2048");
when(cmd.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getIkeVersion()).thenReturn("ike");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms, "3des");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup, "");
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList("192.168.1.0/24")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "3des-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("esp", "aes128-sha256;modp2048")).thenReturn(true);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayDuplicateName() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getName()).thenReturn("test-gateway");
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("192.168.1.0/24");
when(cmd.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList("192.168.1.0/24")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "aes128-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("esp", "aes128-sha256;modp2048")).thenReturn(true);
when(_customerGatewayDao.findByNameAndAccountId("test-gateway", ACCOUNT_ID)).thenReturn(customerGateway);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayInvalidIkeLifetime() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("192.168.1.0/24");
when(cmd.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getIkeLifetime()).thenReturn(86401L);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList("192.168.1.0/24")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "aes128-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("esp", "aes128-sha256;modp2048")).thenReturn(true);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayInvalidEspLifetime() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("192.168.1.0/24");
when(cmd.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEspLifetime()).thenReturn(86401L);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList("192.168.1.0/24")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "aes128-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("esp", "aes128-sha256;modp2048")).thenReturn(true);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayTooManySubnets() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
String tooManyCidrs = "192.168.1.0/24,192.168.2.0/24,192.168.3.0/24,192.168.4.0/24,192.168.5.0/24," +
"192.168.6.0/24,192.168.7.0/24,192.168.8.0/24,192.168.9.0/24,192.168.10.0/24,192.168.11.0/24";
when(cmd.getGuestCidrList()).thenReturn(tooManyCidrs);
when(cmd.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList(tooManyCidrs)).thenReturn(true);
netUtilsMock.when(() -> NetUtils.getCleanIp4CidrList(tooManyCidrs)).thenReturn(tooManyCidrs);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "aes128-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("esp", "aes128-sha256;modp2048")).thenReturn(true);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayOverlappingSubnets() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("192.168.1.0/24,192.168.1.0/25");
when(cmd.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
String cidrList = "192.168.1.0/24,192.168.1.0/25";
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList(cidrList)).thenReturn(true);
netUtilsMock.when(() -> NetUtils.getCleanIp4CidrList(cidrList)).thenReturn(cidrList);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "aes128-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("esp", "aes128-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isNetworksOverlap("192.168.1.0/24", "192.168.1.0/25")).thenReturn(true);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateVpnConnectionCidrOverlapWithVpc() {
CreateVpnConnectionCmd cmd = mock(CreateVpnConnectionCmd.class);
when(cmd.getVpnGatewayId()).thenReturn(VPN_GATEWAY_ID);
when(cmd.getCustomerGatewayId()).thenReturn(CUSTOMER_GATEWAY_ID);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
Site2SiteCustomerGatewayVO customerGw = mock(Site2SiteCustomerGatewayVO.class);
when(customerGw.getGuestCidrList()).thenReturn("10.0.0.0/24");
when(customerGw.getAccountId()).thenReturn(ACCOUNT_ID);
when(customerGw.getDomainId()).thenReturn(DOMAIN_ID);
when(_customerGatewayDao.findById(CUSTOMER_GATEWAY_ID)).thenReturn(customerGw);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
when(_vpnConnectionDao.findByVpnGatewayIdAndCustomerGatewayId(VPN_GATEWAY_ID, CUSTOMER_GATEWAY_ID)).thenReturn(null);
when(_vpnGatewayDao.findByVpcId(VPC_ID)).thenReturn(vpnGateway);
when(_vpcDao.findById(VPC_ID)).thenReturn(vpc);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isNetworksOverlap("10.0.0.0/16", "10.0.0.0/24")).thenReturn(true);
site2SiteVpnManager.createVpnConnection(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateVpnConnectionExceedsLimit() {
CreateVpnConnectionCmd cmd = mock(CreateVpnConnectionCmd.class);
when(cmd.getVpnGatewayId()).thenReturn(VPN_GATEWAY_ID);
when(cmd.getCustomerGatewayId()).thenReturn(CUSTOMER_GATEWAY_ID);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
when(_customerGatewayDao.findById(CUSTOMER_GATEWAY_ID)).thenReturn(customerGateway);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
when(_vpnConnectionDao.findByVpnGatewayIdAndCustomerGatewayId(VPN_GATEWAY_ID, CUSTOMER_GATEWAY_ID)).thenReturn(null);
when(_vpnGatewayDao.findByVpcId(VPC_ID)).thenReturn(vpnGateway);
when(_vpcDao.findById(VPC_ID)).thenReturn(vpc);
List<Site2SiteVpnConnectionVO> existingConns = new ArrayList<>();
for (int i = 0; i < 4; i++) {
existingConns.add(mock(Site2SiteVpnConnectionVO.class));
}
when(_vpnConnectionDao.listByVpnGatewayId(VPN_GATEWAY_ID)).thenReturn(existingConns);
site2SiteVpnManager.createVpnConnection(cmd);
}
@Test
public void testDeleteCustomerGatewaySuccess() {
DeleteVpnCustomerGatewayCmd cmd = mock(DeleteVpnCustomerGatewayCmd.class);
when(cmd.getId()).thenReturn(CUSTOMER_GATEWAY_ID);
when(_customerGatewayDao.findById(CUSTOMER_GATEWAY_ID)).thenReturn(customerGateway);
when(_vpnConnectionDao.listByCustomerGatewayId(CUSTOMER_GATEWAY_ID)).thenReturn(new ArrayList<>());
boolean result = site2SiteVpnManager.deleteCustomerGateway(cmd);
assertTrue(result);
verify(_customerGatewayDao).remove(CUSTOMER_GATEWAY_ID);
}
@Test(expected = InvalidParameterValueException.class)
public void testDeleteCustomerGatewayWithConnections() {
DeleteVpnCustomerGatewayCmd cmd = mock(DeleteVpnCustomerGatewayCmd.class);
when(cmd.getId()).thenReturn(CUSTOMER_GATEWAY_ID);
when(_customerGatewayDao.findById(CUSTOMER_GATEWAY_ID)).thenReturn(customerGateway);
when(_vpnConnectionDao.listByCustomerGatewayId(CUSTOMER_GATEWAY_ID)).thenReturn(List.of(vpnConnection));
site2SiteVpnManager.deleteCustomerGateway(cmd);
}
@Test
public void testDeleteVpnGatewaySuccess() {
DeleteVpnGatewayCmd cmd = mock(DeleteVpnGatewayCmd.class);
when(cmd.getId()).thenReturn(VPN_GATEWAY_ID);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
when(_vpnConnectionDao.listByVpnGatewayId(VPN_GATEWAY_ID)).thenReturn(new ArrayList<>());
boolean result = site2SiteVpnManager.deleteVpnGateway(cmd);
assertTrue(result);
verify(_vpnGatewayDao).remove(VPN_GATEWAY_ID);
}
@Test(expected = InvalidParameterValueException.class)
public void testDeleteVpnGatewayWithConnections() {
DeleteVpnGatewayCmd cmd = mock(DeleteVpnGatewayCmd.class);
when(cmd.getId()).thenReturn(VPN_GATEWAY_ID);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
when(_vpnConnectionDao.listByVpnGatewayId(VPN_GATEWAY_ID)).thenReturn(List.of(vpnConnection));
site2SiteVpnManager.deleteVpnGateway(cmd);
}
@Test
public void testDeleteVpnConnectionSuccess() throws ResourceUnavailableException {
DeleteVpnConnectionCmd cmd = mock(DeleteVpnConnectionCmd.class);
when(cmd.getId()).thenReturn(VPN_CONNECTION_ID);
when(_vpnConnectionDao.findById(VPN_CONNECTION_ID)).thenReturn(vpnConnection);
vpnConnection.setState(State.Pending);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
when(_vpcMgr.applyStaticRouteForVpcVpnIfNeeded(anyLong(), anyBoolean())).thenReturn(true);
boolean result = site2SiteVpnManager.deleteVpnConnection(cmd);
assertTrue(result);
verify(_vpnConnectionDao).remove(VPN_CONNECTION_ID);
}
@Test
public void testStartVpnConnectionSuccess() throws ResourceUnavailableException {
when(_vpnConnectionDao.acquireInLockTable(VPN_CONNECTION_ID)).thenReturn(vpnConnection);
vpnConnection.setState(State.Pending);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
Site2SiteVpnServiceProvider provider = mock(Site2SiteVpnServiceProvider.class);
when(provider.startSite2SiteVpn(any(Site2SiteVpnConnection.class))).thenReturn(true);
when(_s2sProviders.iterator()).thenReturn(List.of(provider).iterator());
when(_vpnConnectionDao.persist(any(Site2SiteVpnConnectionVO.class))).thenReturn(vpnConnection);
when(_vpcMgr.applyStaticRouteForVpcVpnIfNeeded(anyLong(), anyBoolean())).thenReturn(true);
Site2SiteVpnConnection result = site2SiteVpnManager.startVpnConnection(VPN_CONNECTION_ID);
assertNotNull(result);
verify(_vpnConnectionDao, org.mockito.Mockito.atLeastOnce()).persist(any(Site2SiteVpnConnectionVO.class));
}
@Test(expected = InvalidParameterValueException.class)
public void testStartVpnConnectionWrongState() throws ResourceUnavailableException {
when(_vpnConnectionDao.acquireInLockTable(VPN_CONNECTION_ID)).thenReturn(vpnConnection);
vpnConnection.setState(State.Connected);
site2SiteVpnManager.startVpnConnection(VPN_CONNECTION_ID);
}
@Test
public void testResetVpnConnectionSuccess() throws ResourceUnavailableException {
ResetVpnConnectionCmd cmd = mock(ResetVpnConnectionCmd.class);
when(cmd.getId()).thenReturn(VPN_CONNECTION_ID);
when(_vpnConnectionDao.findById(VPN_CONNECTION_ID)).thenReturn(vpnConnection);
vpnConnection.setState(State.Connected);
when(_vpnConnectionDao.acquireInLockTable(VPN_CONNECTION_ID)).thenReturn(vpnConnection);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
Site2SiteVpnServiceProvider provider = mock(Site2SiteVpnServiceProvider.class);
when(provider.stopSite2SiteVpn(any(Site2SiteVpnConnection.class))).thenReturn(true);
when(provider.startSite2SiteVpn(any(Site2SiteVpnConnection.class))).thenReturn(true);
when(_s2sProviders.iterator()).thenReturn(List.of(provider).iterator());
when(_vpnConnectionDao.persist(any(Site2SiteVpnConnectionVO.class))).thenReturn(vpnConnection);
when(_vpcMgr.applyStaticRouteForVpcVpnIfNeeded(anyLong(), anyBoolean())).thenReturn(true);
Site2SiteVpnConnection result = site2SiteVpnManager.resetVpnConnection(cmd);
assertNotNull(result);
}
@Test
public void testCleanupVpnConnectionByVpc() {
when(_vpnConnectionDao.listByVpcId(VPC_ID)).thenReturn(List.of(vpnConnection));
boolean result = site2SiteVpnManager.cleanupVpnConnectionByVpc(VPC_ID);
assertTrue(result);
verify(_vpnConnectionDao).remove(vpnConnection.getId());
}
@Test
public void testCleanupVpnGatewayByVpc() {
when(_vpnGatewayDao.findByVpcId(VPC_ID)).thenReturn(vpnGateway);
when(_vpnConnectionDao.listByVpnGatewayId(VPN_GATEWAY_ID)).thenReturn(new ArrayList<>());
boolean result = site2SiteVpnManager.cleanupVpnGatewayByVpc(VPC_ID);
assertTrue(result);
verify(_vpnGatewayDao).remove(VPN_GATEWAY_ID);
}
@Test
public void testCleanupVpnGatewayByVpcNotFound() {
when(_vpnGatewayDao.findByVpcId(VPC_ID)).thenReturn(null);
boolean result = site2SiteVpnManager.cleanupVpnGatewayByVpc(VPC_ID);
assertTrue(result);
verify(_vpnGatewayDao, never()).remove(anyLong());
}
@Test
public void testGetConnectionsForRouter() {
DomainRouterVO router = mock(DomainRouterVO.class);
when(router.getVpcId()).thenReturn(VPC_ID);
when(_vpnConnectionDao.listByVpcId(VPC_ID)).thenReturn(List.of(vpnConnection));
List<Site2SiteVpnConnectionVO> result = site2SiteVpnManager.getConnectionsForRouter(router);
assertNotNull(result);
assertEquals(1, result.size());
}
@Test
public void testGetConnectionsForRouterNoVpc() {
DomainRouterVO router = mock(DomainRouterVO.class);
when(router.getVpcId()).thenReturn(null);
List<Site2SiteVpnConnectionVO> result = site2SiteVpnManager.getConnectionsForRouter(router);
assertNotNull(result);
assertTrue(result.isEmpty());
}
@Test
public void testDeleteCustomerGatewayByAccount() {
when(_customerGatewayDao.listByAccountId(ACCOUNT_ID)).thenReturn(List.of(customerGateway));
when(_vpnConnectionDao.listByCustomerGatewayId(CUSTOMER_GATEWAY_ID)).thenReturn(new ArrayList<>());
boolean result = site2SiteVpnManager.deleteCustomerGatewayByAccount(ACCOUNT_ID);
assertTrue(result);
verify(_customerGatewayDao).remove(CUSTOMER_GATEWAY_ID);
}
@Test
public void testReconnectDisconnectedVpnByVpc() throws ResourceUnavailableException {
Site2SiteVpnConnectionVO conn = mock(Site2SiteVpnConnectionVO.class);
when(conn.getId()).thenReturn(VPN_CONNECTION_ID);
when(conn.getState()).thenReturn(State.Disconnected);
when(conn.getCustomerGatewayId()).thenReturn(CUSTOMER_GATEWAY_ID);
when(conn.getVpnGatewayId()).thenReturn(VPN_GATEWAY_ID);
when(_vpnConnectionDao.listByVpcId(VPC_ID)).thenReturn(List.of(conn));
when(_customerGatewayDao.findById(CUSTOMER_GATEWAY_ID)).thenReturn(customerGateway);
when(_vpnConnectionDao.acquireInLockTable(VPN_CONNECTION_ID)).thenReturn(conn);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
Site2SiteVpnServiceProvider provider = mock(Site2SiteVpnServiceProvider.class);
when(provider.startSite2SiteVpn(any(Site2SiteVpnConnection.class))).thenReturn(true);
when(_s2sProviders.iterator()).thenReturn(List.of(provider).iterator());
when(_vpnConnectionDao.persist(any(Site2SiteVpnConnectionVO.class))).thenReturn(conn);
when(_vpcMgr.applyStaticRouteForVpcVpnIfNeeded(anyLong(), anyBoolean())).thenReturn(true);
site2SiteVpnManager.reconnectDisconnectedVpnByVpc(VPC_ID);
verify(_vpnConnectionDao, org.mockito.Mockito.atLeastOnce()).persist(any(Site2SiteVpnConnectionVO.class));
}
@Test
public void testUpdateVpnConnection() {
when(_vpnConnectionDao.findById(VPN_CONNECTION_ID)).thenReturn(vpnConnection);
when(_vpnConnectionDao.update(anyLong(), any(Site2SiteVpnConnectionVO.class))).thenReturn(true);
when(_vpnConnectionDao.findById(VPN_CONNECTION_ID)).thenReturn(vpnConnection);
Site2SiteVpnConnection result = site2SiteVpnManager.updateVpnConnection(VPN_CONNECTION_ID, "custom-id", true);
assertNotNull(result);
}
@Test
public void testUpdateVpnGateway() {
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
when(_vpnGatewayDao.update(anyLong(), any(Site2SiteVpnGatewayVO.class))).thenReturn(true);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
Site2SiteVpnGateway result = site2SiteVpnManager.updateVpnGateway(VPN_GATEWAY_ID, "custom-id", true);
assertNotNull(result);
}
@Test
public void testVpnGatewayContainsExcludedParametersWithExcludedIkeVersion() throws Exception {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ikev1");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions, "ikev1");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getExcludedVpnGatewayParameters(gw);
assertFalse("Should detect excluded IKE version", result.isEmpty());
assertEquals("Should detect excluded IKE version", "[ikev1]", result.toString());
}
@Test
public void testVpnGatewayContainsExcludedParametersWithExcludedEncryption() throws Exception {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("3des-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms, "3des");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getExcludedVpnGatewayParameters(gw);
assertFalse("Should detect excluded encryption algorithm", result.isEmpty());
assertEquals("Should detect excluded encryption algorithm", "[3des]", result.toString());
}
@Test
public void testVpnGatewayContainsExcludedParametersWithExcludedHashing() throws Exception {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-md5;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms, "aes128");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms, "md5");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getExcludedVpnGatewayParameters(gw);
assertFalse("Should detect excluded algorithms", result.isEmpty());
assertEquals("Should detect excluded algorithms", "[aes128, md5]", result.toString());
}
@Test
public void testVpnGatewayContainsExcludedParametersWithExcludedDhGroup() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp1024");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup, "modp1024");
java.util.Set<String> result = site2SiteVpnManager.getExcludedVpnGatewayParameters(gw);
assertFalse("Should detect excluded DH group", result.isEmpty());
assertEquals("Should detect excluded DH group", "[modp1024]", result.toString());
}
@Test
public void testVpnGatewayContainsExcludedParametersNoExcludedParameters() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getExcludedVpnGatewayParameters(gw);
assertTrue("Should not detect excluded parameters when none are configured", result.isEmpty());
}
@Test
public void testVpnGatewayContainsExcludedParametersWithExcludedEspPolicy() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("3des-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms, "3des");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getExcludedVpnGatewayParameters(gw);
assertFalse("Should detect excluded encryption in ESP policy", result.isEmpty());
assertEquals("Should detect excluded encryption in ESP policy", "[3des]", result.toString());
}
@Test
public void testVpnGatewayContainsObsoleteParametersWithObsoleteIkeVersion() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ikev1");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions, "ikev1");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getObsoleteVpnGatewayParameters(gw);
assertFalse("Should detect obsolete IKE version", result.isEmpty());
assertEquals("Should detect obsolete IKE version", "[ikev1]", result.toString());
}
@Test
public void testVpnGatewayContainsObsoleteParametersWithObsoleteEncryption() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("3des-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms, "3des");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getObsoleteVpnGatewayParameters(gw);
assertFalse("Should detect obsolete encryption algorithm", result.isEmpty());
assertEquals("Should detect obsolete encryption algorithm", "[3des]", result.toString());
}
@Test
public void testVpnGatewayContainsObsoleteParametersWithObsoleteHashing() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-md5;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms, "md5");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getObsoleteVpnGatewayParameters(gw);
assertFalse("Should detect obsolete hashing algorithm", result.isEmpty());
assertEquals("Should detect obsolete hashing algorithm", "[md5]", result.toString());
}
@Test
public void testVpnGatewayContainsObsoleteParametersWithObsoleteDhGroup() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp1024");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup, "modp1024");
java.util.Set<String> result = site2SiteVpnManager.getObsoleteVpnGatewayParameters(gw);
assertFalse("Should detect obsolete DH group", result.isEmpty());
assertEquals("Should detect obsolete DH group", "[modp1024]", result.toString());
}
@Test
public void testVpnGatewayContainsObsoleteParametersNoObsoleteParameters() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getObsoleteVpnGatewayParameters(gw);
assertTrue("Should not detect obsolete parameters when none are configured", result.isEmpty());
}
@Test
public void testVpnGatewayContainsObsoleteParametersWithObsoleteEspPolicy() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("3des-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms, "3des");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getObsoleteVpnGatewayParameters(gw);
assertFalse("Should detect obsolete encryption in ESP policy", result.isEmpty());
assertEquals("Should detect obsolete encryption in ESP policy", "[3des]", result.toString());
}
}

View File

@ -23,7 +23,6 @@ import com.cloud.network.Site2SiteVpnConnection;
import com.cloud.network.Site2SiteVpnGateway;
import com.cloud.network.dao.Site2SiteVpnConnectionVO;
import com.cloud.network.vpn.Site2SiteVpnManager;
import com.cloud.network.vpn.Site2SiteVpnService;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ManagerBase;
import com.cloud.vm.DomainRouterVO;
@ -41,11 +40,13 @@ import org.apache.cloudstack.api.command.user.vpn.UpdateVpnCustomerGatewayCmd;
import org.springframework.stereotype.Component;
import javax.naming.ConfigurationException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Component
public class MockSite2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpnManager, Site2SiteVpnService {
public class MockSite2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpnManager {
/* (non-Javadoc)
* @see com.cloud.network.vpn.Site2SiteVpnService#createVpnGateway(org.apache.cloudstack.api.commands.CreateVpnGatewayCmd)
@ -275,4 +276,16 @@ public class MockSite2SiteVpnManagerImpl extends ManagerBase implements Site2Sit
return null;
}
@Override
public Set<String> getExcludedVpnGatewayParameters(Site2SiteCustomerGateway customerGw) {
// TODO Auto-generated method stub
return new HashSet<>();
}
@Override
public Set<String> getObsoleteVpnGatewayParameters(Site2SiteCustomerGateway customerGw) {
// TODO Auto-generated method stub
return new HashSet<>();
}
}

View File

@ -807,7 +807,7 @@
"label.delete.tungsten.service.group": "Delete Service Group",
"label.delete.volumes": "Data volumes to be deleted",
"label.delete.vpn.connection": "Delete VPN connection",
"label.delete.vpn.customer.gateway": "Delete VPN customer gateway",
"label.delete.vpn.customer.gateway": "Delete VPN Customer Gateway",
"label.delete.vpn.gateway": "Delete VPN gateway",
"label.delete.vpn.user": "Delete VPN User",
"label.delete.webhook": "Delete Webhook",
@ -2629,6 +2629,7 @@
"label.update.to": "updated to",
"label.update.traffic.label": "Update traffic labels",
"label.update.vmware.datacenter": "Update VMWare datacenter",
"label.update.vpn.customer.gateway": "Update VPN Customer Gateway",
"label.update.webhook": "Update Webhook",
"label.updating": "Updating",
"label.upgrade.router.newer.template": "Upgrade router to use newer Template",
@ -3093,8 +3094,8 @@
"message.add.volume": "Please fill in the following data to add a new volume.",
"message.add.vpn.connection.failed": "Adding VPN connection failed",
"message.add.vpn.connection.processing": "Adding VPN Connection...",
"message.add.vpn.customer.gateway": "Adding VPN customer gateway",
"message.add.vpn.customer.gateway.processing": "Creation of VPN customer gateway is in progress",
"message.add.vpn.customer.gateway": "Adding VPN Customer Gateway",
"message.add.vpn.customer.gateway.processing": "Creation of VPN Customer Gateway is in progress",
"message.add.vpn.gateway": "Please confirm that you want to add a VPN Gateway.",
"message.add.vpn.gateway.failed": "Adding VPN gateway failed",
"message.add.vpn.gateway.processing": "Adding VPN gateway...",
@ -3242,7 +3243,7 @@
"message.create.volume.failed": "Failed to create Volume.",
"message.create.volume.processing": "Volume creation in progress",
"message.create.vpc.offering": "VPC offering created.",
"message.create.vpn.customer.gateway.failed": "VPN customer gateway creation failed.",
"message.create.vpn.customer.gateway.failed": "VPN Customer Gateway creation failed.",
"message.creating.autoscale.vmgroup": "Creating AutoScaling Group",
"message.creating.autoscale.vmprofile": "Creating AutoScale Instance profile",
"message.creating.autoscale.scaledown.conditions": "Creating ScaleDown conditions",
@ -3293,7 +3294,7 @@
"message.delete.tungsten.tag": "Are you sure you want to remove this Tag from this Policy?",
"message.delete.user": "Please confirm that you would like to delete this User.",
"message.delete.vpn.connection": "Please confirm that you want to delete VPN connection.",
"message.delete.vpn.customer.gateway": "Please confirm that you want to delete this VPN customer gateway.",
"message.delete.vpn.customer.gateway": "Please confirm that you want to delete this VPN Customer Gateway.",
"message.delete.vpn.gateway": "Please confirm that you want to delete this VPN Gateway.",
"message.delete.vpn.gateway.failed": "Failed to delete VPN Gateway.",
"message.delete.webhook": "Please confirm that you want to delete this Webhook.",
@ -3834,7 +3835,7 @@
"message.success.add.tungsten.routing.policy": "Successfully added Tungsten-Fabric routing policy",
"message.success.add.vpc": "Successfully added a Virtual Private Cloud",
"message.success.add.vpc.network": "Successfully added a VPC network",
"message.success.add.vpn.customer.gateway": "Successfully added VPN customer gateway",
"message.success.add.vpn.customer.gateway": "Successfully added VPN Customer Gateway",
"message.success.add.vpn.gateway": "Successfully added VPN gateway",
"message.success.add.webhook.filter": "Successfully added Webhook Filter",
"message.success.assign.sslcert": "Successfully assigned SSL certificate",
@ -3960,6 +3961,7 @@
"message.success.update.network": "Successfully updated Network",
"message.success.update.template": "Successfully updated Template",
"message.success.update.user": "Successfully updated User",
"message.success.update.vpn.customer.gateway": "Successfully updated VPN Customer Gateway",
"message.success.upgrade.kubernetes": "Successfully upgraded Kubernetes Cluster",
"message.success.upload": "Successfully uploaded",
"message.success.upload.description": "This ISO file has been uploaded. Please check its status in the Templates menu.",
@ -3987,6 +3989,9 @@
"message.update.condition.failed": "Failed to update condition",
"message.update.condition.processing": "Updating condition...",
"message.update.failed": "Update failed",
"message.update.vpn.customer.gateway": "Update VPN Customer Gateway",
"message.update.vpn.customer.gateway.failed": "Updating the VPN Customer Gateway failed",
"message.update.vpn.customer.gateway.processing": "Updating VPN Customer Gateway...",
"message.test.webhook.delivery": "Test delivery to the Webhook with an optional payload",
"message.two.factor.authorization.failed": "Unable to verify 2FA with provided code, please retry.",
"message.two.fa.auth": "Open the two-factor authentication app on your mobile device to view your authentication code.",
@ -4074,6 +4079,11 @@
"message.volumes.managed": "Volumes controlled by CloudStack.",
"message.volumes.unmanaged": "Volumes not controlled by CloudStack.",
"message.vpc.restart.required": "Restart is required for VPC(s). Click here to view VPC(s) which require restart.",
"message.vpn.customer.gateway.contains.excluded.obsolete.parameters": "This VPN Customer Gateway contains cryptographic parameters that are marked as excluded or obsolete by the Administrator. Consider changing them using the Update VPN Customer Gateway form.",
"message.vpn.customer.gateway.excluded.parameter": " is marked as excluded. Please choose another value.",
"message.vpn.customer.gateway.obsolete.parameter": " is marked as obsolete/insecure. Please choose another value.",
"message.vpn.customer.gateway.obsolete.parameter.tooltip": "This parameter value is marked as obsolete/insecure.",
"message.vr.alert.upon.network.offering.creation.l2": "As virtual routers are not created for L2 Networks, the compute offering will not be used.",
"message.vr.alert.upon.network.offering.creation.others": "As none of the obligatory services for creating a virtual router (VPN, DHCP, DNS, Firewall, LB, UserData, SourceNat, StaticNat, PortForwarding) are enabled, the virtual router will not be created and the compute offering will not be used.",
"message.warn.change.primary.storage.scope": "This feature is tested and supported for the following configurations:<br>KVM - NFS/Ceph - DefaultPrimary<br>VMware - NFS - DefaultPrimary<br>*There might be extra steps involved to make it work for other configurations.",
"message.warn.filetype": "jpg, jpeg, png, bmp and svg are the only supported image formats.",

View File

@ -170,6 +170,14 @@
<warning-outlined style="color: #f5222d" />
</a-tooltip>
</span>
<span v-else-if="$route.path.startsWith('/vpncustomergateway')">
&nbsp;
<a-tooltip
v-if="record.excludedparameters || record.obsoleteparameters"
:title="$t('message.vpn.customer.gateway.contains.excluded.obsolete.parameters')">
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
</a-tooltip>
</span>
</span>
<span
v-if="record.leaseduration !== undefined"

View File

@ -1263,15 +1263,11 @@ export default {
{
api: 'updateVpnCustomerGateway',
icon: 'edit-outlined',
label: 'label.edit',
label: 'label.update.vpn.customer.gateway',
docHelp: 'adminguide/networking_and_traffic.html#updating-and-removing-a-vpn-customer-gateway',
dataView: true,
args: ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'ikeversion', 'esppolicy', 'esplifetime', 'dpd', 'splitconnections', 'forceencap'],
mapping: {
ikeversion: {
options: ['ike', 'ikev1', 'ikev2']
}
}
popup: true,
component: shallowRef(defineAsyncComponent(() => import('@/views/network/UpdateVpnCustomerGateway.vue')))
},
{
api: 'deleteVpnCustomerGateway',

View File

@ -15,352 +15,58 @@
// specific language governing permissions and limitations
// under the License.
<template>
<div>
<a-form
class="form-layout"
:ref="formRef"
:model="form"
:rules="rules"
layout="vertical"
@finish="handleSubmit"
v-ctrl-enter="handleSubmit"
>
<a-form-item ref="name" name="name">
<template #label>
<tooltip-label :title="$t('label.name')" :tooltip="apiParams.name.description"/>
</template>
<a-input
v-model:value="form.name"
:placeholder="apiParams.name.description"
v-focus="true" />
</a-form-item>
<a-form-item ref="gateway" name="gateway">
<template #label>
<tooltip-label :title="$t('label.gateway')" :tooltip="apiParams.gateway.description"/>
</template>
<a-input
v-model:value="form.gateway"
:placeholder="apiParams.gateway.description" />
</a-form-item>
<a-form-item ref="cidrlist" name="cidrlist">
<template #label>
<tooltip-label :title="$t('label.cidrlist')" :tooltip="apiParams.cidrlist.description"/>
</template>
<a-input
v-model:value="form.cidrlist"
:placeholder="apiParams.cidrlist.description" />
</a-form-item>
<a-form-item ref="ipsecpsk" name="ipsecpsk">
<template #label>
<tooltip-label :title="$t('label.ipsecpsk')" :tooltip="apiParams.ipsecpsk.description"/>
</template>
<a-input
v-model:value="form.ipsecpsk"
:placeholder="apiParams.ipsecpsk.description" />
</a-form-item>
<a-form-item ref="ikeEncryption" name="ikeEncryption" :label="$t('label.ikeencryption')">
<a-select
v-model:value="form.ikeEncryption"
showSearch
optionFilterProp="value"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option :value="algo" v-for="(algo, idx) in encryptionAlgo" :key="idx">
{{ algo }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item ref="ikeHash" name="ikeHash" :label="$t('label.ikehash')">
<a-select
v-model:value="form.ikeHash"
showSearch
optionFilterProp="value"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option :value="h" v-for="(h, idx) in hash" :key="idx">
{{ h }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item ref="ikeversion" name="ikeversion">
<template #label>
<tooltip-label :title="$t('label.ikeversion')" :tooltip="apiParams.ikeversion.description"/>
</template>
<a-select
v-model:value="form.ikeversion"
showSearch
optionFilterProp="value"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option :value="vers" v-for="(vers, idx) in ikeVersions" :key="idx">
{{ vers }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item ref="ikeDh" name="ikeDh" :label="$t('label.ikedh')">
<a-select
v-model:value="form.ikeDh"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option
:value="DHGroups[group]"
v-for="(group, idx) in Object.keys(DHGroups)"
:key="idx"
:label="group + '(' + DHGroups[group] + ')'">
<div v-if="group !== ''">
{{ group+"("+DHGroups[group]+")" }}
</div>
</a-select-option>
</a-select>
</a-form-item>
<a-form-item ref="espEncryption" name="espEncryption" :label="$t('label.espencryption')">
<a-select
v-model:value="form.espEncryption"
showSearch
optionFilterProp="value"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option :value="algo" v-for="(algo, idx) in encryptionAlgo" :key="idx">
{{ algo }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item ref="espHash" name="espHash" :label="$t('label.esphash')">
<a-select
v-model:value="form.espHash"
showSearch
optionFilterProp="value"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option :value="h" v-for="(h, idx) in hash" :key="idx">
{{ h }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item ref="perfectForwardSecrecy" name="perfectForwardSecrecy" :label="$t('label.perfectforwardsecrecy')">
<a-select
v-model:value="form.perfectForwardSecrecy"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option
:value="DHGroups[group]"
v-for="(group, idx) in Object.keys(DHGroups)"
:key="idx"
:label="group === '' ? DHGroups[group] : group + '(' + DHGroups[group] + ')'">
<div v-if="group === ''">
{{ DHGroups[group] }}
</div>
<div v-else>
{{ group+"("+DHGroups[group]+")" }}
</div>
</a-select-option>
</a-select>
</a-form-item>
<a-form-item ref="ikelifetime" name="ikelifetime">
<template #label>
<tooltip-label :title="$t('label.ikelifetime')" :tooltip="apiParams.ikelifetime.description"/>
</template>
<a-input
v-model:value="form.ikelifetime"
:placeholder="apiParams.ikelifetime.description"/>
</a-form-item>
<a-form-item ref="esplifetime" name="esplifetime">
<template #label>
<tooltip-label :title="$t('label.esplifetime')" :tooltip="apiParams.esplifetime.description"/>
</template>
<a-input
v-model:value="form.esplifetime"
:placeholder="apiParams.esplifetime.description"/>
</a-form-item>
<a-form-item ref="dpd" name="dpd">
<template #label>
<tooltip-label :title="$t('label.dpd')" :tooltip="apiParams.dpd.description"/>
</template>
<a-switch v-model:checked="form.dpd"/>
</a-form-item>
<a-form-item ref="splitconnections" name="splitconnections" v-if="form.ikeversion !== 'ikev1'">
<template #label>
<tooltip-label :title="$t('label.splitconnections')" :tooltip="apiParams.splitconnections.description"/>
</template>
<a-switch v-model:checked="form.splitconnections"/>
</a-form-item>
<a-form-item ref="forceencap" name="forceencap">
<template #label>
<tooltip-label :title="$t('label.forceencap')" :tooltip="apiParams.forceencap.description"/>
</template>
<a-switch v-model:checked="form.forceencap"/>
</a-form-item>
<div class="action-button">
<a-button @click="closeModal">
{{ $t('label.cancel') }}
</a-button>
<a-button type="primary" @click="handleSubmit" html-type="submit">
{{ $t('label.ok') }}
</a-button>
</div>
</a-form>
</div>
<vpn-customer-gateway
ref="vpnCustomerGatewayForm"
:apiParams="apiParams"
@submit="handleSubmit"
@cancel="closeModal"
/>
</template>
<script>
import { ref, reactive, toRaw } from 'vue'
import { postAPI } from '@/api'
import { mixinForm } from '@/utils/mixin'
import TooltipLabel from '@/components/widgets/TooltipLabel'
import VpnCustomerGateway from './VpnCustomerGateway.vue'
export default {
name: 'CreateVpnCustomerGateway',
mixins: [mixinForm],
components: {
TooltipLabel
},
props: {
resource: {
type: Object,
required: true
}
},
data () {
return {
encryptionAlgo: [
'aes128',
'aes192',
'aes256',
'3des'
],
hash: [
'sha1',
'sha256',
'sha384',
'sha512',
'md5'
],
ikeVersions: [
'ike',
'ikev1',
'ikev2'
],
DHGroups: {
'': 'None',
'Group 2': 'modp1024',
'Group 5': 'modp1536',
'Group 14': 'modp2048',
'Group 15': 'modp3072',
'Group 16': 'modp4096',
'Group 17': 'modp6144',
'Group 18': 'modp8192'
},
ikeDhGroupInitialValue: 'Group 5(modp1536)',
isSubmitted: false,
ikeversion: 'ike'
}
VpnCustomerGateway
},
beforeCreate () {
this.apiParams = this.$getApiParams('createVpnCustomerGateway')
},
created () {
this.initForm()
},
methods: {
initForm () {
this.formRef = ref()
this.form = reactive({
ikeEncryption: 'aes128',
ikeHash: 'sha1',
ikeversion: 'ike',
ikeDh: 'Group 5(modp1536)',
espEncryption: 'aes128',
espHash: 'sha1',
perfectForwardSecrecy: 'None',
ikelifetime: '86400',
esplifetime: '3600',
dpd: false,
splitconnections: false,
forceencap: false
})
this.rules = reactive({
name: [{ required: true, message: this.$t('label.required') }],
gateway: [{ required: true, message: this.$t('label.required') }],
cidrlist: [{ required: true, message: this.$t('label.required') }],
ipsecpsk: [{ required: true, message: this.$t('label.required') }]
})
},
closeModal () {
this.$emit('close-action')
},
handleSubmit (e) {
e.preventDefault()
if (this.isSubmitted) return
this.formRef.value.validate().then(() => {
const formRaw = toRaw(this.form)
const values = this.handleRemoveFields(formRaw)
let ikepolicy = values.ikeEncryption + '-' + values.ikeHash + ';'
ikepolicy += (values.ikeDh !== this.ikeDhGroupInitialValue) ? values.ikeDh : (values.ikeDh.split('(')[1]).split(')')[0]
let esppolicy = values.espEncryption + '-' + values.espHash
if (values.perfectForwardSecrecy !== 'None') {
esppolicy += ';' + (values.perfectForwardSecrecy)
}
postAPI('createVpnCustomerGateway', {
name: values.name,
gateway: values.gateway,
cidrlist: values.cidrlist,
ipsecpsk: values.ipsecpsk,
ikelifetime: values.ikelifetime,
esplifetime: values.esplifetime,
dpd: values.dpd,
forceencap: values.forceencap,
ikepolicy: ikepolicy,
esppolicy: esppolicy,
splitconnections: values.splitconnections,
ikeversion: values.ikeversion
}).then(response => {
this.$pollJob({
jobId: response.createvpncustomergatewayresponse.jobid,
title: this.$t('message.add.vpn.customer.gateway'),
description: values.name,
successMessage: this.$t('message.success.add.vpn.customer.gateway'),
successMethod: () => {
this.closeModal()
this.isSubmitted = false
},
errorMessage: `${this.$t('message.create.vpn.customer.gateway.failed')} ` + response,
errorMethod: () => {
this.closeModal()
this.isSubmitted = false
},
loadingMessage: this.$t('message.add.vpn.customer.gateway.processing'),
catchMessage: this.$t('error.fetching.async.job.result'),
catchMethod: () => {
this.closeModal()
this.isSubmitted = false
}
})
this.closeModal()
this.formRef.value.resetFields()
}).catch(error => {
console.error(error)
this.$message.error(this.$t('message.add.vpn.customer.gateway.failed'))
this.isSubmitted = false
handleSubmit ({ payload }) {
postAPI('createVpnCustomerGateway', payload).then(response => {
this.$pollJob({
jobId: response.createvpncustomergatewayresponse.jobid,
title: this.$t('message.add.vpn.customer.gateway'),
description: payload.name,
successMessage: this.$t('message.success.add.vpn.customer.gateway'),
successMethod: () => {
this.closeModal()
},
errorMessage: this.$t('message.create.vpn.customer.gateway.failed'),
errorMethod: () => {
this.closeModal()
},
loadingMessage: this.$t('message.add.vpn.customer.gateway.processing'),
catchMessage: this.$t('error.fetching.async.job.result'),
catchMethod: () => {
this.closeModal()
}
})
this.closeModal()
}).catch(error => {
this.formRef.value.scrollToField(error.errorFields[0].name)
this.$refs.vpnCustomerGatewayForm.resetSubmission()
const errResponse = error.response?.data?.createvpncustomergatewayresponse
const errText = errResponse?.errortext || this.$t('message.create.vpn.customer.gateway.failed')
this.$message.error(errText)
})
}
}
}
</script>
<style lang="scss" scoped>
.form-layout {
width: 500px;
}
</style>

View File

@ -0,0 +1,129 @@
// 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.
<template>
<vpn-customer-gateway
ref="vpnCustomerGatewayForm"
:initial-values="initialValues"
:apiParams="apiParams"
@submit="handleSubmit"
@cancel="closeModal"
/>
</template>
<script>
import { postAPI } from '@/api'
import VpnCustomerGateway from './VpnCustomerGateway.vue'
export default {
name: 'UpdateVpnCustomerGateway',
components: {
VpnCustomerGateway
},
props: {
resource: {
type: Object,
required: true
}
},
beforeCreate () {
this.apiParams = this.$getApiParams('updateVpnCustomerGateway')
},
computed: {
initialValues () {
const ikepolicy = this.parseIkePolicy(this.resource.ikepolicy)
const esppolicy = this.parseEspPolicy(this.resource.esppolicy)
return {
id: this.resource.id,
name: this.resource.name,
domainid: this.resource.domainid,
gateway: this.resource.gateway,
cidrlist: this.resource.cidrlist,
ipsecpsk: this.resource.ipsecpsk,
ikeEncryption: ikepolicy.encryption,
ikeHash: ikepolicy.hash,
ikeDh: ikepolicy.dh,
espEncryption: esppolicy.encryption,
espHash: esppolicy.hash,
espDh: esppolicy.dh,
ikelifetime: this.resource.ikelifetime,
esplifetime: this.resource.esplifetime,
dpd: this.resource.dpd,
splitconnections: this.resource.splitconnections,
forceencap: this.resource.forceencap,
ikeversion: this.resource.ikeversion
}
}
},
methods: {
closeModal () {
this.$emit('close-action')
},
parseIkePolicy (ikePolicy) {
console.log('ikePolicy', ikePolicy)
if (!ikePolicy) return { encryption: null, hash: null, dh: null }
const parts = ikePolicy.split(';')
const cipherHash = parts[0] || ''
const dhGroup = parts[1] || null
const cipherHashParts = cipherHash.split('-')
const encryption = cipherHashParts[0] || null
const hash = cipherHashParts[1] || null
return { encryption, hash, dh: dhGroup }
},
parseEspPolicy (espPolicy) {
if (!espPolicy) return { encryption: null, hash: null, dh: null }
const parts = espPolicy.split(';')
const cipherHash = parts[0] || ''
const dhGroup = parts[1] || null
const cipherHashParts = cipherHash.split('-')
const encryption = cipherHashParts[0] || null
const hash = cipherHashParts[1] || null
return { encryption, hash, dh: dhGroup }
},
handleSubmit ({ payload }) {
postAPI('updateVpnCustomerGateway', {
id: this.resource.id,
...payload
}).then(response => {
this.$pollJob({
jobId: response.updatevpncustomergatewayresponse.jobid,
title: this.$t('message.update.vpn.customer.gateway'),
description: payload.name,
successMessage: this.$t('message.success.update.vpn.customer.gateway'),
successMethod: () => {
this.closeModal()
},
errorMessage: this.$t('message.update.vpn.customer.gateway.failed'),
errorMethod: () => {
this.closeModal()
},
loadingMessage: this.$t('message.update.vpn.customer.gateway.processing'),
catchMessage: this.$t('error.fetching.async.job.result'),
catchMethod: () => {
this.closeModal()
}
})
this.closeModal()
}).catch(error => {
this.$refs.vpnCustomerGatewayForm.resetSubmission()
const errResponse = error.response?.data?.createvpncustomergatewayresponse
const errText = errResponse?.errortext || this.$t('message.update.vpn.customer.gateway.failed')
this.$message.error(errText)
})
}
}
}
</script>

View File

@ -0,0 +1,581 @@
// 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.
<template>
<div>
<a-form
class="form-layout"
:ref="formRef"
:model="form"
:rules="rules"
layout="vertical"
@finish="handleSubmit"
v-ctrl-enter="handleSubmit"
>
<a-form-item ref="name" name="name">
<template #label>
<tooltip-label :title="$t('label.name')" :tooltip="apiParams.name.description"/>
</template>
<a-input
v-model:value="form.name"
:placeholder="apiParams.name.description"
v-focus="true" />
</a-form-item>
<a-form-item ref="gateway" name="gateway">
<template #label>
<tooltip-label :title="$t('label.gateway')" :tooltip="apiParams.gateway.description"/>
</template>
<a-input
v-model:value="form.gateway"
:placeholder="apiParams.gateway.description" />
</a-form-item>
<a-form-item ref="cidrlist" name="cidrlist">
<template #label>
<tooltip-label :title="$t('label.cidrlist')" :tooltip="apiParams.cidrlist.description"/>
</template>
<a-input
v-model:value="form.cidrlist"
:placeholder="apiParams.cidrlist.description" />
</a-form-item>
<a-form-item ref="ipsecpsk" name="ipsecpsk">
<template #label>
<tooltip-label :title="$t('label.ipsecpsk')" :tooltip="apiParams.ipsecpsk.description"/>
</template>
<a-input
v-model:value="form.ipsecpsk"
:placeholder="apiParams.ipsecpsk.description" />
</a-form-item>
<a-form-item ref="ikeEncryption" name="ikeEncryption" :label="$t('label.ikeencryption')">
<a-select
v-model:value="form.ikeEncryption"
showSearch
optionFilterProp="value"
:loading="loadingParameters"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option :value="algo" v-for="(algo, idx) in allowedEncryptionAlgos" :key="idx">
{{ algo }}
<a-tooltip v-if="isObsolete('encryption', algo)" :title="$t('message.vpn.customer.gateway.obsolete.parameter.tooltip')">
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
</a-tooltip>
</a-select-option>
</a-select>
<template #extra v-if="isExcluded('encryption', form.ikeEncryption)">
<span>
<warning-outlined :style="{ color: $config.theme['@error-color'] }" />
{{ form.ikeEncryption }} {{ $t('message.vpn.customer.gateway.excluded.parameter') }}
</span>
</template>
<template #extra v-else-if="isObsolete('encryption', form.ikeEncryption)">
<span>
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
{{ form.ikeEncryption }} {{ $t('message.vpn.customer.gateway.obsolete.parameter') }}
</span>
</template>
</a-form-item>
<a-form-item ref="ikeHash" name="ikeHash" :label="$t('label.ikehash')">
<a-select
v-model:value="form.ikeHash"
showSearch
optionFilterProp="value"
:loading="loadingParameters"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option :value="h" v-for="(h, idx) in allowedHashingAlgos" :key="idx">
{{ h }}
<a-tooltip v-if="isObsolete('hash', h)" :title="$t('message.vpn.customer.gateway.obsolete.parameter.tooltip')">
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
</a-tooltip>
</a-select-option>
</a-select>
<template #extra v-if="isExcluded('hash', form.ikeHash)">
<span>
<warning-outlined :style="{ color: $config.theme['@error-color'] }" />
{{ form.ikeHash }} {{ $t('message.vpn.customer.gateway.excluded.parameter') }}
</span>
</template>
<template #extra v-else-if="isObsolete('hash', form.ikeHash)">
<span>
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
{{ form.ikeHash }} {{ $t('message.vpn.customer.gateway.obsolete.parameter') }}
</span>
</template>
</a-form-item>
<a-form-item ref="ikeversion" name="ikeversion">
<template #label>
<tooltip-label :title="$t('label.ikeversion')" :tooltip="apiParams.ikeversion.description"/>
</template>
<a-select
v-model:value="form.ikeversion"
showSearch
optionFilterProp="value"
:loading="loadingParameters"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option :value="vers" v-for="(vers, idx) in allowedIkeVersions" :key="idx">
{{ vers }}
<a-tooltip v-if="isObsolete('ikeversion', vers)" :title="$t('message.vpn.customer.gateway.obsolete.parameter.tooltip')">
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
</a-tooltip>
</a-select-option>
</a-select>
<template #extra v-if="isExcluded('ikeversion', form.ikeversion)">
<span>
<warning-outlined :style="{ color: $config.theme['@error-color'] }" />
{{ form.ikeversion }} {{ $t('message.vpn.customer.gateway.excluded.parameter') }}
</span>
</template>
<template #extra v-else-if="isObsolete('ikeversion', form.ikeversion)">
<span>
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
{{ form.ikeversion }} {{ $t('message.vpn.customer.gateway.obsolete.parameter') }}
</span>
</template>
</a-form-item>
<a-form-item ref="ikeDh" name="ikeDh" :label="$t('label.ikedh')">
<a-select
v-model:value="form.ikeDh"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option
:value="group + '(' + DHGroups[group] + ')'"
v-for="(group, idx) in allowedDhGroupKeys"
:key="idx"
:label="group + '(' + DHGroups[group] + ')'">
<div v-if="group !== ''">
{{ group+"("+DHGroups[group]+")" }}
<a-tooltip v-if="isObsolete('dh', group)" :title="$t('message.vpn.customer.gateway.obsolete.parameter.tooltip')">
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
</a-tooltip>
</div>
</a-select-option>
</a-select>
<template #extra v-if="isExcluded('dh', form.ikeDh)">
<span>
<warning-outlined :style="{ color: $config.theme['@error-color'] }" />
{{ form.ikeDh }} {{ $t('message.vpn.customer.gateway.excluded.parameter') }}
</span>
</template>
<template #extra v-else-if="isObsolete('dh', form.ikeDh)">
<span>
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
{{ form.ikeDh }} {{ $t('message.vpn.customer.gateway.obsolete.parameter') }}
</span>
</template>
</a-form-item>
<a-form-item ref="espEncryption" name="espEncryption" :label="$t('label.espencryption')">
<a-select
v-model:value="form.espEncryption"
showSearch
optionFilterProp="value"
:loading="loadingParameters"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option :value="algo" v-for="(algo, idx) in allowedEncryptionAlgos" :key="idx">
{{ algo }}
<a-tooltip v-if="isObsolete('encryption', algo)" :title="$t('message.vpn.customer.gateway.obsolete.parameter.tooltip')">
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
</a-tooltip>
</a-select-option>
</a-select>
<template #extra v-if="isExcluded('encryption', form.espEncryption)">
<span>
<warning-outlined :style="{ color: $config.theme['@error-color'] }" />
{{ form.espEncryption }} {{ $t('message.vpn.customer.gateway.excluded.parameter') }}
</span>
</template>
<template #extra v-else-if="isObsolete('encryption', form.espEncryption)">
<span>
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
{{ form.espEncryption }} {{ $t('message.vpn.customer.gateway.obsolete.parameter') }}
</span>
</template>
</a-form-item>
<a-form-item ref="espHash" name="espHash" :label="$t('label.esphash')">
<a-select
v-model:value="form.espHash"
showSearch
optionFilterProp="value"
:loading="loadingParameters"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option :value="h" v-for="(h, idx) in allowedHashingAlgos" :key="idx">
{{ h }}
<a-tooltip v-if="isObsolete('hash', h)" :title="$t('message.vpn.customer.gateway.obsolete.parameter.tooltip')">
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
</a-tooltip>
</a-select-option>
</a-select>
<template #extra v-if="isExcluded('hash', form.espHash)">
<span>
<warning-outlined :style="{ color: $config.theme['@error-color'] }" />
{{ form.espHash }} {{ $t('message.vpn.customer.gateway.excluded.parameter') }}
</span>
</template>
<template #extra v-else-if="isObsolete('hash', form.espHash)">
<span>
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
{{ form.espHash }} {{ $t('message.vpn.customer.gateway.obsolete.parameter') }}
</span>
</template>
</a-form-item>
<a-form-item ref="perfectForwardSecrecy" name="perfectForwardSecrecy" :label="$t('label.perfectforwardsecrecy')">
<a-select
v-model:value="form.perfectForwardSecrecy"
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option
:value="group + '(' + DHGroups[group] + ')'"
v-for="(group, idx) in allowedDhGroupKeys"
:key="idx"
:label="group === '' ? DHGroups[group] : group + '(' + DHGroups[group] + ')'">
<div v-if="group === ''">
{{ DHGroups[group] }}
</div>
<div v-else>
{{ group+"("+DHGroups[group]+")" }}
<a-tooltip v-if="isObsolete('dh', group)" :title="$t('message.vpn.customer.gateway.obsolete.parameter.tooltip')">
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
</a-tooltip>
</div>
</a-select-option>
</a-select>
<template #extra v-if="isExcluded('dh', form.perfectForwardSecrecy)">
<span>
<warning-outlined :style="{ color: $config.theme['@error-color'] }" />
{{ form.perfectForwardSecrecy }} {{ $t('message.vpn.customer.gateway.excluded.parameter') }}
</span>
</template>
<template #extra v-else-if="isObsolete('dh', form.perfectForwardSecrecy)">
<span>
<warning-outlined :style="{ color: $config.theme['@warning-color'] }" />
{{ form.perfectForwardSecrecy }} {{ $t('message.vpn.customer.gateway.obsolete.parameter') }}
</span>
</template>
</a-form-item>
<a-form-item ref="ikelifetime" name="ikelifetime">
<template #label>
<tooltip-label :title="$t('label.ikelifetime')" :tooltip="apiParams.ikelifetime.description"/>
</template>
<a-input
v-model:value="form.ikelifetime"
:placeholder="apiParams.ikelifetime.description"/>
</a-form-item>
<a-form-item ref="esplifetime" name="esplifetime">
<template #label>
<tooltip-label :title="$t('label.esplifetime')" :tooltip="apiParams.esplifetime.description"/>
</template>
<a-input
v-model:value="form.esplifetime"
:placeholder="apiParams.esplifetime.description"/>
</a-form-item>
<a-form-item ref="dpd" name="dpd">
<template #label>
<tooltip-label :title="$t('label.dpd')" :tooltip="apiParams.dpd.description"/>
</template>
<a-switch v-model:checked="form.dpd"/>
</a-form-item>
<a-form-item ref="splitconnections" name="splitconnections" v-if="form.ikeversion !== 'ikev1'">
<template #label>
<tooltip-label :title="$t('label.splitconnections')" :tooltip="apiParams.splitconnections.description"/>
</template>
<a-switch v-model:checked="form.splitconnections"/>
</a-form-item>
<a-form-item ref="forceencap" name="forceencap">
<template #label>
<tooltip-label :title="$t('label.forceencap')" :tooltip="apiParams.forceencap.description"/>
</template>
<a-switch v-model:checked="form.forceencap"/>
</a-form-item>
<div class="action-button">
<a-button @click="$emit('cancel')">
{{ $t('label.cancel') }}
</a-button>
<a-button type="primary" @click="handleSubmit" html-type="submit">
{{ $t('label.ok') }}
</a-button>
</div>
</a-form>
</div>
</template>
<script>
import { getAPI } from '@/api'
import { ref, reactive, toRaw } from 'vue'
import { mixinForm } from '@/utils/mixin'
import TooltipLabel from '@/components/widgets/TooltipLabel'
export default {
name: 'VpnCustomerGateway',
mixins: [mixinForm],
components: {
TooltipLabel
},
props: {
initialValues: {
type: Object,
default: () => ({})
},
apiParams: {
type: Object,
default: () => {}
}
},
data () {
return {
encryptionAlgo: [
'aes128',
'aes192',
'aes256',
'3des'
],
hash: [
'sha1',
'sha256',
'sha384',
'sha512',
'md5'
],
ikeVersions: [
'ike',
'ikev1',
'ikev2'
],
DHGroups: {
'Group 2': 'modp1024',
'Group 5': 'modp1536',
'Group 14': 'modp2048',
'Group 15': 'modp3072',
'Group 16': 'modp4096',
'Group 17': 'modp6144',
'Group 18': 'modp8192'
},
ikeDhGroupInitialKey: 'Group 5',
isSubmitted: false,
ikeversion: 'ike',
allowedEncryptionAlgos: [],
allowedHashingAlgos: [],
allowedIkeVersions: [],
allowedDhGroupKeys: [],
allowedDhGroupValues: [],
obsoleteEncryptionAlgos: [],
obsoleteHashingAlgos: [],
obsoleteIkeVersions: [],
obsoleteDhGroups: [],
loadingParameters: false
}
},
created () {
this.initForm()
this.fetchVpnCustomerGatewayParameters()
},
methods: {
initForm () {
this.formRef = ref()
const defaults = {
name: '',
gateway: '',
cidrlist: '',
ipsecpsk: '',
ikeEncryption: '',
ikeHash: '',
ikeversion: '',
ikeDh: '',
espEncryption: '',
espHash: '',
perfectForwardSecrecy: 'None',
ikelifetime: '86400',
esplifetime: '3600',
dpd: false,
splitconnections: false,
forceencap: false
}
this.form = reactive({
...defaults,
...this.initialValues
})
if (this.initialValues.ikeDh) {
const ikeDhKey = Object.keys(this.DHGroups).find(key => this.DHGroups[key] === this.initialValues.ikeDh)
this.form.ikeDh = ikeDhKey + '(' + this.initialValues.ikeDh + ')'
}
if (this.initialValues.espDh) {
const espDhKey = Object.keys(this.DHGroups).find(key => this.DHGroups[key] === this.initialValues.espDh)
this.form.perfectForwardSecrecy = espDhKey + '(' + this.initialValues.espDh + ')'
}
this.rules = reactive({
name: [{ required: true, message: this.$t('label.required') }],
gateway: [{ required: true, message: this.$t('label.required') }],
cidrlist: [{ required: true, message: this.$t('label.required') }],
ipsecpsk: [{ required: true, message: this.$t('label.required') }]
})
},
closeModal () {
this.$emit('close-action')
},
resetSubmission () {
this.isSubmitted = false
},
buildPolicies (values) {
let ikepolicy = values.ikeEncryption + '-' + values.ikeHash + ';'
ikepolicy += (values.ikeDh.split('(')[1]).split(')')[0]
let esppolicy = values.espEncryption + '-' + values.espHash
if (values.perfectForwardSecrecy !== 'None') {
esppolicy += ';' + (values.perfectForwardSecrecy.split('(')[1]).split(')')[0]
}
return { ikepolicy, esppolicy }
},
isObsolete (type, value) {
switch (type) {
case 'encryption':
return this.obsoleteEncryptionAlgos.some(v => v.toLowerCase() === value)
case 'hash':
return this.obsoleteHashingAlgos.some(v => v.toLowerCase() === value)
case 'ikeversion':
return this.obsoleteIkeVersions.some(v => v.toLowerCase() === value)
case 'dh': {
const key = value.split('(')[0]
return this.obsoleteDhGroups.some(v => v.toLowerCase() === this.DHGroups[key])
}
default:
return false
}
},
isExcluded (type, value) {
switch (type) {
case 'encryption':
return !this.allowedEncryptionAlgos.some(v => v.toLowerCase() === value)
case 'hash':
return !this.allowedHashingAlgos.some(v => v.toLowerCase() === value)
case 'ikeversion':
return !this.allowedIkeVersions.some(v => v.toLowerCase() === value)
case 'dh': {
if (value === '' || value === 'None') return false
const key = value.split('(')[0]
return !this.allowedDhGroupValues.some(v => v.toLowerCase() === this.DHGroups[key])
}
default:
return false
}
},
async fetchVpnCustomerGatewayParameters () {
const getParam = (obj, key) => {
const val = obj?.[key.toLowerCase()]
return typeof val === 'string'
? val.split(',').map(s => s.trim()).filter(Boolean)
: []
}
const getAllowed = (baseList, excludedList) => {
const excluded = new Set(excludedList.map(v => v.toLowerCase()))
return baseList.filter(item => !excluded.has(item.toLowerCase()))
}
this.loadingParameters = true
const response = await getAPI('listCapabilities', { domainid: this.initialValues.domainid })
const capability = response.listcapabilitiesresponse?.capability || {}
const parameters = capability.vpncustomergatewayparameters || {}
const excludedEnc = getParam(parameters, 'excludedEncryptionAlgorithms')
const excludedHash = getParam(parameters, 'excludedHashingAlgorithms')
const excludedIke = getParam(parameters, 'excludedIkeVersions')
const excludedDh = getParam(parameters, 'excludedDhGroups')
this.allowedEncryptionAlgos = getAllowed(this.encryptionAlgo, excludedEnc)
this.allowedHashingAlgos = getAllowed(this.hash, excludedHash)
this.allowedIkeVersions = getAllowed(this.ikeVersions, excludedIke)
const dhValues = Object.values(this.DHGroups)
this.allowedDhGroupValues = getAllowed(dhValues, excludedDh)
this.allowedDhGroupKeys = Object.entries(this.DHGroups)
.filter(([key, value]) => this.allowedDhGroupValues.includes(value))
.map(([key]) => key)
this.form.ikeEncryption = this.form.ikeEncryption || this.allowedEncryptionAlgos[0]
this.form.ikeHash = this.form.ikeHash || this.allowedHashingAlgos[0]
this.form.ikeversion = this.form.ikeversion || this.allowedIkeVersions[0]
this.form.espEncryption = this.form.espEncryption || this.allowedEncryptionAlgos[0]
this.form.espHash = this.form.espHash || this.allowedHashingAlgos[0]
if (!this.initialValues.ikeDh) {
if (this.allowedDhGroupKeys.includes(this.ikeDhGroupInitialKey)) {
this.form.ikeDh = this.ikeDhGroupInitialKey + '(' + this.DHGroups[this.ikeDhGroupInitialKey] + ')'
} else {
this.form.ikeDh = this.allowedDhGroupKeys[0] + '(' + this.DHGroups[this.allowedDhGroupKeys[0]] + ')'
}
}
this.obsoleteEncryptionAlgos = getParam(parameters, 'obsoleteEncryptionAlgorithms')
this.obsoleteHashingAlgos = getParam(parameters, 'obsoleteHashingAlgorithms')
this.obsoleteIkeVersions = getParam(parameters, 'obsoleteIkeVersions')
this.obsoleteDhGroups = getParam(parameters, 'obsoleteDhGroups')
this.loadingParameters = false
},
handleSubmit (e) {
if (e && e.preventDefault) {
e.preventDefault()
}
if (this.isSubmitted) return
this.formRef.value.validate().then(() => {
this.isSubmitted = true
const formRaw = toRaw(this.form)
const values = this.handleRemoveFields(formRaw)
const { ikepolicy, esppolicy } = this.buildPolicies(values)
const payload = {
name: values.name,
gateway: values.gateway,
cidrlist: values.cidrlist,
ipsecpsk: values.ipsecpsk,
ikelifetime: values.ikelifetime,
esplifetime: values.esplifetime,
dpd: values.dpd,
forceencap: values.forceencap,
ikepolicy: ikepolicy,
esppolicy: esppolicy,
splitconnections: values.splitconnections,
ikeversion: values.ikeversion
}
this.$emit('submit', {
payload,
rawValues: values
})
}).catch(error => {
this.formRef.value.scrollToField(error.errorFields[0].name)
})
}
}
}
</script>
<style lang="scss" scoped>
.form-layout {
width: 500px;
}
</style>