From 6143582de404bc723982f9cd5b692d1a0a015e62 Mon Sep 17 00:00:00 2001 From: Greg Goodrich Date: Wed, 18 Mar 2020 10:15:20 -0500 Subject: [PATCH] Adding VPN options for IKE version and IKE split connections. IKE version allows selecting ike (autoselect), ikev1, or ikev2. Split connections gives an option of separating the first right subnet from the rest, and kicking out individual statements for each right subnet for better cross-compatibility. update per PR suggestion --- .../network/Site2SiteCustomerGateway.java | 4 ++ .../apache/cloudstack/api/ApiConstants.java | 2 + .../user/vpn/CreateVpnCustomerGatewayCmd.java | 14 +++++ .../user/vpn/UpdateVpnCustomerGatewayCmd.java | 14 +++++ .../Site2SiteCustomerGatewayResponse.java | 16 +++++ .../Site2SiteVpnConnectionResponse.java | 16 +++++ .../api/routing/Site2SiteVpnCfgCommand.java | 23 ++++++- .../facade/Site2SiteVpnConfigItem.java | 2 +- .../virtualnetwork/model/Site2SiteVpn.java | 24 ++++++- .../VirtualRoutingResourceTest.java | 6 +- .../dao/Site2SiteCustomerGatewayVO.java | 28 ++++++++- .../META-INF/db/schema-41510to41600.sql | 4 ++ .../java/com/cloud/api/ApiResponseHelper.java | 4 ++ .../network/router/CommandSetupHelper.java | 4 +- .../network/vpn/Site2SiteVpnManagerImpl.java | 62 ++++++++++++++++--- ...irtualNetworkApplianceManagerImplTest.java | 2 +- systemvm/debian/opt/cloud/bin/configure.py | 35 +++++++++-- systemvm/debian/opt/cloud/bin/ipsectunnel.sh | 35 +++++++++-- ui/public/locales/ca.json | 2 + ui/public/locales/en.json | 4 ++ ui/public/locales/it_IT.json | 2 + ui/public/locales/nb_NO.json | 2 + ui/public/locales/nl_NL.json | 2 + ui/public/locales/pl.json | 2 + ui/src/config/section/network.js | 6 +- .../network/CreateVpnCustomerGateway.vue | 37 ++++++++++- 26 files changed, 320 insertions(+), 32 deletions(-) diff --git a/api/src/main/java/com/cloud/network/Site2SiteCustomerGateway.java b/api/src/main/java/com/cloud/network/Site2SiteCustomerGateway.java index f9a88bdd8453..de83fddf8132 100644 --- a/api/src/main/java/com/cloud/network/Site2SiteCustomerGateway.java +++ b/api/src/main/java/com/cloud/network/Site2SiteCustomerGateway.java @@ -43,5 +43,9 @@ public interface Site2SiteCustomerGateway extends ControlledEntity, Identity, In public Date getRemoved(); + public Boolean getSplitConnections(); + + public String getIkeVersion(); + String getName(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 5c3050c85ae5..4d7accbc75d2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -628,12 +628,14 @@ public class ApiConstants { public static final String GUEST_IP = "guestip"; public static final String REMOVED = "removed"; public static final String COMPLETED = "completed"; + public static final String IKE_VERSION = "ikeversion"; public static final String IKE_POLICY = "ikepolicy"; public static final String ESP_POLICY = "esppolicy"; public static final String IKE_LIFETIME = "ikelifetime"; public static final String ESP_LIFETIME = "esplifetime"; public static final String DPD = "dpd"; public static final String FORCE_ENCAP = "forceencap"; + public static final String SPLIT_CONNECTIONS = "splitconnections"; public static final String FOR_VPC = "forvpc"; public static final String SHRINK_OK = "shrinkok"; public static final String NICIRA_NVP_DEVICE_ID = "nvpdeviceid"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java index 6f591756d2e9..f99ee54c695f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java @@ -92,6 +92,12 @@ public class CreateVpnCustomerGatewayCmd extends BaseAsyncCmd { description = "create site-to-site VPN customer gateway for the project", since = "4.6") private Long projectId; + @Parameter(name = ApiConstants.SPLIT_CONNECTIONS, type = CommandType.BOOLEAN, required = false, description = "For IKEv2, whether to split multiple right subnet cidrs into multiple connection statements.") + private Boolean splitConnections; + + @Parameter(name = ApiConstants.IKE_VERSION, type = CommandType.STRING, required = false, description = "Which IKE Version to use, one of ike (autoselect), ikev1, or ikev2. Defaults to ike") + private String ikeVersion; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -146,6 +152,14 @@ public Long getProjectId() { return projectId; } + public Boolean getSplitConnections() { + return splitConnections; + } + + public String getIkeVersion() { + return ikeVersion; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java index d7bf5c4b557e..cb8fb9ceae42 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java @@ -94,6 +94,12 @@ public class UpdateVpnCustomerGatewayCmd extends BaseAsyncCmd { + "gateway associated with the account for the specified domain.") private Long domainId; + @Parameter(name = ApiConstants.SPLIT_CONNECTIONS, type = CommandType.BOOLEAN, required = false, description = "For IKEv2, whether to split multiple right subnet cidrs into multiple connection statements.") + private Boolean splitConnections; + + @Parameter(name = ApiConstants.IKE_VERSION, type = CommandType.STRING, required = false, description = "Which IKE Version to use, one of ike (autoselect), ikev1, or ikev2. Defaults to ike") + private String ikeVersion; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -140,6 +146,14 @@ public Boolean getDpd() { public Boolean getEncap() { return encap; } + public boolean getSplitConnections() { + return splitConnections; + } + + public String getIkeVersion() { + return ikeVersion; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteCustomerGatewayResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteCustomerGatewayResponse.java index 8128405cdaee..88e0e1600e5b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteCustomerGatewayResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteCustomerGatewayResponse.java @@ -102,6 +102,14 @@ public class Site2SiteCustomerGatewayResponse extends BaseResponse implements Co @Param(description = "the date and time the host was removed") private Date removed; + @SerializedName(ApiConstants.SPLIT_CONNECTIONS) + @Param(description = "For IKEv2, whether to split multiple right subnet cidrs into multiple connection statements.") + private Boolean splitConnections; + + @SerializedName(ApiConstants.IKE_VERSION) + @Param(description = "Which IKE Version to use, one of ike (autoselect), ikev1, or ikev2. Defaults to ike") + private String ikeVersion; + public void setId(String id) { this.id = id; } @@ -148,6 +156,14 @@ public void setDpd(Boolean dpd) { public void setEncap(Boolean encap) { this.encap = encap; } + public void setSplitConnections(Boolean splitConnections) { + this.splitConnections = splitConnections; + } + + public void setIkeVersion(String ikeVersion) { + this.ikeVersion = ikeVersion; + } + public void setRemoved(Date removed) { this.removed = removed; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java index edaa1b262a74..1f7509239d1a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java @@ -132,6 +132,14 @@ public class Site2SiteVpnConnectionResponse extends BaseResponse implements Cont @Param(description = "is connection for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; + @SerializedName(ApiConstants.SPLIT_CONNECTIONS) + @Param(description = "Split multiple remote networks into multiple phase 2 SAs. Often used with Cisco some products.") + private Boolean splitConnections; + + @SerializedName(ApiConstants.IKE_VERSION) + @Param(description = "Which IKE Version to use, one of ike (autoselect), ikev1, or ikev2. Defaults to ike") + private String ikeVersion; + public void setId(String id) { this.id = id; } @@ -200,6 +208,14 @@ public void setRemoved(Date removed) { this.removed = removed; } + public void setSplitConnections(Boolean splitConnections) { + this.splitConnections = splitConnections; + } + + public void setIkeVersion(String ikeVersion) { + this.ikeVersion = ikeVersion; + } + @Override public void setAccountName(String accountName) { this.accountName = accountName; diff --git a/core/src/main/java/com/cloud/agent/api/routing/Site2SiteVpnCfgCommand.java b/core/src/main/java/com/cloud/agent/api/routing/Site2SiteVpnCfgCommand.java index 685cf4049c79..f679b75d155d 100644 --- a/core/src/main/java/com/cloud/agent/api/routing/Site2SiteVpnCfgCommand.java +++ b/core/src/main/java/com/cloud/agent/api/routing/Site2SiteVpnCfgCommand.java @@ -35,6 +35,8 @@ public class Site2SiteVpnCfgCommand extends NetworkElementCommand { private boolean dpd; private boolean passive; private boolean encap; + private boolean splitConnections; + private String ikeVersion; @Override public boolean executeInSequence() { @@ -46,7 +48,8 @@ public Site2SiteVpnCfgCommand() { } public Site2SiteVpnCfgCommand(boolean create, String localPublicIp, String localPublicGateway, String localGuestCidr, String peerGatewayIp, String peerGuestCidrList, - String ikePolicy, String espPolicy, String ipsecPsk, Long ikeLifetime, Long espLifetime, Boolean dpd, boolean passive, boolean encap) { + String ikePolicy, String espPolicy, String ipsecPsk, Long ikeLifetime, Long espLifetime, Boolean dpd, boolean passive, boolean encap, + boolean splitConnections, String ikeVersion) { this.create = create; this.setLocalPublicIp(localPublicIp); this.setLocalPublicGateway(localPublicGateway); @@ -61,6 +64,8 @@ public Site2SiteVpnCfgCommand(boolean create, String localPublicIp, String local this.dpd = dpd; this.passive = passive; this.encap = encap; + this.splitConnections = splitConnections; + this.ikeVersion = ikeVersion; } public boolean isCreate() { @@ -174,4 +179,20 @@ public boolean isPassive() { public void setPassive(boolean passive) { this.passive = passive; } + + public boolean getSplitConnections() { + return splitConnections; + } + + public void setSplitConnections(boolean splitConnections) { + this.splitConnections = splitConnections; + } + + public String getIkeVersion() { + return ikeVersion; + } + + public void setIkeVersion(String ikeVersion) { + this.ikeVersion = ikeVersion; + } } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/Site2SiteVpnConfigItem.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/Site2SiteVpnConfigItem.java index 5bb466c59352..badd0d376d12 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/Site2SiteVpnConfigItem.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/Site2SiteVpnConfigItem.java @@ -36,7 +36,7 @@ public List generateConfig(final NetworkElementCommand cmd) { final Site2SiteVpn site2siteVpn = new Site2SiteVpn(command.getLocalPublicIp(), command.getLocalGuestCidr(), command.getLocalPublicGateway(), command.getPeerGatewayIp(), command.getPeerGuestCidrList(), command.getEspPolicy(), command.getIkePolicy(), command.getIpsecPsk(), command.getIkeLifetime(), command.getEspLifetime(), command.isCreate(), command.getDpd(), - command.isPassive(), command.getEncap()); + command.isPassive(), command.getEncap(), command.getSplitConnections(), command.getIkeVersion()); return generateConfigItems(site2siteVpn); } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/Site2SiteVpn.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/Site2SiteVpn.java index 232e99f099ac..9057bf4f2936 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/Site2SiteVpn.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/Site2SiteVpn.java @@ -21,9 +21,9 @@ public class Site2SiteVpn extends ConfigBase { - private String localPublicIp, localGuestCidr, localPublicGateway, peerGatewayIp, peerGuestCidrList, espPolicy, ikePolicy, ipsecPsk; + private String localPublicIp, localGuestCidr, localPublicGateway, peerGatewayIp, peerGuestCidrList, espPolicy, ikePolicy, ipsecPsk, ikeVersion; private Long ikeLifetime, espLifetime; - private boolean create, dpd, passive, encap; + private boolean create, dpd, passive, encap, splitConnections; public Site2SiteVpn() { super(ConfigBase.SITE2SITEVPN); @@ -31,7 +31,7 @@ public Site2SiteVpn() { public Site2SiteVpn(String localPublicIp, String localGuestCidr, String localPublicGateway, String peerGatewayIp, String peerGuestCidrList, String espPolicy, String ikePolicy, - String ipsecPsk, Long ikeLifetime, Long espLifetime, boolean create, Boolean dpd, boolean passive, boolean encap) { + String ipsecPsk, Long ikeLifetime, Long espLifetime, boolean create, Boolean dpd, boolean passive, boolean encap, boolean splitConnections, String ikeVersion) { super(ConfigBase.SITE2SITEVPN); this.localPublicIp = localPublicIp; this.localGuestCidr = localGuestCidr; @@ -47,6 +47,8 @@ public Site2SiteVpn(String localPublicIp, String localGuestCidr, String localPub this.dpd = dpd; this.passive = passive; this.encap = encap; + this.splitConnections = splitConnections; + this.ikeVersion = ikeVersion; } public String getLocalPublicIp() { @@ -161,4 +163,20 @@ public void setEncap(boolean encap) { this.encap = encap; } + public boolean getSplitConnections() { + return splitConnections; + } + + public void setSplitConnections(boolean splitConnections) { + this.splitConnections = splitConnections; + } + + public String getIkeVersion() { + return ikeVersion; + } + + public void setIkeVersion(String ikeVersion) { + this.ikeVersion = ikeVersion; + } + } diff --git a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java index 200f266b9251..6eb30aeeed9e 100644 --- a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java +++ b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java @@ -495,17 +495,17 @@ private void verifyArgs(final SetMonitorServiceCommand cmd, final String script, public void testSite2SiteVpnCfgCommand() { _count = 0; - Site2SiteVpnCfgCommand cmd = new Site2SiteVpnCfgCommand(true, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), true, false, false); + Site2SiteVpnCfgCommand cmd = new Site2SiteVpnCfgCommand(true, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), true, false, false, false, "ike"); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); Answer answer = _resource.executeRequest(cmd); assertTrue(answer.getResult()); - cmd = new Site2SiteVpnCfgCommand(true, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), false, true, false); + cmd = new Site2SiteVpnCfgCommand(true, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), false, true, false, false, "ike"); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); answer = _resource.executeRequest(cmd); assertTrue(answer.getResult()); - cmd = new Site2SiteVpnCfgCommand(false, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), false, true, false); + cmd = new Site2SiteVpnCfgCommand(false, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), false, true, false, false, "ike"); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); answer = _resource.executeRequest(cmd); assertTrue(answer.getResult()); diff --git a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteCustomerGatewayVO.java b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteCustomerGatewayVO.java index f1d3ef32712d..c8241518d63e 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteCustomerGatewayVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteCustomerGatewayVO.java @@ -79,6 +79,12 @@ public class Site2SiteCustomerGatewayVO implements Site2SiteCustomerGateway { @Column(name = "account_id") private Long accountId; + @Column(name = "split_connections") + private boolean splitConnections; + + @Column(name = "ike_version") + private String ikeVersion; + @Column(name = GenericDao.REMOVED_COLUMN) private Date removed; @@ -86,7 +92,7 @@ public Site2SiteCustomerGatewayVO() { } public Site2SiteCustomerGatewayVO(String name, long accountId, long domainId, String gatewayIp, String guestCidrList, String ipsecPsk, String ikePolicy, - String espPolicy, long ikeLifetime, long espLifetime, boolean dpd, boolean encap) { + String espPolicy, long ikeLifetime, long espLifetime, boolean dpd, boolean encap, boolean splitConnections, String ikeVersion) { this.name = name; this.gatewayIp = gatewayIp; this.guestCidrList = guestCidrList; @@ -100,6 +106,8 @@ public Site2SiteCustomerGatewayVO(String name, long accountId, long domainId, St uuid = UUID.randomUUID().toString(); this.accountId = accountId; this.domainId = domainId; + this.splitConnections = splitConnections; + this.ikeVersion = ikeVersion; } @Override @@ -221,6 +229,24 @@ public long getAccountId() { return accountId; } + @Override + public Boolean getSplitConnections() { + return splitConnections; + } + + public void setSplitConnections(Boolean splitConnections) { + this.splitConnections = splitConnections; + } + + @Override + public String getIkeVersion() { + return ikeVersion; + } + + public void setIkeVersion(String ikeVersion) { + this.ikeVersion = ikeVersion; + } + @Override public Class getEntityType() { return Site2SiteCustomerGateway.class; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41510to41600.sql b/engine/schema/src/main/resources/META-INF/db/schema-41510to41600.sql index 7168f883db67..058daa54ca93 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41510to41600.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41510to41600.sql @@ -301,3 +301,7 @@ from left join `cloud`.`resource_count` secondary_storage_count ON domain.id = secondary_storage_count.domain_id and secondary_storage_count.type = 'secondary_storage'; + +ALTER TABLE `cloud`.`s2s_customer_gateway` ADD COLUMN `ike_version` varchar(5) NOT NULL DEFAULT 'ike' COMMENT 'one of ike, ikev1, ikev2'; +ALTER TABLE `cloud`.`s2s_customer_gateway` ADD COLUMN `split_connections` int(1) NOT NULL DEFAULT 0; + diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index edbd649c9431..486b0f55e2ca 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -3208,6 +3208,8 @@ public Site2SiteCustomerGatewayResponse createSite2SiteCustomerGatewayResponse(S response.setDpd(result.getDpd()); response.setEncap(result.getEncap()); response.setRemoved(result.getRemoved()); + response.setIkeVersion(result.getIkeVersion()); + response.setSplitConnections(result.getSplitConnections()); response.setObjectName("vpncustomergateway"); populateAccount(response, result.getAccountId()); @@ -3247,6 +3249,8 @@ public Site2SiteVpnConnectionResponse createSite2SiteVpnConnectionResponse(Site2 response.setEspLifetime(customerGateway.getEspLifetime()); response.setDpd(customerGateway.getDpd()); response.setEncap(customerGateway.getEncap()); + response.setIkeVersion(customerGateway.getIkeVersion()); + response.setSplitConnections(customerGateway.getSplitConnections()); } } diff --git a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java index 262fa4b4ff14..96c487731dc3 100644 --- a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java +++ b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java @@ -944,9 +944,11 @@ public void createSite2SiteVpnCfgCommands(final Site2SiteVpnConnection conn, fin final Long espLifetime = gw.getEspLifetime(); final Boolean dpd = gw.getDpd(); final Boolean encap = gw.getEncap(); + final Boolean splitConnections = gw.getSplitConnections(); + final String ikeVersion = gw.getIkeVersion(); final Site2SiteVpnCfgCommand cmd = new Site2SiteVpnCfgCommand(isCreate, localPublicIp, localPublicGateway, localGuestCidr, peerGatewayIp, peerGuestCidrList, ikePolicy, - espPolicy, ipsecPsk, ikeLifetime, espLifetime, dpd, conn.isPassive(), encap); + espPolicy, ipsecPsk, ikeLifetime, espLifetime, dpd, conn.isPassive(), encap, splitConnections, ikeVersion); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId())); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId())); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); diff --git a/server/src/main/java/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java b/server/src/main/java/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java index 7fd3473a85f4..668eb114cb66 100644 --- a/server/src/main/java/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java +++ b/server/src/main/java/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java @@ -45,6 +45,7 @@ import com.cloud.event.EventTypes; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Site2SiteCustomerGateway; import com.cloud.network.Site2SiteVpnConnection; @@ -229,10 +230,20 @@ public Site2SiteCustomerGateway createCustomerGateway(CreateVpnCustomerGatewayCm throw new InvalidParameterValueException("The customer gateway with name " + name + " already existed!"); } + Boolean splitConnections = cmd.getSplitConnections(); + if (splitConnections == null) { + splitConnections = false; + } + + String ikeVersion = cmd.getIkeVersion(); + if (ikeVersion == null) { + ikeVersion = "ike"; + } + checkCustomerGatewayCidrList(peerCidrList); Site2SiteCustomerGatewayVO gw = - new Site2SiteCustomerGatewayVO(name, accountId, owner.getDomainId(), gatewayIp, peerCidrList, ipsecPsk, ikePolicy, espPolicy, ikeLifetime, espLifetime, dpd, encap); + new Site2SiteCustomerGatewayVO(name, accountId, owner.getDomainId(), gatewayIp, peerCidrList, ipsecPsk, ikePolicy, espPolicy, ikeLifetime, espLifetime, dpd, encap, splitConnections, ikeVersion); _customerGatewayDao.persist(gw); return gw; } @@ -419,14 +430,6 @@ public Site2SiteCustomerGateway updateCustomerGateway(UpdateVpnCustomerGatewayCm } _accountMgr.checkAccess(caller, null, false, gw); - List conns = _vpnConnectionDao.listByCustomerGatewayId(id); - if (conns != null) { - for (Site2SiteVpnConnection conn : conns) { - if (conn.getState() != State.Error) { - throw new InvalidParameterValueException("Unable to update customer gateway with connections in non-Error state!"); - } - } - } String name = cmd.getName(); String gatewayIp = cmd.getGatewayIp(); @@ -476,6 +479,16 @@ public Site2SiteCustomerGateway updateCustomerGateway(UpdateVpnCustomerGatewayCm encap = false; } + Boolean splitConnections = cmd.getSplitConnections(); + if (splitConnections == null) { + splitConnections = false; + } + + String ikeVersion = cmd.getIkeVersion(); + if (ikeVersion == null) { + ikeVersion = "ike"; + } + checkCustomerGatewayCidrList(guestCidrList); long accountId = gw.getAccountId(); @@ -494,7 +507,38 @@ public Site2SiteCustomerGateway updateCustomerGateway(UpdateVpnCustomerGatewayCm gw.setEspLifetime(espLifetime); gw.setDpd(dpd); gw.setEncap(encap); + gw.setSplitConnections(splitConnections); + gw.setIkeVersion(ikeVersion); _customerGatewayDao.persist(gw); + + List conns = _vpnConnectionDao.listByCustomerGatewayId(id); + if (conns != null) { + for (Site2SiteVpnConnection conn : conns) { + try { + _accountMgr.checkAccess(caller, null, false, conn); + } catch (PermissionDeniedException e) { + // Just don't restart this connection, as the user has no rights to it + // Maybe should issue a notification to the system? + s_logger.info("Site2SiteVpnManager:updateCustomerGateway() Not resetting VPN connection " + conn.getId() + " as user lacks permission"); + continue; + } + + if (conn.getState() == State.Pending) { + // Vpn connection cannot be reset when the state is Pending + continue; + } + try { + if (conn.getState() == State.Connected || conn.getState() == State.Error) { + stopVpnConnection(conn.getId()); + } + startVpnConnection(conn.getId()); + } catch (ResourceUnavailableException e) { + // Should never get here, as we are looping on the actual connections, but we must handle it regardless + continue; + } + } + } + return gw; } diff --git a/server/src/test/java/com/cloud/network/router/VirtualNetworkApplianceManagerImplTest.java b/server/src/test/java/com/cloud/network/router/VirtualNetworkApplianceManagerImplTest.java index 0ddbd845a8c7..aed4769ab964 100644 --- a/server/src/test/java/com/cloud/network/router/VirtualNetworkApplianceManagerImplTest.java +++ b/server/src/test/java/com/cloud/network/router/VirtualNetworkApplianceManagerImplTest.java @@ -278,7 +278,7 @@ public void testUpdateSite2SiteVpnConnectionState() throws Exception{ conns.add(conn); conns.add(conn1); - Site2SiteCustomerGatewayVO gw = new Site2SiteCustomerGatewayVO("Testing gateway", 1L, 1L, "192.168.50.15", "Guest List", "ipsecPsk", "ikePolicy", "espPolicy", 1L, 1L, true, true); + Site2SiteCustomerGatewayVO gw = new Site2SiteCustomerGatewayVO("Testing gateway", 1L, 1L, "192.168.50.15", "Guest List", "ipsecPsk", "ikePolicy", "espPolicy", 1L, 1L, true, true, false, "ike"); HostVO hostVo = new HostVO(1L, "Testing host", Host.Type.Routing, "192.168.50.15", "privateNetmask", "privateMacAddress", "publicIpAddress", "publicNetmask", "publicMacAddress", "storageIpAddress", "storageNetmask", "storageMacAddress", "deuxStorageIpAddress", "duxStorageNetmask", "deuxStorageMacAddress", "guid", Status.Up, "version", "iqn", new Date() , 1L, 1L, 1L, 1L, "parent", 20L, Storage.StoragePoolType.Gluster); hostVo.setManagementServerId(ManagementServerNode.getManagementServerId()); diff --git a/systemvm/debian/opt/cloud/bin/configure.py b/systemvm/debian/opt/cloud/bin/configure.py index be67f403c8be..d0a83abd8551 100755 --- a/systemvm/debian/opt/cloud/bin/configure.py +++ b/systemvm/debian/opt/cloud/bin/configure.py @@ -556,10 +556,18 @@ def configure_ipsec(self, obj): vpnsecretsfile = "%s/ipsec.vpn-%s.secrets" % (self.VPNCONFDIR, rightpeer) ikepolicy = obj['ike_policy'].replace(';', '-') esppolicy = obj['esp_policy'].replace(';', '-') + splitconnections = obj['split_connections'] if 'split_connections' in obj else False + ikeversion = obj['ike_version'] if 'ike_version' in obj and obj['ike_version'].lower() in ('ike', 'ikev1', 'ikev2') else 'ike' + + peerlistarr = peerlist.split(',') + if splitconnections: + logging.debug('Splitting rightsubnets %s' % peerlistarr) + peerlist = peerlistarr[0] if rightpeer in self.confips: self.confips.remove(rightpeer) file = CsFile(vpnconffile) + file.repopulate() # This avoids issues when switching off split_connections or removing subnets with split_connections == true file.add("#conn for vpn-%s" % rightpeer, 0) file.search("conn ", "conn vpn-%s" % rightpeer) file.addeq(" left=%s" % leftpeer) @@ -568,7 +576,7 @@ def configure_ipsec(self, obj): file.addeq(" rightsubnet=%s" % peerlist) file.addeq(" type=tunnel") file.addeq(" authby=secret") - file.addeq(" keyexchange=ike") + file.addeq(" keyexchange=%s" % ikeversion) file.addeq(" ike=%s" % ikepolicy) file.addeq(" ikelifetime=%s" % self.convert_sec_to_h(obj['ike_lifetime'])) file.addeq(" esp=%s" % esppolicy) @@ -582,6 +590,14 @@ def configure_ipsec(self, obj): file.addeq(" dpddelay=30") file.addeq(" dpdtimeout=120") file.addeq(" dpdaction=restart") + if splitconnections and peerlistarr.count > 1: + logging.debug('Splitting connections for rightsubnets %s' % peerlistarr) + for peeridx in range(1, len(peerlistarr)): + logging.debug('Adding split connection -%d for subnet %s' % (peeridx + 1, peerlistarr[peeridx])) + file.append('') + file.search('conn vpn-.*-%d' % (peeridx + 1), "conn vpn-%s-%d" % (rightpeer, peeridx + 1)) + file.append(' also=vpn-%s' % rightpeer) + file.append(' rightsubnet=%s' % peerlistarr[peeridx]) secret = CsFile(vpnsecretsfile) secret.search("%s " % leftpeer, "%s %s : PSK \"%s\"" % (leftpeer, rightpeer, obj['ipsec_psk'])) if secret.is_changed() or file.is_changed(): @@ -595,14 +611,25 @@ def configure_ipsec(self, obj): os.chmod(vpnsecretsfile, 0400) for i in xrange(3): - result = CsHelper.execute('ipsec status vpn-%s | grep "%s"' % (rightpeer, peerlist.split(",", 1)[0])) - if len(result) > 0: + done = True + for peeridx in range(0, len(peerlistarr)): + # Check for the proper connection and subnet + conn = rightpeer if not splitconnections else rightpeer if peeridx == 0 else '%s-%d' % (rightpeer, peeridx + 1) + result = CsHelper.execute('ipsec status vpn-%s | grep "%s"' % (conn, peerlistarr[peeridx])) + # If any of the peers hasn't yet finished, continue the outer loop + if len(result) == 0: + done = False + if done: break time.sleep(1) # With 'auto=route', connections are established on an attempt to # communicate over the S2S VPN. This uses ping to initialize the connection. - CsHelper.execute("timeout 5 ping -c 3 %s" % (peerlist.split("/", 1)[0].replace(".0", ".1"))) + for peer in peerlistarr: + octets = peer.split('/', 1)[0].split('.') + octets[3] = str((int(octets[3]) + 1)) + ipinsubnet = '.'.join(octets) + CsHelper.execute("timeout 5 ping -c 3 %s" % ipinsubnet) def convert_sec_to_h(self, val): hrs = int(val) / 3600 diff --git a/systemvm/debian/opt/cloud/bin/ipsectunnel.sh b/systemvm/debian/opt/cloud/bin/ipsectunnel.sh index c42650f8d1de..ad12efddbdb6 100755 --- a/systemvm/debian/opt/cloud/bin/ipsectunnel.sh +++ b/systemvm/debian/opt/cloud/bin/ipsectunnel.sh @@ -23,7 +23,7 @@ vpnoutmark="0x525" vpninmark="0x524" usage() { - printf "Usage: %s: (-A|-D) -l -n -g -r -N -e -i -t -T -s -d [ -p -c -S ]\n" $(basename $0) >&2 + printf "Usage: %s: (-A|-D) -l -n -g -r -N -e -i -t -T -s -d [ -p -c -S -C -v ]\n" $(basename $0) >&2 } #set -x @@ -139,14 +139,21 @@ ipsec_tunnel_add() { check_and_enable_iptables + rsubnets=" rightsubnets={$rightnets}" + if [ $splitconnections -eq 1 ] + then + rsubnetarr=(${rightnets}) + rsubnets=" rightsubnet=${rsubnetarr[0]}" + fi + sudo echo "conn vpn-$rightpeer" > $vpnconffile && sudo echo " left=$leftpeer" >> $vpnconffile && sudo echo " leftsubnet=$leftnet" >> $vpnconffile && sudo echo " right=$rightpeer" >> $vpnconffile && - sudo echo " rightsubnets={$rightnets}" >> $vpnconffile && + sudo echo $rsubnets >> $vpnconffile && sudo echo " type=tunnel" >> $vpnconffile && sudo echo " authby=secret" >> $vpnconffile && - sudo echo " keyexchange=ike" >> $vpnconffile && + sudo echo " keyexchange=${ikeversion:-ike}" >> $vpnconffile && sudo echo " ike=$ikepolicy" >> $vpnconffile && sudo echo " ikelifetime=${ikelifetime}s" >> $vpnconffile && sudo echo " esp=$esppolicy" >> $vpnconffile && @@ -163,6 +170,20 @@ ipsec_tunnel_add() { sudo echo " dpdaction=restart" >> $vpnconffile fi + if [ $splitconnections -eq 1 ] + then + # Split out all but the first right subnet into their own statements + subnetidx=2 + for rsubnet in ${rsubnetarr[@]:1}; do + sudo echo "" >> $vpnconffile && + sudo echo "conn vpn-$rightpeer-$subnetidx" >> $vpnconffile && + sudo echo " also=vpn-$rightpeer" >> $vpnconffile && + sudo echo " auto=route" >> $vpnconffile && + sudo echo " rightsubnet=$rsubnet" >> $vpnconffile + ((++subnetidx)) + done + fi + enable_iptables_subnets sudo ipsec auto --rereadall @@ -215,8 +236,10 @@ passive=0 op="" checkup=0 secure=1 +ikeversion="ike" +splitconnections=0 -while getopts 'ADSpcl:n:g:r:N:e:i:t:T:s:d:' OPTION +while getopts 'ACDSpcl:n:g:r:N:e:i:t:T:s:d:v:' OPTION do case $OPTION in A) opflag=1 @@ -243,6 +266,8 @@ do e) eflag=1 esppolicy="$OPTARG" ;; + v) ikeversion="$OPTARG" + ;; i) iflag=1 ikepolicy="$OPTARG" ;; @@ -264,6 +289,8 @@ do ;; S) secure=0 ;; + C) splitconnections=1 + ;; ?) usage exit 2 ;; diff --git a/ui/public/locales/ca.json b/ui/public/locales/ca.json index 743d82f1e81c..1e312e9965b2 100644 --- a/ui/public/locales/ca.json +++ b/ui/public/locales/ca.json @@ -828,6 +828,7 @@ "label.icmpcode": "ICMP Code", "label.icmptype": "ICMP Type", "label.id": "ID", +"label.ike.version": "IKE Version", "label.ikedh": "IKE DH", "label.ikeencryption": "IKE Encryption", "label.ikehash": "IKE Hash", @@ -897,6 +898,7 @@ "label.ipaddress2": "IP Address", "label.iplimit": "Public IP Limits", "label.ips": "IPs", +"label.ipsec.splitconnections": "Split Connections", "label.ipsecpsk": "IPsec Preshared-Key", "label.iptotal": "Total of IP Addresses", "label.ipv4.cidr": "IPv4 CIDR", diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 3241c82309d8..4689e39a5efd 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1025,11 +1025,13 @@ "label.icmptype.start.port": "ICMP Type / Start Port", "label.id": "ID", "label.identity.and.access": "Identity and Access", +"label.ike.version": "IKE Version", "label.ikedh": "IKE DH", "label.ikeencryption": "IKE Encryption", "label.ikehash": "IKE Hash", "label.ikelifetime": "IKE lifetime (second)", "label.ikepolicy": "IKE policy", +"label.ikeversion": "IKE Version", "label.images": "Images", "label.import.backup.offering": "Import Backup Offering", "label.import.offering": "Import Offering", @@ -1105,6 +1107,7 @@ "label.ipaddress2": "IP Address", "label.iplimit": "Public IP Limits", "label.ips": "IPs", +"label.ipsec.splitconnections": "Split Connections", "label.ipsecpsk": "IPsec Preshared-Key", "label.iptotal": "Total of IP Addresses", "label.ipv4.cidr": "IPv4 CIDR", @@ -1973,6 +1976,7 @@ "label.specify.vxlan": "Specify VXLAN", "label.specifyipranges": "Specify IP ranges", "label.specifyvlan": "Specify VLAN", +"label.splitconnections": "Split Connections", "label.sr.name": "SR Name-Label", "label.srx": "SRX", "label.srx.details": "SRX details", diff --git a/ui/public/locales/it_IT.json b/ui/public/locales/it_IT.json index 9a6be067678c..a8308a0bc05e 100644 --- a/ui/public/locales/it_IT.json +++ b/ui/public/locales/it_IT.json @@ -828,6 +828,7 @@ "label.icmpcode": "Codice ICMP", "label.icmptype": "Tipo ICMP", "label.id": "ID", +"label.ike.version": "Versione di IKE", "label.ikedh": "DH di IKE", "label.ikeencryption": "Encryption di IKE", "label.ikehash": "Hash di IKE", @@ -897,6 +898,7 @@ "label.ipaddress2": "Indirizzo IP", "label.iplimit": "Public IP Limits", "label.ips": "Indirizzi IP", +"label.ipsec.splitconnections": "Connessioni Divise", "label.ipsecpsk": "Preshared-Key di IPsec", "label.iptotal": "Total of IP Addresses", "label.ipv4.cidr": "IPv4 CIDR", diff --git a/ui/public/locales/nb_NO.json b/ui/public/locales/nb_NO.json index 722529319bd7..81cbbbe3668c 100644 --- a/ui/public/locales/nb_NO.json +++ b/ui/public/locales/nb_NO.json @@ -828,6 +828,7 @@ "label.icmpcode": "ICMP-kode", "label.icmptype": "ICMP-type", "label.id": "ID", +"label.ike.version": "IKE versjon", "label.ikedh": "IKE DH", "label.ikeencryption": "IKE kryptering", "label.ikehash": "IKE Hash", @@ -897,6 +898,7 @@ "label.ipaddress2": "IP-adresse", "label.iplimit": "Offentlig IP-addresse Grenser", "label.ips": "IPer", +"label.ipsec.splitconnections": "delte forbindelser", "label.ipsecpsk": "IPSec Delt N\u00f8kkel", "label.iptotal": "Totalt IP-adresser", "label.ipv4.cidr": "IPv4 CIDR", diff --git a/ui/public/locales/nl_NL.json b/ui/public/locales/nl_NL.json index 4a5e606f0737..07aae3063b00 100644 --- a/ui/public/locales/nl_NL.json +++ b/ui/public/locales/nl_NL.json @@ -828,6 +828,7 @@ "label.icmpcode": "ICMP Code", "label.icmptype": "ICMP Type", "label.id": "ID", +"label.ike.version": "IKE Versie", "label.ikedh": "IKE DH", "label.ikeencryption": "IKE Encryptie", "label.ikehash": "IKE Hash", @@ -897,6 +898,7 @@ "label.ipaddress2": "IP Adres", "label.iplimit": "Publieke IP Limieten", "label.ips": "IPs", +"label.ipsec.splitconnections": "Gesplitste Verbindingen", "label.ipsecpsk": "IPsec Preshared-Key", "label.iptotal": "totaal aantal IP adressen", "label.ipv4.cidr": "IPv4 CIDR", diff --git a/ui/public/locales/pl.json b/ui/public/locales/pl.json index 29c1c0617d0a..163f1a2a7274 100644 --- a/ui/public/locales/pl.json +++ b/ui/public/locales/pl.json @@ -828,6 +828,7 @@ "label.icmpcode": "ICMP Code", "label.icmptype": "ICMP Type", "label.id": "ID", +"label.ike.version": "IKE Version", "label.ikedh": "IKE DH", "label.ikeencryption": "IKE Encryption", "label.ikehash": "IKE Hash", @@ -897,6 +898,7 @@ "label.ipaddress2": "IP Address", "label.iplimit": "Public IP Limits", "label.ips": "IP", +"label.ipsec.splitconnections": "Split Connections", "label.ipsecpsk": "IPsec Preshared-Key", "label.iptotal": "Total of IP Addresses", "label.ipv4.cidr": "IPv4 CIDR", diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index 29b57e2f3f5d..030142361f26 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -402,7 +402,7 @@ export default { hidden: true, permission: ['listVpnConnections'], columns: ['publicip', 'state', 'gateway', 'ipsecpsk', 'ikepolicy', 'esppolicy'], - details: ['publicip', 'gateway', 'passive', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'esppolicy', 'ikelifetime', 'esplifetime', 'dpd', 'forceencap', 'created'], + details: ['publicip', 'gateway', 'passive', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'esppolicy', 'ikelifetime', 'ikeversion', 'esplifetime', 'dpd', 'splitconnections', 'forceencap', 'created'], actions: [ { api: 'createVpnConnection', @@ -588,7 +588,7 @@ export default { icon: 'lock', permission: ['listVpnCustomerGateways'], columns: ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'account'], - details: ['name', 'id', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'esppolicy', 'esplifetime', 'dpd', 'forceencap', 'account', 'domain'], + details: ['name', 'id', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'ikeversion', 'esppolicy', 'esplifetime', 'dpd', 'splitconnections', 'forceencap', 'account', 'domain'], searchFilters: ['keyword', 'domainid', 'account'], actions: [ { @@ -606,7 +606,7 @@ export default { label: 'label.edit', docHelp: 'adminguide/networking_and_traffic.html#updating-and-removing-a-vpn-customer-gateway', dataView: true, - args: ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'esppolicy', 'esplifetime', 'dpd', 'forceencap'] + args: ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'ikeversion', 'esppolicy', 'esplifetime', 'dpd', 'splitconnections', 'forceencap'] }, { api: 'deleteVpnCustomerGateway', diff --git a/ui/src/views/network/CreateVpnCustomerGateway.vue b/ui/src/views/network/CreateVpnCustomerGateway.vue index ebbb7be12167..7654a0bd78a4 100644 --- a/ui/src/views/network/CreateVpnCustomerGateway.vue +++ b/ui/src/views/network/CreateVpnCustomerGateway.vue @@ -107,6 +107,19 @@ + + + + {{ vers }} + + + + + + {{ $t('label.splitconnections') }} + + + + + + {{ $t('label.forceencap') }} @@ -269,6 +297,11 @@ export default { 'sha512', 'md5' ], + ikeVersions: [ + 'ike', + 'ikev1', + 'ikev2' + ], DHGroups: { '': 'None', 'Group 2': 'modp1024', @@ -316,7 +349,9 @@ export default { dpd: values.dpd, forceencap: values.forceencap, ikepolicy: ikepolicy, - esppolicy: esppolicy + esppolicy: esppolicy, + splitconnections: values.splitconnections, + ikeversion: values.ikeversion }).then(response => { this.$store.dispatch('AddAsyncJob', { title: this.$t('message.add.vpn.customer.gateway'),