mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Improvement: SSL offloading with Virtual Router (#11468)
* SSL offloading with Virtual Router * PR11468: fix pre-commit errors * PR11468: api->getAPI/postAPI in UI * SSL: add smoke tests for VPC in user project * PR11468: address Daan's comments * Fix test/integration/smoke/test_ssl_offloading.py * SSL: remove ssl certificates when clean up account * SSL offloading: add unit tests * SSL offloading: UI fixes part 1 * SSL offloading: UI changes part 2 * SSL offloading: add more unit tests * SSL offloading: more unit tests 3 * SSL offloading: wrong check * SSL offloading: more and more unit tests * SSL offloading: add testUpdateLoadBalancerRule5
This commit is contained in:
parent
8089d32740
commit
b46e29dc67
@ -48,6 +48,7 @@ repos:
|
|||||||
exclude: >
|
exclude: >
|
||||||
(?x)
|
(?x)
|
||||||
^scripts/vm/systemvm/id_rsa\.cloud$|
|
^scripts/vm/systemvm/id_rsa\.cloud$|
|
||||||
|
^server/src/test/java/org/apache/cloudstack/network/ssl/CertServiceTest.java$|
|
||||||
^server/src/test/java/com/cloud/keystore/KeystoreTest\.java$|
|
^server/src/test/java/com/cloud/keystore/KeystoreTest\.java$|
|
||||||
^server/src/test/resources/certs/dsa_self_signed\.key$|
|
^server/src/test/resources/certs/dsa_self_signed\.key$|
|
||||||
^server/src/test/resources/certs/non_root\.key$|
|
^server/src/test/resources/certs/non_root\.key$|
|
||||||
@ -57,7 +58,8 @@ repos:
|
|||||||
^server/src/test/resources/certs/rsa_self_signed\.key$|
|
^server/src/test/resources/certs/rsa_self_signed\.key$|
|
||||||
^services/console-proxy/rdpconsole/src/test/doc/rdp-key\.pem$|
|
^services/console-proxy/rdpconsole/src/test/doc/rdp-key\.pem$|
|
||||||
^systemvm/agent/certs/localhost\.key$|
|
^systemvm/agent/certs/localhost\.key$|
|
||||||
^systemvm/agent/certs/realhostip\.key$
|
^systemvm/agent/certs/realhostip\.key$|
|
||||||
|
^test/integration/smoke/test_ssl_offloading.py$
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
exclude: \.vhd$
|
exclude: \.vhd$
|
||||||
- id: fix-byte-order-marker
|
- id: fix-byte-order-marker
|
||||||
@ -75,7 +77,7 @@ repos:
|
|||||||
name: run codespell
|
name: run codespell
|
||||||
description: Check spelling with codespell
|
description: Check spelling with codespell
|
||||||
args: [--ignore-words=.github/linters/codespell.txt]
|
args: [--ignore-words=.github/linters/codespell.txt]
|
||||||
exclude: ^systemvm/agent/noVNC/|^ui/package\.json$|^ui/package-lock\.json$|^ui/public/js/less\.min\.js$|^ui/public/locales/.*[^n].*\.json$
|
exclude: ^systemvm/agent/noVNC/|^ui/package\.json$|^ui/package-lock\.json$|^ui/public/js/less\.min\.js$|^ui/public/locales/.*[^n].*\.json$|^server/src/test/java/org/apache/cloudstack/network/ssl/CertServiceTest.java$|^test/integration/smoke/test_ssl_offloading.py$
|
||||||
- repo: https://github.com/pycqa/flake8
|
- repo: https://github.com/pycqa/flake8
|
||||||
rev: 7.0.0
|
rev: 7.0.0
|
||||||
hooks:
|
hooks:
|
||||||
|
|||||||
@ -71,7 +71,7 @@ public class LoadBalancerTO {
|
|||||||
this.destinations = new DestinationTO[destinations.size()];
|
this.destinations = new DestinationTO[destinations.size()];
|
||||||
this.stickinessPolicies = null;
|
this.stickinessPolicies = null;
|
||||||
this.sslCert = null;
|
this.sslCert = null;
|
||||||
this.lbProtocol = null;
|
this.lbProtocol = protocol;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (LbDestination destination : destinations) {
|
for (LbDestination destination : destinations) {
|
||||||
this.destinations[i++] = new DestinationTO(destination.getIpAddress(), destination.getDestinationPortStart(), destination.isRevoked(), false);
|
this.destinations[i++] = new DestinationTO(destination.getIpAddress(), destination.getDestinationPortStart(), destination.isRevoked(), false);
|
||||||
@ -205,6 +205,10 @@ public class LoadBalancerTO {
|
|||||||
return this.sslCert;
|
return this.sslCert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLbSslCert(LbSslCert sslCert) {
|
||||||
|
this.sslCert = sslCert;
|
||||||
|
}
|
||||||
|
|
||||||
public String getSrcIpVlan() {
|
public String getSrcIpVlan() {
|
||||||
return srcIpVlan;
|
return srcIpVlan;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,7 +106,7 @@ public interface LoadBalancingRulesService {
|
|||||||
|
|
||||||
boolean applyLoadBalancerConfig(long lbRuleId) throws ResourceUnavailableException;
|
boolean applyLoadBalancerConfig(long lbRuleId) throws ResourceUnavailableException;
|
||||||
|
|
||||||
boolean assignCertToLoadBalancer(long lbRuleId, Long certId);
|
boolean assignCertToLoadBalancer(long lbRuleId, Long certId, boolean isForced);
|
||||||
|
|
||||||
boolean removeCertFromLoadBalancer(long lbRuleId);
|
boolean removeCertFromLoadBalancer(long lbRuleId);
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import org.apache.cloudstack.api.ServerApiException;
|
|||||||
import org.apache.cloudstack.api.response.FirewallRuleResponse;
|
import org.apache.cloudstack.api.response.FirewallRuleResponse;
|
||||||
import org.apache.cloudstack.api.response.SslCertResponse;
|
import org.apache.cloudstack.api.response.SslCertResponse;
|
||||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
|
|
||||||
import com.cloud.event.EventTypes;
|
import com.cloud.event.EventTypes;
|
||||||
import com.cloud.exception.ConcurrentOperationException;
|
import com.cloud.exception.ConcurrentOperationException;
|
||||||
@ -57,11 +58,17 @@ public class AssignCertToLoadBalancerCmd extends BaseAsyncCmd {
|
|||||||
description = "the ID of the certificate")
|
description = "the ID of the certificate")
|
||||||
Long certId;
|
Long certId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.FORCED,
|
||||||
|
type = CommandType.BOOLEAN,
|
||||||
|
since = "4.22",
|
||||||
|
description = "Force assign the certificate. If there is a certificate assigned to the LB, it will be removed at first.")
|
||||||
|
private Boolean forced;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
|
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
|
||||||
ResourceAllocationException, NetworkRuleConflictException {
|
ResourceAllocationException, NetworkRuleConflictException {
|
||||||
//To change body of implemented methods use File | Settings | File Templates.
|
//To change body of implemented methods use File | Settings | File Templates.
|
||||||
if (_lbService.assignCertToLoadBalancer(getLbRuleId(), getCertId())) {
|
if (_lbService.assignCertToLoadBalancer(getLbRuleId(), getCertId(), isForced())) {
|
||||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||||
this.setResponseObject(response);
|
this.setResponseObject(response);
|
||||||
} else {
|
} else {
|
||||||
@ -95,4 +102,19 @@ public class AssignCertToLoadBalancerCmd extends BaseAsyncCmd {
|
|||||||
public Long getLbRuleId() {
|
public Long getLbRuleId() {
|
||||||
return lbRuleId;
|
return lbRuleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isForced() {
|
||||||
|
return BooleanUtils.toBoolean(forced);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSyncObjType() {
|
||||||
|
return BaseAsyncCmd.networkSyncObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getSyncObjId() {
|
||||||
|
LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, getLbRuleId());
|
||||||
|
return (lb != null)? lb.getNetworkId(): null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,7 @@ import org.apache.cloudstack.api.response.LoadBalancerResponse;
|
|||||||
import org.apache.cloudstack.api.response.NetworkResponse;
|
import org.apache.cloudstack.api.response.NetworkResponse;
|
||||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import com.cloud.dc.DataCenter;
|
import com.cloud.dc.DataCenter;
|
||||||
import com.cloud.dc.DataCenter.NetworkType;
|
import com.cloud.dc.DataCenter.NetworkType;
|
||||||
@ -112,7 +113,7 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements L
|
|||||||
+ "rule will be created for. Required when public Ip address is not associated with any Guest network yet (VPC case)")
|
+ "rule will be created for. Required when public Ip address is not associated with any Guest network yet (VPC case)")
|
||||||
private Long networkId;
|
private Long networkId;
|
||||||
|
|
||||||
@Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "The protocol for the LB such as tcp, udp or tcp-proxy.")
|
@Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "The protocol for the LB such as tcp, udp, tcp-proxy or ssl.")
|
||||||
private String lbProtocol;
|
private String lbProtocol;
|
||||||
|
|
||||||
@Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the rule to the end user or not", since = "4.4", authorized = {RoleType.Admin})
|
@Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the rule to the end user or not", since = "4.4", authorized = {RoleType.Admin})
|
||||||
@ -253,7 +254,7 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements L
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getLbProtocol() {
|
public String getLbProtocol() {
|
||||||
return lbProtocol;
|
return StringUtils.trim(StringUtils.lowerCase(lbProtocol));
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
|
|||||||
@ -82,4 +82,15 @@ public class RemoveCertFromLoadBalancerCmd extends BaseAsyncCmd {
|
|||||||
public Long getLbRuleId() {
|
public Long getLbRuleId() {
|
||||||
return this.lbRuleId;
|
return this.lbRuleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSyncObjType() {
|
||||||
|
return BaseAsyncCmd.networkSyncObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getSyncObjId() {
|
||||||
|
LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, getLbRuleId());
|
||||||
|
return (lb != null)? lb.getNetworkId(): null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,6 +56,8 @@ public class LoadBalancerConfigItem extends AbstractConfigItemFacade {
|
|||||||
final String[] statRules = allRules[LoadBalancerConfigurator.STATS];
|
final String[] statRules = allRules[LoadBalancerConfigurator.STATS];
|
||||||
|
|
||||||
final LoadBalancerRule loadBalancerRule = new LoadBalancerRule(configuration, tmpCfgFilePath, tmpCfgFileName, addRules, removeRules, statRules, routerIp);
|
final LoadBalancerRule loadBalancerRule = new LoadBalancerRule(configuration, tmpCfgFilePath, tmpCfgFileName, addRules, removeRules, statRules, routerIp);
|
||||||
|
final LoadBalancerRule.SslCertEntry[] sslCerts = cfgtr.generateSslCertEntries(command);
|
||||||
|
loadBalancerRule.setSslCerts(sslCerts);
|
||||||
|
|
||||||
final List<LoadBalancerRule> rules = new LinkedList<LoadBalancerRule>();
|
final List<LoadBalancerRule> rules = new LinkedList<LoadBalancerRule>();
|
||||||
rules.add(loadBalancerRule);
|
rules.add(loadBalancerRule);
|
||||||
|
|||||||
@ -25,6 +25,7 @@ public class LoadBalancerRule {
|
|||||||
private String[] configuration;
|
private String[] configuration;
|
||||||
private String tmpCfgFilePath;
|
private String tmpCfgFilePath;
|
||||||
private String tmpCfgFileName;
|
private String tmpCfgFileName;
|
||||||
|
private SslCertEntry[] sslCerts;
|
||||||
|
|
||||||
private String[] addRules;
|
private String[] addRules;
|
||||||
private String[] removeRules;
|
private String[] removeRules;
|
||||||
@ -32,6 +33,53 @@ public class LoadBalancerRule {
|
|||||||
|
|
||||||
private String routerIp;
|
private String routerIp;
|
||||||
|
|
||||||
|
public static class SslCertEntry {
|
||||||
|
private String name;
|
||||||
|
private String cert;
|
||||||
|
private String key;
|
||||||
|
private String chain;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
public SslCertEntry(String name, String cert, String key, String chain, String password) {
|
||||||
|
this.name = name;
|
||||||
|
this.cert = cert;
|
||||||
|
this.key = key;
|
||||||
|
this.chain = chain;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
public void setCert(String cert) {
|
||||||
|
this.cert = cert;
|
||||||
|
}
|
||||||
|
public String getCert() {
|
||||||
|
return cert;
|
||||||
|
}
|
||||||
|
public void setKey(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
public void setChain(String chain) {
|
||||||
|
this.chain = chain;
|
||||||
|
}
|
||||||
|
public String getChain() {
|
||||||
|
return chain;
|
||||||
|
}
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public LoadBalancerRule() {
|
public LoadBalancerRule() {
|
||||||
// Empty constructor for (de)serialization
|
// Empty constructor for (de)serialization
|
||||||
}
|
}
|
||||||
@ -101,4 +149,12 @@ public class LoadBalancerRule {
|
|||||||
public void setRouterIp(final String routerIp) {
|
public void setRouterIp(final String routerIp) {
|
||||||
this.routerIp = routerIp;
|
this.routerIp = routerIp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SslCertEntry[] getSslCerts() {
|
||||||
|
return sslCerts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSslCerts(final SslCertEntry[] sslCerts) {
|
||||||
|
this.sslCerts = sslCerts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,8 @@ import com.cloud.agent.api.to.LoadBalancerTO;
|
|||||||
import com.cloud.agent.api.to.LoadBalancerTO.DestinationTO;
|
import com.cloud.agent.api.to.LoadBalancerTO.DestinationTO;
|
||||||
import com.cloud.agent.api.to.LoadBalancerTO.StickinessPolicyTO;
|
import com.cloud.agent.api.to.LoadBalancerTO.StickinessPolicyTO;
|
||||||
import com.cloud.agent.api.to.PortForwardingRuleTO;
|
import com.cloud.agent.api.to.PortForwardingRuleTO;
|
||||||
|
import com.cloud.agent.resource.virtualnetwork.model.LoadBalancerRule.SslCertEntry;
|
||||||
|
import com.cloud.network.lb.LoadBalancingRule.LbSslCert;
|
||||||
import com.cloud.network.rules.LbStickinessMethod.StickinessMethodType;
|
import com.cloud.network.rules.LbStickinessMethod.StickinessMethodType;
|
||||||
import com.cloud.utils.Pair;
|
import com.cloud.utils.Pair;
|
||||||
import com.cloud.utils.net.NetUtils;
|
import com.cloud.utils.net.NetUtils;
|
||||||
@ -52,6 +54,12 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator {
|
|||||||
|
|
||||||
private static String[] defaultListen = {"listen vmops", "\tbind 0.0.0.0:9", "\toption transparent"};
|
private static String[] defaultListen = {"listen vmops", "\tbind 0.0.0.0:9", "\toption transparent"};
|
||||||
|
|
||||||
|
private static final String SSL_CERTS_DIR = "/etc/cloudstack/ssl/";
|
||||||
|
|
||||||
|
private static final String SSL_CONFIGURATION_INTERMEDIATE = " ssl-min-ver TLSv1.2 no-tls-tickets " +
|
||||||
|
"ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-GCM-SHA256 " +
|
||||||
|
"ciphersuites TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] generateConfiguration(final List<PortForwardingRuleTO> fwRules) {
|
public String[] generateConfiguration(final List<PortForwardingRuleTO> fwRules) {
|
||||||
// Group the rules by publicip:publicport
|
// Group the rules by publicip:publicport
|
||||||
@ -469,30 +477,41 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getRulesForPool(final LoadBalancerTO lbTO, final boolean keepAliveEnabled) {
|
private List<String> getRulesForPool(final LoadBalancerTO lbTO, final LoadBalancerConfigCommand lbCmd) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
final String poolName = sb.append(lbTO.getSrcIp().replace(".", "_")).append('-').append(lbTO.getSrcPort()).toString();
|
final String poolName = sb.append(lbTO.getSrcIp().replace(".", "_")).append('-').append(lbTO.getSrcPort()).toString();
|
||||||
final String publicIP = lbTO.getSrcIp();
|
final String publicIP = lbTO.getSrcIp();
|
||||||
final int publicPort = lbTO.getSrcPort();
|
final int publicPort = lbTO.getSrcPort();
|
||||||
final String algorithm = lbTO.getAlgorithm();
|
final String algorithm = lbTO.getAlgorithm();
|
||||||
|
|
||||||
final List<String> result = new ArrayList<String>();
|
boolean sslOffloading = lbTO.getSslCert() != null && !lbTO.getSslCert().isRevoked()
|
||||||
// add line like this: "listen 65_37_141_30-80\n\tbind 65.37.141.30:80"
|
&& NetUtils.SSL_PROTO.equals(lbTO.getLbProtocol());
|
||||||
sb = new StringBuilder();
|
|
||||||
sb.append("listen ").append(poolName);
|
final List<String> frontendConfigs = new ArrayList<>();
|
||||||
result.add(sb.toString());
|
final List<String> backendConfigs = new ArrayList<>();
|
||||||
|
final List<String> result = new ArrayList<>();
|
||||||
|
|
||||||
sb = new StringBuilder();
|
sb = new StringBuilder();
|
||||||
sb.append("\tbind ").append(publicIP).append(":").append(publicPort);
|
sb.append("\tbind ").append(publicIP).append(":").append(publicPort);
|
||||||
result.add(sb.toString());
|
|
||||||
|
if (sslOffloading) {
|
||||||
|
sb.append(" ssl crt ").append(SSL_CERTS_DIR).append(poolName).append(".pem");
|
||||||
|
// check for http2 support
|
||||||
|
sb.append(" alpn h2,http/1.1");
|
||||||
|
sb.append(SSL_CONFIGURATION_INTERMEDIATE);
|
||||||
|
sb.append("\n\thttp-request add-header X-Forwarded-Proto https");
|
||||||
|
}
|
||||||
|
frontendConfigs.add(sb.toString());
|
||||||
|
|
||||||
sb = new StringBuilder();
|
sb = new StringBuilder();
|
||||||
sb.append("\t").append("balance ").append(algorithm.toLowerCase());
|
sb.append("\t").append("balance ").append(algorithm.toLowerCase());
|
||||||
result.add(sb.toString());
|
backendConfigs.add(sb.toString());
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
Boolean destsAvailable = false;
|
boolean destsAvailable = false;
|
||||||
final String stickinessSubRule = getLbSubRuleForStickiness(lbTO);
|
final String stickinessSubRule = getLbSubRuleForStickiness(lbTO);
|
||||||
final List<String> dstSubRule = new ArrayList<String>();
|
final List<String> dstSubRule = new ArrayList<>();
|
||||||
final List<String> dstWithCookieSubRule = new ArrayList<String>();
|
final List<String> dstWithCookieSubRule = new ArrayList<>();
|
||||||
for (final DestinationTO dest : lbTO.getDestinations()) {
|
for (final DestinationTO dest : lbTO.getDestinations()) {
|
||||||
// add line like this: "server 65_37_141_30-80_3 10.1.1.4:80 check"
|
// add line like this: "server 65_37_141_30-80_3 10.1.1.4:80 check"
|
||||||
if (dest.isRevoked()) {
|
if (dest.isRevoked()) {
|
||||||
@ -500,15 +519,20 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator {
|
|||||||
}
|
}
|
||||||
sb = new StringBuilder();
|
sb = new StringBuilder();
|
||||||
sb.append("\t")
|
sb.append("\t")
|
||||||
.append("server ")
|
.append("server ")
|
||||||
.append(poolName)
|
.append(poolName)
|
||||||
.append("_")
|
.append("_")
|
||||||
.append(Integer.toString(i++))
|
.append(i++)
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append(dest.getDestIp())
|
.append(dest.getDestIp())
|
||||||
.append(":")
|
.append(":")
|
||||||
.append(dest.getDestPort())
|
.append(dest.getDestPort())
|
||||||
.append(" check");
|
.append(" check");
|
||||||
|
|
||||||
|
if (sslOffloading) {
|
||||||
|
sb.append(SSL_CONFIGURATION_INTERMEDIATE);
|
||||||
|
}
|
||||||
|
|
||||||
if(lbTO.getLbProtocol() != null && lbTO.getLbProtocol().equals("tcp-proxy")) {
|
if(lbTO.getLbProtocol() != null && lbTO.getLbProtocol().equals("tcp-proxy")) {
|
||||||
sb.append(" send-proxy");
|
sb.append(" send-proxy");
|
||||||
}
|
}
|
||||||
@ -520,9 +544,9 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator {
|
|||||||
destsAvailable = true;
|
destsAvailable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Boolean httpbasedStickiness = false;
|
boolean httpbasedStickiness = false;
|
||||||
/* attach stickiness sub rule only if the destinations are available */
|
/* attach stickiness sub rule only if the destinations are available */
|
||||||
if (stickinessSubRule != null && destsAvailable == true) {
|
if (stickinessSubRule != null && destsAvailable) {
|
||||||
for (final StickinessPolicyTO stickinessPolicy : lbTO.getStickinessPolicies()) {
|
for (final StickinessPolicyTO stickinessPolicy : lbTO.getStickinessPolicies()) {
|
||||||
if (stickinessPolicy == null) {
|
if (stickinessPolicy == null) {
|
||||||
continue;
|
continue;
|
||||||
@ -530,35 +554,40 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator {
|
|||||||
if (StickinessMethodType.LBCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName()) ||
|
if (StickinessMethodType.LBCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName()) ||
|
||||||
StickinessMethodType.AppCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) {
|
StickinessMethodType.AppCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) {
|
||||||
httpbasedStickiness = true;
|
httpbasedStickiness = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (httpbasedStickiness) {
|
if (httpbasedStickiness) {
|
||||||
result.addAll(dstWithCookieSubRule);
|
backendConfigs.addAll(dstWithCookieSubRule);
|
||||||
} else {
|
} else {
|
||||||
result.addAll(dstSubRule);
|
backendConfigs.addAll(dstSubRule);
|
||||||
}
|
}
|
||||||
result.add(stickinessSubRule);
|
backendConfigs.add(stickinessSubRule);
|
||||||
} else {
|
} else {
|
||||||
result.addAll(dstSubRule);
|
backendConfigs.addAll(dstSubRule);
|
||||||
}
|
}
|
||||||
if (stickinessSubRule != null && !destsAvailable) {
|
if (stickinessSubRule != null && !destsAvailable) {
|
||||||
logger.warn("Haproxy stickiness policy for lb rule: " + lbTO.getSrcIp() + ":" + lbTO.getSrcPort() + ": Not Applied, cause: backends are unavailable");
|
logger.warn("Haproxy stickiness policy for lb rule: " + lbTO.getSrcIp() + ":" + lbTO.getSrcPort() + ": Not Applied, cause: backends are unavailable");
|
||||||
}
|
}
|
||||||
if (publicPort == NetUtils.HTTP_PORT && !keepAliveEnabled || httpbasedStickiness) {
|
boolean keepAliveEnabled = lbCmd.keepAliveEnabled;
|
||||||
sb = new StringBuilder();
|
boolean http = (publicPort == NetUtils.HTTP_PORT && !keepAliveEnabled);
|
||||||
sb.append("\t").append("mode http");
|
if (http || httpbasedStickiness || sslOffloading) {
|
||||||
result.add(sb.toString());
|
frontendConfigs.add("\tmode http");
|
||||||
sb = new StringBuilder();
|
String keepAliveLine = keepAliveEnabled ? "\tno option forceclose" : "\toption httpclose";
|
||||||
sb.append("\t").append("option httpclose");
|
frontendConfigs.add(keepAliveLine);
|
||||||
result.add(sb.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add line like this: "listen 65_37_141_30-80\n\tbind 65.37.141.30:80"
|
||||||
|
result.add(String.format("listen %s", poolName));
|
||||||
|
result.addAll(frontendConfigs);
|
||||||
|
|
||||||
String cidrList = lbTO.getCidrList();
|
String cidrList = lbTO.getCidrList();
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(cidrList)) {
|
if (StringUtils.isNotBlank(cidrList)) {
|
||||||
result.add(String.format("\tacl network_allowed src %s \n\ttcp-request connection reject if !network_allowed", cidrList));
|
result.add(String.format("\tacl network_allowed src %s \n\ttcp-request connection reject if !network_allowed", cidrList));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.addAll(backendConfigs);
|
||||||
result.add(blankLine);
|
result.add(blankLine);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -566,15 +595,18 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator {
|
|||||||
private String generateStatsRule(final LoadBalancerConfigCommand lbCmd, final String ruleName, final String statsIp) {
|
private String generateStatsRule(final LoadBalancerConfigCommand lbCmd, final String ruleName, final String statsIp) {
|
||||||
final StringBuilder rule = new StringBuilder("\nlisten ").append(ruleName).append("\n\tbind ").append(statsIp).append(":").append(lbCmd.lbStatsPort);
|
final StringBuilder rule = new StringBuilder("\nlisten ").append(ruleName).append("\n\tbind ").append(statsIp).append(":").append(lbCmd.lbStatsPort);
|
||||||
// TODO DH: write test for this in both cases
|
// TODO DH: write test for this in both cases
|
||||||
if (!lbCmd.keepAliveEnabled) {
|
rule.append("\n\tmode http");
|
||||||
logger.info("Haproxy mode http enabled");
|
if (lbCmd.keepAliveEnabled) {
|
||||||
rule.append("\n\tmode http\n\toption httpclose");
|
logger.info("Haproxy option http-keep-alive enabled");
|
||||||
|
} else {
|
||||||
|
logger.info("Haproxy option httpclose enabled");
|
||||||
|
rule.append("\n\toption httpclose");
|
||||||
}
|
}
|
||||||
rule.append("\n\tstats enable\n\tstats uri ")
|
rule.append("\n\tstats enable\n\tstats uri ")
|
||||||
.append(lbCmd.lbStatsUri)
|
.append(lbCmd.lbStatsUri)
|
||||||
.append("\n\tstats realm Haproxy\\ Statistics\n\tstats auth ")
|
.append("\n\tstats realm Haproxy\\ Statistics\n\tstats auth ")
|
||||||
.append(lbCmd.lbStatsAuth);
|
.append(lbCmd.lbStatsAuth)
|
||||||
rule.append("\n");
|
.append("\n");
|
||||||
final String result = rule.toString();
|
final String result = rule.toString();
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Haproxystats rule: " + result);
|
logger.debug("Haproxystats rule: " + result);
|
||||||
@ -644,7 +676,7 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator {
|
|||||||
if (lbTO.isRevoked()) {
|
if (lbTO.isRevoked()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final List<String> poolRules = getRulesForPool(lbTO, lbCmd.keepAliveEnabled);
|
final List<String> poolRules = getRulesForPool(lbTO, lbCmd);
|
||||||
result.addAll(poolRules);
|
result.addAll(poolRules);
|
||||||
has_listener = true;
|
has_listener = true;
|
||||||
}
|
}
|
||||||
@ -696,4 +728,30 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SslCertEntry[] generateSslCertEntries(LoadBalancerConfigCommand lbCmd) {
|
||||||
|
final Set<SslCertEntry> sslCertEntries = new HashSet<>();
|
||||||
|
for (final LoadBalancerTO lbTO : lbCmd.getLoadBalancers()) {
|
||||||
|
if (lbTO.getSslCert() != null) {
|
||||||
|
addSslCertEntry(sslCertEntries, lbTO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final SslCertEntry[] result = sslCertEntries.toArray(new SslCertEntry[sslCertEntries.size()]);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSslCertEntry(Set<SslCertEntry> sslCertEntries, LoadBalancerTO lbTO) {
|
||||||
|
final LbSslCert cert = lbTO.getSslCert();
|
||||||
|
if (cert.isRevoked()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (lbTO.getLbProtocol() == null || ! lbTO.getLbProtocol().equals(NetUtils.SSL_PROTO)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
final String name = sb.append(lbTO.getSrcIp().replace(".", "_")).append('-').append(lbTO.getSrcPort()).toString();
|
||||||
|
final SslCertEntry sslCertEntry = new SslCertEntry(name, cert.getCert(), cert.getKey(), cert.getChain(), cert.getPassword());
|
||||||
|
sslCertEntries.add(sslCertEntry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import com.cloud.agent.api.routing.LoadBalancerConfigCommand;
|
import com.cloud.agent.api.routing.LoadBalancerConfigCommand;
|
||||||
import com.cloud.agent.api.to.PortForwardingRuleTO;
|
import com.cloud.agent.api.to.PortForwardingRuleTO;
|
||||||
|
import com.cloud.agent.resource.virtualnetwork.model.LoadBalancerRule.SslCertEntry;
|
||||||
|
|
||||||
public interface LoadBalancerConfigurator {
|
public interface LoadBalancerConfigurator {
|
||||||
public final static int ADD = 0;
|
public final static int ADD = 0;
|
||||||
@ -34,4 +35,6 @@ public interface LoadBalancerConfigurator {
|
|||||||
public String[] generateConfiguration(LoadBalancerConfigCommand lbCmd);
|
public String[] generateConfiguration(LoadBalancerConfigCommand lbCmd);
|
||||||
|
|
||||||
public String[][] generateFwRules(LoadBalancerConfigCommand lbCmd);
|
public String[][] generateFwRules(LoadBalancerConfigCommand lbCmd);
|
||||||
|
|
||||||
|
public SslCertEntry[] generateSslCertEntries(LoadBalancerConfigCommand lbCmd);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,6 +57,7 @@ import com.cloud.agent.resource.virtualnetwork.model.IpAssociation;
|
|||||||
import com.cloud.agent.resource.virtualnetwork.model.LoadBalancerRule;
|
import com.cloud.agent.resource.virtualnetwork.model.LoadBalancerRule;
|
||||||
import com.cloud.agent.resource.virtualnetwork.model.LoadBalancerRules;
|
import com.cloud.agent.resource.virtualnetwork.model.LoadBalancerRules;
|
||||||
import com.cloud.network.lb.LoadBalancingRule.LbDestination;
|
import com.cloud.network.lb.LoadBalancingRule.LbDestination;
|
||||||
|
import com.cloud.network.lb.LoadBalancingRule.LbSslCert;
|
||||||
import com.cloud.network.Networks.TrafficType;
|
import com.cloud.network.Networks.TrafficType;
|
||||||
|
|
||||||
public class ConfigHelperTest {
|
public class ConfigHelperTest {
|
||||||
@ -223,9 +224,12 @@ public class ConfigHelperTest {
|
|||||||
protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand() {
|
protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand() {
|
||||||
final List<LoadBalancerTO> lbs = new ArrayList<>();
|
final List<LoadBalancerTO> lbs = new ArrayList<>();
|
||||||
final List<LbDestination> dests = new ArrayList<>();
|
final List<LbDestination> dests = new ArrayList<>();
|
||||||
|
final LbSslCert lbSslCert = new LbSslCert("cert", "key", "password", "chain", "fingerprint", false);
|
||||||
dests.add(new LbDestination(80, 8080, "10.1.10.2", false));
|
dests.add(new LbDestination(80, 8080, "10.1.10.2", false));
|
||||||
dests.add(new LbDestination(80, 8080, "10.1.10.2", true));
|
dests.add(new LbDestination(80, 8080, "10.1.10.2", true));
|
||||||
lbs.add(new LoadBalancerTO(UUID.randomUUID().toString(), "64.10.1.10", 80, "tcp", "algo", false, false, false, dests));
|
LoadBalancerTO loadBalancerTO = new LoadBalancerTO(UUID.randomUUID().toString(), "64.10.1.10", 80, "tcp", "algo", false, false, false, dests);
|
||||||
|
loadBalancerTO.setLbSslCert(lbSslCert);
|
||||||
|
lbs.add(loadBalancerTO);
|
||||||
|
|
||||||
final LoadBalancerTO[] arrayLbs = new LoadBalancerTO[lbs.size()];
|
final LoadBalancerTO[] arrayLbs = new LoadBalancerTO[lbs.size()];
|
||||||
lbs.toArray(arrayLbs);
|
lbs.toArray(arrayLbs);
|
||||||
|
|||||||
@ -0,0 +1,63 @@
|
|||||||
|
// 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.agent.resource.virtualnetwork.model;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class LoadBalancerRuleTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSslCertEntry() {
|
||||||
|
String name = "name";
|
||||||
|
String cert = "cert";
|
||||||
|
String key = "key1";
|
||||||
|
String chain = "chain";
|
||||||
|
String password = "password";
|
||||||
|
LoadBalancerRule.SslCertEntry sslCertEntry = new LoadBalancerRule.SslCertEntry(name, cert, key, chain, password);
|
||||||
|
|
||||||
|
Assert.assertEquals(name, sslCertEntry.getName());
|
||||||
|
Assert.assertEquals(cert, sslCertEntry.getCert());
|
||||||
|
Assert.assertEquals(key, sslCertEntry.getKey());
|
||||||
|
Assert.assertEquals(chain, sslCertEntry.getChain());
|
||||||
|
Assert.assertEquals(password, sslCertEntry.getPassword());
|
||||||
|
|
||||||
|
String name2 = "name2";
|
||||||
|
String cert2 = "cert2";
|
||||||
|
String key2 = "key2";
|
||||||
|
String chain2 = "chain2";
|
||||||
|
String password2 = "password2";
|
||||||
|
|
||||||
|
sslCertEntry.setName(name2);
|
||||||
|
sslCertEntry.setCert(cert2);
|
||||||
|
sslCertEntry.setKey(key2);
|
||||||
|
sslCertEntry.setChain(chain2);
|
||||||
|
sslCertEntry.setPassword(password2);
|
||||||
|
|
||||||
|
Assert.assertEquals(name2, sslCertEntry.getName());
|
||||||
|
Assert.assertEquals(cert2, sslCertEntry.getCert());
|
||||||
|
Assert.assertEquals(key2, sslCertEntry.getKey());
|
||||||
|
Assert.assertEquals(chain2, sslCertEntry.getChain());
|
||||||
|
Assert.assertEquals(password2, sslCertEntry.getPassword());
|
||||||
|
|
||||||
|
LoadBalancerRule loadBalancerRule = new LoadBalancerRule();
|
||||||
|
loadBalancerRule.setSslCerts(new LoadBalancerRule.SslCertEntry[]{sslCertEntry});
|
||||||
|
|
||||||
|
Assert.assertEquals(1, loadBalancerRule.getSslCerts().length);
|
||||||
|
Assert.assertEquals(sslCertEntry, loadBalancerRule.getSslCerts()[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -31,6 +31,7 @@ import org.junit.Test;
|
|||||||
import com.cloud.agent.api.routing.LoadBalancerConfigCommand;
|
import com.cloud.agent.api.routing.LoadBalancerConfigCommand;
|
||||||
import com.cloud.agent.api.to.LoadBalancerTO;
|
import com.cloud.agent.api.to.LoadBalancerTO;
|
||||||
import com.cloud.network.lb.LoadBalancingRule.LbDestination;
|
import com.cloud.network.lb.LoadBalancingRule.LbDestination;
|
||||||
|
import com.cloud.network.lb.LoadBalancingRule.LbSslCert;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -80,11 +81,11 @@ public class HAProxyConfiguratorTest {
|
|||||||
HAProxyConfigurator hpg = new HAProxyConfigurator();
|
HAProxyConfigurator hpg = new HAProxyConfigurator();
|
||||||
LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false);
|
LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false);
|
||||||
String result = genConfig(hpg, cmd);
|
String result = genConfig(hpg, cmd);
|
||||||
assertTrue("keepalive disabled should result in 'mode http' in the resulting haproxy config", result.contains("mode http"));
|
assertTrue("keepalive disabled should result in 'option httpclose' in the resulting haproxy config", result.contains("\toption httpclose"));
|
||||||
|
|
||||||
cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "4", true);
|
cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "4", true);
|
||||||
result = genConfig(hpg, cmd);
|
result = genConfig(hpg, cmd);
|
||||||
assertTrue("keepalive enabled should not result in 'mode http' in the resulting haproxy config", !result.contains("mode http"));
|
assertTrue("keepalive enabled should result in 'no option httpclose' in the resulting haproxy config", result.contains("\tno option httpclose"));
|
||||||
// TODO
|
// TODO
|
||||||
// create lb command
|
// create lb command
|
||||||
// setup tests for
|
// setup tests for
|
||||||
@ -122,6 +123,19 @@ public class HAProxyConfiguratorTest {
|
|||||||
Assert.assertTrue(result.contains("acl network_allowed src 1.1.1.1 2.2.2.2/24 \n\ttcp-request connection reject if !network_allowed"));
|
Assert.assertTrue(result.contains("acl network_allowed src 1.1.1.1 2.2.2.2/24 \n\ttcp-request connection reject if !network_allowed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generateConfigurationTestWithSslCert() {
|
||||||
|
LoadBalancerTO lb = new LoadBalancerTO("1", "10.2.0.1", 443, "ssl", "roundrobin", false, false, false, null);
|
||||||
|
final LbSslCert lbSslCert = new LbSslCert("cert", "key", "password", "chain", "fingerprint", false);
|
||||||
|
lb.setLbSslCert(lbSslCert);
|
||||||
|
LoadBalancerTO[] lba = new LoadBalancerTO[1];
|
||||||
|
lba[0] = lb;
|
||||||
|
HAProxyConfigurator hpg = new HAProxyConfigurator();
|
||||||
|
LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false);
|
||||||
|
String result = genConfig(hpg, cmd);
|
||||||
|
Assert.assertTrue(result.contains("bind 10.2.0.1:443 ssl crt /etc/cloudstack/ssl/10_2_0_1-443.pem"));
|
||||||
|
}
|
||||||
|
|
||||||
private String genConfig(HAProxyConfigurator hpg, LoadBalancerConfigCommand cmd) {
|
private String genConfig(HAProxyConfigurator hpg, LoadBalancerConfigCommand cmd) {
|
||||||
String[] sa = hpg.generateConfiguration(cmd);
|
String[] sa = hpg.generateConfiguration(cmd);
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|||||||
@ -22,4 +22,6 @@ import com.cloud.utils.db.GenericDao;
|
|||||||
|
|
||||||
public interface SslCertDao extends GenericDao<SslCertVO, Long> {
|
public interface SslCertDao extends GenericDao<SslCertVO, Long> {
|
||||||
List<SslCertVO> listByAccountId(Long id);
|
List<SslCertVO> listByAccountId(Long id);
|
||||||
|
|
||||||
|
int removeByAccountId(long accountId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,4 +40,10 @@ public class SslCertDaoImpl extends GenericDaoBase<SslCertVO, Long> implements S
|
|||||||
return listBy(sc);
|
return listBy(sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int removeByAccountId(long accountId) {
|
||||||
|
SearchCriteria<SslCertVO> sc = listByAccountId.create();
|
||||||
|
sc.setParameters("accountId", accountId);
|
||||||
|
return remove(sc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -517,9 +517,11 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ
|
|||||||
final Map<Capability, String> lbCapabilities = new HashMap<Capability, String>();
|
final Map<Capability, String> lbCapabilities = new HashMap<Capability, String>();
|
||||||
lbCapabilities.put(Capability.SupportedLBAlgorithms, "roundrobin,leastconn,source");
|
lbCapabilities.put(Capability.SupportedLBAlgorithms, "roundrobin,leastconn,source");
|
||||||
lbCapabilities.put(Capability.SupportedLBIsolation, "dedicated");
|
lbCapabilities.put(Capability.SupportedLBIsolation, "dedicated");
|
||||||
lbCapabilities.put(Capability.SupportedProtocols, "tcp, udp, tcp-proxy");
|
lbCapabilities.put(Capability.SupportedProtocols, "tcp, udp, tcp-proxy, ssl");
|
||||||
lbCapabilities.put(Capability.SupportedStickinessMethods, getHAProxyStickinessCapability());
|
lbCapabilities.put(Capability.SupportedStickinessMethods, getHAProxyStickinessCapability());
|
||||||
lbCapabilities.put(Capability.LbSchemes, LoadBalancerContainer.Scheme.Public.toString());
|
lbCapabilities.put(Capability.LbSchemes, LoadBalancerContainer.Scheme.Public.toString());
|
||||||
|
// Supports SSL offloading
|
||||||
|
lbCapabilities.put(Capability.SslTermination, "true");
|
||||||
|
|
||||||
// specifies that LB rules can support autoscaling and the list of
|
// specifies that LB rules can support autoscaling and the list of
|
||||||
// counters it supports
|
// counters it supports
|
||||||
|
|||||||
@ -1267,10 +1267,10 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
|
|||||||
@Override
|
@Override
|
||||||
@DB
|
@DB
|
||||||
@ActionEvent(eventType = EventTypes.EVENT_LB_CERT_ASSIGN, eventDescription = "assigning certificate to load balancer", async = true)
|
@ActionEvent(eventType = EventTypes.EVENT_LB_CERT_ASSIGN, eventDescription = "assigning certificate to load balancer", async = true)
|
||||||
public boolean assignCertToLoadBalancer(long lbRuleId, Long certId) {
|
public boolean assignCertToLoadBalancer(long lbRuleId, Long certId, boolean forced) {
|
||||||
CallContext caller = CallContext.current();
|
CallContext caller = CallContext.current();
|
||||||
|
|
||||||
LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(lbRuleId));
|
LoadBalancerVO loadBalancer = _lbDao.findById(lbRuleId);
|
||||||
if (loadBalancer == null) {
|
if (loadBalancer == null) {
|
||||||
throw new InvalidParameterValueException("Invalid load balancer id: " + lbRuleId);
|
throw new InvalidParameterValueException("Invalid load balancer id: " + lbRuleId);
|
||||||
}
|
}
|
||||||
@ -1292,10 +1292,7 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
|
|||||||
throw new InvalidParameterValueException("Ssl termination not supported by the loadbalancer");
|
throw new InvalidParameterValueException("Ssl termination not supported by the loadbalancer");
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if the lb is already bound
|
validateCertMapRule(lbRuleId, forced);
|
||||||
LoadBalancerCertMapVO certMapRule = _lbCertMapDao.findByLbRuleId(loadBalancer.getId());
|
|
||||||
if (certMapRule != null)
|
|
||||||
throw new InvalidParameterValueException("Another certificate is already bound to the LB");
|
|
||||||
|
|
||||||
//check for correct port
|
//check for correct port
|
||||||
if (loadBalancer.getLbProtocol() == null || !(loadBalancer.getLbProtocol().equals(NetUtils.SSL_PROTO)))
|
if (loadBalancer.getLbProtocol() == null || !(loadBalancer.getLbProtocol().equals(NetUtils.SSL_PROTO)))
|
||||||
@ -1326,6 +1323,18 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateCertMapRule(long lbRuleId, boolean forced) {
|
||||||
|
//check if the lb is already bound
|
||||||
|
LoadBalancerCertMapVO certMapRule = _lbCertMapDao.findByLbRuleId(lbRuleId);
|
||||||
|
if (certMapRule != null) {
|
||||||
|
if (!forced) {
|
||||||
|
throw new InvalidParameterValueException("Another certificate is already bound to the LB");
|
||||||
|
}
|
||||||
|
logger.debug("Another certificate is already bound to the LB, removing it");
|
||||||
|
removeCertFromLoadBalancer(lbRuleId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@DB
|
@DB
|
||||||
@ActionEvent(eventType = EventTypes.EVENT_LB_CERT_REMOVE, eventDescription = "removing certificate from load balancer", async = true)
|
@ActionEvent(eventType = EventTypes.EVENT_LB_CERT_REMOVE, eventDescription = "removing certificate from load balancer", async = true)
|
||||||
@ -1987,7 +1996,7 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
|
|||||||
return handled;
|
return handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoadBalancingRule getLoadBalancerRuleToApply(LoadBalancerVO lb) {
|
protected LoadBalancingRule getLoadBalancerRuleToApply(LoadBalancerVO lb) {
|
||||||
|
|
||||||
List<LbStickinessPolicy> policyList = getStickinessPolicies(lb.getId());
|
List<LbStickinessPolicy> policyList = getStickinessPolicies(lb.getId());
|
||||||
Ip sourceIp = getSourceIp(lb);
|
Ip sourceIp = getSourceIp(lb);
|
||||||
@ -2257,12 +2266,17 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
|
|||||||
LoadBalancerVO tmplbVo = _lbDao.findById(lbRuleId);
|
LoadBalancerVO tmplbVo = _lbDao.findById(lbRuleId);
|
||||||
boolean success = _lbDao.update(lbRuleId, lb);
|
boolean success = _lbDao.update(lbRuleId, lb);
|
||||||
|
|
||||||
// If algorithm is changed, have to reapply the lb config
|
// If algorithm or lb protocol is changed, have to reapply the lb config
|
||||||
if ((algorithm != null) && (tmplbVo.getAlgorithm().compareTo(algorithm) != 0)){
|
boolean needToReApplyRule = (algorithm != null && !algorithm.equals(tmplbVo.getAlgorithm()))
|
||||||
|
|| (lbProtocol != null && !lbProtocol.equals(tmplbVo.getLbProtocol()));
|
||||||
|
if (needToReApplyRule) {
|
||||||
try {
|
try {
|
||||||
lb.setState(FirewallRule.State.Add);
|
lb.setState(FirewallRule.State.Add);
|
||||||
_lbDao.persist(lb);
|
_lbDao.persist(lb);
|
||||||
applyLoadBalancerConfig(lbRuleId);
|
applyLoadBalancerConfig(lbRuleId);
|
||||||
|
if (!lb.getLbProtocol().equals(NetUtils.SSL_PROTO)) {
|
||||||
|
removeCertMapIfExists(lb);
|
||||||
|
}
|
||||||
} catch (ResourceUnavailableException e) {
|
} catch (ResourceUnavailableException e) {
|
||||||
if (isRollBackAllowedForProvider(lb)) {
|
if (isRollBackAllowedForProvider(lb)) {
|
||||||
/*
|
/*
|
||||||
@ -2279,6 +2293,9 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
|
|||||||
if (lbBackup.getAlgorithm() != null) {
|
if (lbBackup.getAlgorithm() != null) {
|
||||||
lb.setAlgorithm(lbBackup.getAlgorithm());
|
lb.setAlgorithm(lbBackup.getAlgorithm());
|
||||||
}
|
}
|
||||||
|
if (lbBackup.getLbProtocol() != null) {
|
||||||
|
lb.setLbProtocol(lbBackup.getLbProtocol());
|
||||||
|
}
|
||||||
lb.setState(lbBackup.getState());
|
lb.setState(lbBackup.getState());
|
||||||
_lbDao.update(lb.getId(), lb);
|
_lbDao.update(lb.getId(), lb);
|
||||||
_lbDao.persist(lb);
|
_lbDao.persist(lb);
|
||||||
@ -2309,6 +2326,14 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeCertMapIfExists(LoadBalancerVO lb) {
|
||||||
|
LoadBalancerCertMapVO loadBalancerCertMapVO = _lbCertMapDao.findByLbRuleId(lb.getId());
|
||||||
|
if (loadBalancerCertMapVO != null) {
|
||||||
|
logger.debug("Removing SSL cert for load balancer %s as the new protocol is not ssl but %s", lb, lb.getLbProtocol());
|
||||||
|
_lbCertMapDao.remove(loadBalancerCertMapVO.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Pair<List<? extends UserVm>, List<String>> listLoadBalancerInstances(ListLoadBalancerRuleInstancesCmd cmd) throws PermissionDeniedException {
|
public Pair<List<? extends UserVm>, List<String>> listLoadBalancerInstances(ListLoadBalancerRuleInstancesCmd cmd) throws PermissionDeniedException {
|
||||||
Account caller = CallContext.current().getCallingAccount();
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
|||||||
@ -366,6 +366,7 @@ public class CommandSetupHelper {
|
|||||||
final LoadBalancerTO lb = new LoadBalancerTO(uuid, srcIp, srcPort, protocol, algorithm, revoked, false, inline, destinations, stickinessPolicies);
|
final LoadBalancerTO lb = new LoadBalancerTO(uuid, srcIp, srcPort, protocol, algorithm, revoked, false, inline, destinations, stickinessPolicies);
|
||||||
lb.setCidrList(rule.getCidrList());
|
lb.setCidrList(rule.getCidrList());
|
||||||
lb.setLbProtocol(lb_protocol);
|
lb.setLbProtocol(lb_protocol);
|
||||||
|
lb.setLbSslCert(rule.getLbSslCert());
|
||||||
lbs[i++] = lb;
|
lbs[i++] = lb;
|
||||||
}
|
}
|
||||||
String routerPublicIp = null;
|
String routerPublicIp = null;
|
||||||
|
|||||||
@ -929,6 +929,8 @@ public class NetworkHelperImpl implements NetworkHelper {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateHAproxyLbProtocol(rule.getLbProtocol());
|
||||||
|
|
||||||
for (final LoadBalancingRule.LbStickinessPolicy stickinessPolicy : rule.getStickinessPolicies()) {
|
for (final LoadBalancingRule.LbStickinessPolicy stickinessPolicy : rule.getStickinessPolicies()) {
|
||||||
final List<Pair<String, String>> paramsList = stickinessPolicy.getParams();
|
final List<Pair<String, String>> paramsList = stickinessPolicy.getParams();
|
||||||
|
|
||||||
@ -982,6 +984,13 @@ public class NetworkHelperImpl implements NetworkHelper {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateHAproxyLbProtocol(String lbProtocol) {
|
||||||
|
List<String> lbProtocols = Arrays.asList("tcp", "udp", "tcp-proxy", "ssl");
|
||||||
|
if (lbProtocol != null && !lbProtocols.contains(lbProtocol)) {
|
||||||
|
throw new InvalidParameterValueException(String.format("protocol %s is not in valid protocols %s", lbProtocol, lbProtocols));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function detects numbers like 12 ,32h ,42m .. etc,. 1) plain number
|
* This function detects numbers like 12 ,32h ,42m .. etc,. 1) plain number
|
||||||
* like 12 2) time or tablesize like 12h, 34m, 45k, 54m , here last
|
* like 12 2) time or tablesize like 12h, 34m, 45k, 54m , here last
|
||||||
|
|||||||
@ -1737,11 +1737,13 @@ Configurable, StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualM
|
|||||||
.append(",sourcePortEnd=").append(firewallRuleVO.getSourcePortEnd());
|
.append(",sourcePortEnd=").append(firewallRuleVO.getSourcePortEnd());
|
||||||
if (firewallRuleVO instanceof LoadBalancerVO) {
|
if (firewallRuleVO instanceof LoadBalancerVO) {
|
||||||
LoadBalancerVO loadBalancerVO = (LoadBalancerVO) firewallRuleVO;
|
LoadBalancerVO loadBalancerVO = (LoadBalancerVO) firewallRuleVO;
|
||||||
loadBalancingData.append(",sourceIp=").append(_ipAddressDao.findById(loadBalancerVO.getSourceIpAddressId()).getAddress().toString())
|
String sourceIp = _ipAddressDao.findById(loadBalancerVO.getSourceIpAddressId()).getAddress().toString();
|
||||||
|
loadBalancingData.append(",sourceIp=").append(sourceIp)
|
||||||
.append(",destPortStart=").append(loadBalancerVO.getDefaultPortStart())
|
.append(",destPortStart=").append(loadBalancerVO.getDefaultPortStart())
|
||||||
.append(",destPortEnd=").append(loadBalancerVO.getDefaultPortEnd())
|
.append(",destPortEnd=").append(loadBalancerVO.getDefaultPortEnd())
|
||||||
.append(",algorithm=").append(loadBalancerVO.getAlgorithm())
|
.append(",algorithm=").append(loadBalancerVO.getAlgorithm())
|
||||||
.append(",protocol=").append(loadBalancerVO.getLbProtocol());
|
.append(",protocol=").append(loadBalancerVO.getLbProtocol());
|
||||||
|
updateWithLbRuleSslCertificates(loadBalancingData, loadBalancerVO, sourceIp);
|
||||||
} else if (firewallRuleVO instanceof ApplicationLoadBalancerRuleVO) {
|
} else if (firewallRuleVO instanceof ApplicationLoadBalancerRuleVO) {
|
||||||
ApplicationLoadBalancerRuleVO appLoadBalancerVO = (ApplicationLoadBalancerRuleVO) firewallRuleVO;
|
ApplicationLoadBalancerRuleVO appLoadBalancerVO = (ApplicationLoadBalancerRuleVO) firewallRuleVO;
|
||||||
loadBalancingData.append(",sourceIp=").append(appLoadBalancerVO.getSourceIp())
|
loadBalancingData.append(",sourceIp=").append(appLoadBalancerVO.getSourceIp())
|
||||||
@ -1760,6 +1762,16 @@ Configurable, StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void updateWithLbRuleSslCertificates(final StringBuilder loadBalancingData, LoadBalancerVO loadBalancerVO, String sourceIp) {
|
||||||
|
if (NetUtils.SSL_PROTO.equals(loadBalancerVO.getLbProtocol())) {
|
||||||
|
final LbSslCert sslCert = _lbMgr.getLbSslCert(loadBalancerVO.getId());
|
||||||
|
if (sslCert != null && ! sslCert.isRevoked()) {
|
||||||
|
loadBalancingData.append(",sslcert=").append(sourceIp.replace(".", "_")).append('-')
|
||||||
|
.append(loadBalancerVO.getSourcePortStart()).append(".pem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected Map<String, String> getRouterHealthChecksConfig(final DomainRouterVO router) {
|
protected Map<String, String> getRouterHealthChecksConfig(final DomainRouterVO router) {
|
||||||
Map<String, String> data = new HashMap<>();
|
Map<String, String> data = new HashMap<>();
|
||||||
List<DomainRouterJoinVO> routerJoinVOs = domainRouterJoinDao.searchByIds(router.getId());
|
List<DomainRouterJoinVO> routerJoinVOs = domainRouterJoinDao.searchByIds(router.getId());
|
||||||
|
|||||||
@ -136,6 +136,7 @@ import com.cloud.network.dao.NetworkDao;
|
|||||||
import com.cloud.network.dao.NetworkVO;
|
import com.cloud.network.dao.NetworkVO;
|
||||||
import com.cloud.network.dao.RemoteAccessVpnDao;
|
import com.cloud.network.dao.RemoteAccessVpnDao;
|
||||||
import com.cloud.network.dao.RemoteAccessVpnVO;
|
import com.cloud.network.dao.RemoteAccessVpnVO;
|
||||||
|
import com.cloud.network.dao.SslCertDao;
|
||||||
import com.cloud.network.dao.VpnUserDao;
|
import com.cloud.network.dao.VpnUserDao;
|
||||||
import com.cloud.network.router.VirtualRouter;
|
import com.cloud.network.router.VirtualRouter;
|
||||||
import com.cloud.network.security.SecurityGroupManager;
|
import com.cloud.network.security.SecurityGroupManager;
|
||||||
@ -309,6 +310,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
private UserDataDao userDataDao;
|
private UserDataDao userDataDao;
|
||||||
@Inject
|
@Inject
|
||||||
private NetworkPermissionDao networkPermissionDao;
|
private NetworkPermissionDao networkPermissionDao;
|
||||||
|
@Inject
|
||||||
|
private SslCertDao sslCertDao;
|
||||||
|
|
||||||
private List<QuerySelector> _querySelectors;
|
private List<QuerySelector> _querySelectors;
|
||||||
|
|
||||||
@ -1203,6 +1206,9 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
// Delete registered UserData
|
// Delete registered UserData
|
||||||
userDataDao.removeByAccountId(accountId);
|
userDataDao.removeByAccountId(accountId);
|
||||||
|
|
||||||
|
// Delete SSL certificates
|
||||||
|
sslCertDao.removeByAccountId(accountId);
|
||||||
|
|
||||||
// Delete Webhooks
|
// Delete Webhooks
|
||||||
deleteWebhooksForAccount(accountId);
|
deleteWebhooksForAccount(accountId);
|
||||||
|
|
||||||
|
|||||||
@ -26,8 +26,9 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.security.NoSuchProviderException;
|
import java.security.NoSuchProviderException;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.security.SignatureException;
|
||||||
import java.security.cert.CertPathBuilder;
|
import java.security.cert.CertPathBuilder;
|
||||||
import java.security.cert.CertPathBuilderException;
|
import java.security.cert.CertPathBuilderException;
|
||||||
import java.security.cert.CertStore;
|
import java.security.cert.CertStore;
|
||||||
@ -48,10 +49,6 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import org.apache.cloudstack.acl.SecurityChecker;
|
import org.apache.cloudstack.acl.SecurityChecker;
|
||||||
@ -62,9 +59,21 @@ import org.apache.cloudstack.api.response.SslCertResponse;
|
|||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.network.tls.CertService;
|
import org.apache.cloudstack.network.tls.CertService;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
|
||||||
|
import org.bouncycastle.openssl.PEMKeyPair;
|
||||||
|
import org.bouncycastle.openssl.PEMParser;
|
||||||
|
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
|
||||||
|
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
|
||||||
|
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
|
||||||
|
import org.bouncycastle.operator.InputDecryptorProvider;
|
||||||
|
import org.bouncycastle.operator.OperatorCreationException;
|
||||||
|
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
|
||||||
|
import org.bouncycastle.pkcs.PKCSException;
|
||||||
import org.bouncycastle.util.io.pem.PemObject;
|
import org.bouncycastle.util.io.pem.PemObject;
|
||||||
import org.bouncycastle.util.io.pem.PemReader;
|
import org.bouncycastle.util.io.pem.PemReader;
|
||||||
|
|
||||||
@ -89,7 +98,6 @@ import com.cloud.utils.db.EntityManager;
|
|||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
import com.cloud.utils.security.CertificateHelper;
|
import com.cloud.utils.security.CertificateHelper;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
public class CertServiceImpl implements CertService {
|
public class CertServiceImpl implements CertService {
|
||||||
|
|
||||||
@ -279,11 +287,11 @@ public class CertServiceImpl implements CertService {
|
|||||||
return certResponseList;
|
return certResponseList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validate(final String certInput, final String keyInput, final String password, final String chainInput, boolean revocationEnabled) {
|
protected void validate(final String certInput, final String keyInput, final String password, final String chainInput, boolean revocationEnabled) {
|
||||||
try {
|
try {
|
||||||
List<Certificate> chain = null;
|
List<Certificate> chain = null;
|
||||||
final Certificate cert = parseCertificate(certInput);
|
final Certificate cert = parseCertificate(certInput);
|
||||||
final PrivateKey key = parsePrivateKey(keyInput);
|
final PrivateKey key = parsePrivateKey(keyInput, password);
|
||||||
|
|
||||||
if (chainInput != null) {
|
if (chainInput != null) {
|
||||||
chain = CertificateHelper.parseChain(chainInput);
|
chain = CertificateHelper.parseChain(chainInput);
|
||||||
@ -295,7 +303,9 @@ public class CertServiceImpl implements CertService {
|
|||||||
if (chainInput != null) {
|
if (chainInput != null) {
|
||||||
validateChain(chain, cert, revocationEnabled);
|
validateChain(chain, cert, revocationEnabled);
|
||||||
}
|
}
|
||||||
} catch (final IOException | CertificateException e) {
|
} catch (final IOException | CertificateException | OperatorCreationException | PKCSException |
|
||||||
|
NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||||
|
logger.warn("Failed to validate certificate", e);
|
||||||
throw new IllegalStateException("Parsing certificate/key failed: " + e.getMessage(), e);
|
throw new IllegalStateException("Parsing certificate/key failed: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -370,18 +380,17 @@ public class CertServiceImpl implements CertService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final String data = "ENCRYPT_DATA";
|
final String data = "ENCRYPT_DATA";
|
||||||
final SecureRandom random = new SecureRandom();
|
Signature sig = Signature.getInstance("SHA256withRSA");
|
||||||
final Cipher cipher = Cipher.getInstance(pubKey.getAlgorithm());
|
sig.initSign(privKey);
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, privKey, random);
|
sig.update(data.getBytes());
|
||||||
final byte[] encryptedData = cipher.doFinal(data.getBytes());
|
byte[] signature = sig.sign();
|
||||||
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, pubKey, random);
|
sig.initVerify(pubKey);
|
||||||
final String decreptedData = new String(cipher.doFinal(encryptedData));
|
sig.update(data.getBytes());
|
||||||
if (!decreptedData.equals(data)) {
|
if (!sig.verify(signature)) {
|
||||||
throw new IllegalStateException("Bad public-private key");
|
throw new IllegalStateException("Bad public-private key");
|
||||||
}
|
}
|
||||||
|
} catch (final InvalidKeyException | SignatureException e) {
|
||||||
} catch (final BadPaddingException | IllegalBlockSizeException | InvalidKeyException | NoSuchPaddingException e) {
|
|
||||||
throw new IllegalStateException("Bad public-private key", e);
|
throw new IllegalStateException("Bad public-private key", e);
|
||||||
} catch (final NoSuchAlgorithmException e) {
|
} catch (final NoSuchAlgorithmException e) {
|
||||||
throw new IllegalStateException("Invalid algorithm for public-private key", e);
|
throw new IllegalStateException("Invalid algorithm for public-private key", e);
|
||||||
@ -423,19 +432,55 @@ public class CertServiceImpl implements CertService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PrivateKey parsePrivateKey(final String key) throws IOException {
|
public PrivateKey parsePrivateKey(final String key, String password) throws IOException, OperatorCreationException, PKCSException, NoSuchAlgorithmException, InvalidKeySpecException {
|
||||||
Preconditions.checkArgument(StringUtils.isNotEmpty(key));
|
Preconditions.checkArgument(StringUtils.isNotEmpty(key));
|
||||||
try (final PemReader pemReader = new PemReader(new StringReader(key));) {
|
PEMParser pemParser = new PEMParser(new StringReader(key));
|
||||||
final PemObject pemObject = pemReader.readPemObject();
|
Object privateKeyObj = pemParser.readObject();
|
||||||
final byte[] content = pemObject.getContent();
|
if (privateKeyObj == null) {
|
||||||
final PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
|
throw new CloudRuntimeException("Cannot parse private key");
|
||||||
final KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
|
|
||||||
return factory.generatePrivate(privKeySpec);
|
|
||||||
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
|
|
||||||
throw new IOException("No encryption provider available.", e);
|
|
||||||
} catch (final InvalidKeySpecException e) {
|
|
||||||
throw new IOException("Invalid Key format.", e);
|
|
||||||
}
|
}
|
||||||
|
PrivateKey privateKey;
|
||||||
|
if (privateKeyObj instanceof PKCS8EncryptedPrivateKeyInfo) {
|
||||||
|
privateKey = parsePKCS8EncryptedPrivateKeyInfo((PKCS8EncryptedPrivateKeyInfo)privateKeyObj, password);
|
||||||
|
} else if (privateKeyObj instanceof PEMEncryptedKeyPair) {
|
||||||
|
privateKey = parsePEMEncryptedKeyPair((PEMEncryptedKeyPair)privateKeyObj, password);
|
||||||
|
} else if (privateKeyObj instanceof PEMKeyPair) {
|
||||||
|
// Key pair
|
||||||
|
PEMKeyPair pemKeyPair = (PEMKeyPair) privateKeyObj;
|
||||||
|
privateKey = new JcaPEMKeyConverter().getKeyPair(pemKeyPair).getPrivate();
|
||||||
|
} else if (privateKeyObj instanceof PrivateKeyInfo) {
|
||||||
|
// Private key only
|
||||||
|
PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) privateKeyObj;
|
||||||
|
privateKey = new JcaPEMKeyConverter().getPrivateKey(privateKeyInfo);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unsupported PEM object: " + privateKeyObj.getClass());
|
||||||
|
}
|
||||||
|
pemParser.close();
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PrivateKey parsePKCS8EncryptedPrivateKeyInfo(PKCS8EncryptedPrivateKeyInfo privateKeyObj, String password)
|
||||||
|
throws IOException, OperatorCreationException, PKCSException, NoSuchAlgorithmException, InvalidKeySpecException {
|
||||||
|
if (password == null) {
|
||||||
|
throw new CloudRuntimeException("Key is encrypted by PKCS#8 but password is null");
|
||||||
|
}
|
||||||
|
PKCS8EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = (PKCS8EncryptedPrivateKeyInfo)privateKeyObj;
|
||||||
|
JceOpenSSLPKCS8DecryptorProviderBuilder builder = new JceOpenSSLPKCS8DecryptorProviderBuilder();
|
||||||
|
InputDecryptorProvider decryptor = builder.build(password.toCharArray());
|
||||||
|
|
||||||
|
PrivateKeyInfo privateKeyInfo = encryptedPrivateKeyInfo.decryptPrivateKeyInfo(decryptor);
|
||||||
|
String algorithm = privateKeyInfo.getPrivateKeyAlgorithm().getAlgorithm().getId();
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
|
||||||
|
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyInfo.getEncoded());
|
||||||
|
return keyFactory.generatePrivate(keySpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PrivateKey parsePEMEncryptedKeyPair(PEMEncryptedKeyPair encryptedKeyPair, String password) throws IOException {
|
||||||
|
if (password == null) {
|
||||||
|
throw new CloudRuntimeException("Key is encrypted but password is null");
|
||||||
|
}
|
||||||
|
return new JcaPEMKeyConverter().getKeyPair(
|
||||||
|
encryptedKeyPair.decryptKeyPair(new JcePEMDecryptorProviderBuilder().build(password.toCharArray()))).getPrivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -17,12 +17,30 @@
|
|||||||
|
|
||||||
package com.cloud.network.lb;
|
package com.cloud.network.lb;
|
||||||
|
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
import com.cloud.network.Network;
|
import com.cloud.network.Network;
|
||||||
|
import com.cloud.network.NetworkModel;
|
||||||
|
import com.cloud.network.dao.LoadBalancerCertMapDao;
|
||||||
|
import com.cloud.network.dao.LoadBalancerCertMapVO;
|
||||||
|
import com.cloud.network.dao.LoadBalancerDao;
|
||||||
import com.cloud.network.dao.LoadBalancerVO;
|
import com.cloud.network.dao.LoadBalancerVO;
|
||||||
import com.cloud.network.dao.NetworkDao;
|
import com.cloud.network.dao.NetworkDao;
|
||||||
import com.cloud.network.dao.NetworkVO;
|
import com.cloud.network.dao.NetworkVO;
|
||||||
|
import com.cloud.network.dao.SslCertVO;
|
||||||
|
import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
|
||||||
|
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.db.EntityManager;
|
||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
import com.cloud.utils.net.NetUtils;
|
||||||
|
import org.apache.cloudstack.acl.SecurityChecker;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
import org.apache.cloudstack.api.ServerApiException;
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.command.user.loadbalancer.UpdateLoadBalancerRuleCmd;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -32,11 +50,16 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.Spy;
|
import org.mockito.Spy;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
@ -48,10 +71,39 @@ public class LoadBalancingRulesManagerImplTest{
|
|||||||
@Mock
|
@Mock
|
||||||
NetworkOrchestrationService _networkMgr;
|
NetworkOrchestrationService _networkMgr;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
LoadBalancerDao _lbDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
EntityManager _entityMgr;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
AccountManager _accountMgr;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
NetworkModel _networkModel;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
LoadBalancerCertMapDao _lbCertMapDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
NetworkOfferingServiceMapDao _networkOfferingServiceDao;
|
||||||
|
|
||||||
@Spy
|
@Spy
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
LoadBalancingRulesManagerImpl lbr = new LoadBalancingRulesManagerImpl();
|
LoadBalancingRulesManagerImpl lbr = new LoadBalancingRulesManagerImpl();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
NetworkVO networkMock;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
LoadBalancerVO loadBalancerMock;
|
||||||
|
|
||||||
|
private long accountId = 10L;
|
||||||
|
private long lbRuleId = 2L;
|
||||||
|
private long certMapRuleId = 3L;
|
||||||
|
private long networkId = 4L;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void generateCidrStringTestNullCidrList() {
|
public void generateCidrStringTestNullCidrList() {
|
||||||
String result = lbr.generateCidrString(null);
|
String result = lbr.generateCidrString(null);
|
||||||
@ -83,7 +135,7 @@ public class LoadBalancingRulesManagerImplTest{
|
|||||||
List<Network.Provider> providers = Arrays.asList(Network.Provider.VirtualRouter);
|
List<Network.Provider> providers = Arrays.asList(Network.Provider.VirtualRouter);
|
||||||
|
|
||||||
when(loadBalancerMock.getNetworkId()).thenReturn(10L);
|
when(loadBalancerMock.getNetworkId()).thenReturn(10L);
|
||||||
when(_networkDao.findById(Mockito.anyLong())).thenReturn(networkMock);
|
when(_networkDao.findById(anyLong())).thenReturn(networkMock);
|
||||||
when(_networkMgr.getProvidersForServiceInNetwork(networkMock, Network.Service.Lb)).thenReturn(providers);
|
when(_networkMgr.getProvidersForServiceInNetwork(networkMock, Network.Service.Lb)).thenReturn(providers);
|
||||||
|
|
||||||
Network.Provider provider = lbr.getLoadBalancerServiceProvider(loadBalancerMock);
|
Network.Provider provider = lbr.getLoadBalancerServiceProvider(loadBalancerMock);
|
||||||
@ -101,4 +153,159 @@ public class LoadBalancingRulesManagerImplTest{
|
|||||||
|
|
||||||
Network.Provider provider = lbr.getLoadBalancerServiceProvider(loadBalancerMock);
|
Network.Provider provider = lbr.getLoadBalancerServiceProvider(loadBalancerMock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAssignCertToLoadBalancer() throws Exception {
|
||||||
|
long accountId = 10L;
|
||||||
|
long lbRuleId = 2L;
|
||||||
|
long certId = 3L;
|
||||||
|
long networkId = 4L;
|
||||||
|
|
||||||
|
AccountVO account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
|
||||||
|
account.setId(accountId);
|
||||||
|
UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone",
|
||||||
|
UUID.randomUUID().toString(), User.Source.UNKNOWN);
|
||||||
|
CallContext.register(user, account);
|
||||||
|
|
||||||
|
LoadBalancerVO loadBalancerMock = Mockito.mock(LoadBalancerVO.class);
|
||||||
|
when(_lbDao.findById(lbRuleId)).thenReturn(loadBalancerMock);
|
||||||
|
when(loadBalancerMock.getId()).thenReturn(lbRuleId);
|
||||||
|
when(loadBalancerMock.getAccountId()).thenReturn(accountId);
|
||||||
|
when(loadBalancerMock.getNetworkId()).thenReturn(networkId);
|
||||||
|
when(loadBalancerMock.getLbProtocol()).thenReturn(NetUtils.SSL_PROTO);
|
||||||
|
|
||||||
|
SslCertVO certVO = Mockito.mock(SslCertVO.class);
|
||||||
|
when(_entityMgr.findById(SslCertVO.class, certId)).thenReturn(certVO);
|
||||||
|
when(certVO.getAccountId()).thenReturn(accountId);
|
||||||
|
|
||||||
|
LoadBalancerCertMapVO certMapRule = Mockito.mock(LoadBalancerCertMapVO.class);
|
||||||
|
when(_lbCertMapDao.findByLbRuleId(lbRuleId)).thenReturn(certMapRule);
|
||||||
|
|
||||||
|
Mockito.doNothing().when(_accountMgr).checkAccess(Mockito.any(Account.class), Mockito.isNull(SecurityChecker.AccessType.class), Mockito.eq(true), Mockito.any(LoadBalancerVO.class));
|
||||||
|
|
||||||
|
Mockito.doReturn("LB").when(lbr).getLBCapability(networkId, Network.Capability.SslTermination.getName());
|
||||||
|
Mockito.doReturn(true).when(lbr).applyLoadBalancerConfig(lbRuleId);
|
||||||
|
|
||||||
|
lbr.assignCertToLoadBalancer(lbRuleId, certId, true);
|
||||||
|
|
||||||
|
Mockito.verify(lbr, times(2)).applyLoadBalancerConfig(lbRuleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupUpdateLoadBalancerRule() throws Exception{
|
||||||
|
AccountVO account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
|
||||||
|
account.setId(accountId);
|
||||||
|
UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone",
|
||||||
|
UUID.randomUUID().toString(), User.Source.UNKNOWN);
|
||||||
|
CallContext.register(user, account);
|
||||||
|
|
||||||
|
when(_lbDao.findById(lbRuleId)).thenReturn(loadBalancerMock);
|
||||||
|
when(loadBalancerMock.getId()).thenReturn(lbRuleId);
|
||||||
|
when(loadBalancerMock.getNetworkId()).thenReturn(networkId);
|
||||||
|
|
||||||
|
when(_networkDao.findById(networkId)).thenReturn(networkMock);
|
||||||
|
|
||||||
|
Mockito.doNothing().when(_accountMgr).checkAccess(Mockito.any(Account.class), Mockito.isNull(SecurityChecker.AccessType.class), Mockito.eq(true), Mockito.any(LoadBalancerVO.class));
|
||||||
|
|
||||||
|
LoadBalancingRule loadBalancingRule = Mockito.mock(LoadBalancingRule.class);
|
||||||
|
Mockito.doReturn(loadBalancingRule).when(lbr).getLoadBalancerRuleToApply(loadBalancerMock);
|
||||||
|
Mockito.doReturn(true).when(lbr).validateLbRule(loadBalancingRule);
|
||||||
|
Mockito.doReturn(true).when(lbr).applyLoadBalancerConfig(lbRuleId);
|
||||||
|
|
||||||
|
when(_lbDao.update(lbRuleId, loadBalancerMock)).thenReturn(true);
|
||||||
|
|
||||||
|
LoadBalancerCertMapVO certMapRule = Mockito.mock(LoadBalancerCertMapVO.class);
|
||||||
|
when(_lbCertMapDao.findByLbRuleId(lbRuleId)).thenReturn(certMapRule);
|
||||||
|
when(certMapRule.getId()).thenReturn(certMapRuleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateLoadBalancerRule1() throws Exception {
|
||||||
|
setupUpdateLoadBalancerRule();
|
||||||
|
|
||||||
|
// Update protocol from TCP to SSL
|
||||||
|
UpdateLoadBalancerRuleCmd cmd = new UpdateLoadBalancerRuleCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, ApiConstants.ID, lbRuleId);
|
||||||
|
ReflectionTestUtils.setField(cmd, "lbProtocol", NetUtils.SSL_PROTO);
|
||||||
|
when(loadBalancerMock.getLbProtocol()).thenReturn(NetUtils.TCP_PROTO).thenReturn(NetUtils.SSL_PROTO);
|
||||||
|
|
||||||
|
lbr.updateLoadBalancerRule(cmd);
|
||||||
|
|
||||||
|
Mockito.verify(lbr, times(1)).applyLoadBalancerConfig(lbRuleId);
|
||||||
|
Mockito.verify(_lbCertMapDao, never()).remove(anyLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateLoadBalancerRule2() throws Exception {
|
||||||
|
setupUpdateLoadBalancerRule();
|
||||||
|
|
||||||
|
// Update protocol from SSL to TCP
|
||||||
|
UpdateLoadBalancerRuleCmd cmd = new UpdateLoadBalancerRuleCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, ApiConstants.ID, lbRuleId);
|
||||||
|
ReflectionTestUtils.setField(cmd, "lbProtocol", NetUtils.TCP_PROTO);
|
||||||
|
when(loadBalancerMock.getLbProtocol()).thenReturn(NetUtils.SSL_PROTO).thenReturn(NetUtils.TCP_PROTO);
|
||||||
|
|
||||||
|
lbr.updateLoadBalancerRule(cmd);
|
||||||
|
|
||||||
|
Mockito.verify(_lbCertMapDao, times(1)).remove(anyLong());
|
||||||
|
Mockito.verify(lbr, times(1)).applyLoadBalancerConfig(lbRuleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateLoadBalancerRule3() throws Exception {
|
||||||
|
setupUpdateLoadBalancerRule();
|
||||||
|
|
||||||
|
// Update algorithm from source to roundrobin, lb protocol is same
|
||||||
|
UpdateLoadBalancerRuleCmd cmd = new UpdateLoadBalancerRuleCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, ApiConstants.ID, lbRuleId);
|
||||||
|
ReflectionTestUtils.setField(cmd, "algorithm", "roundrobin");
|
||||||
|
ReflectionTestUtils.setField(cmd, "lbProtocol", NetUtils.SSL_PROTO);
|
||||||
|
when(loadBalancerMock.getAlgorithm()).thenReturn("source");
|
||||||
|
when(loadBalancerMock.getLbProtocol()).thenReturn(NetUtils.SSL_PROTO);
|
||||||
|
|
||||||
|
lbr.updateLoadBalancerRule(cmd);
|
||||||
|
|
||||||
|
Mockito.verify(lbr, times(1)).applyLoadBalancerConfig(lbRuleId);
|
||||||
|
Mockito.verify(_lbCertMapDao, never()).remove(anyLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateLoadBalancerRule4() throws Exception {
|
||||||
|
setupUpdateLoadBalancerRule();
|
||||||
|
|
||||||
|
// Update with same algorithm and protocol
|
||||||
|
UpdateLoadBalancerRuleCmd cmd = new UpdateLoadBalancerRuleCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, ApiConstants.ID, lbRuleId);
|
||||||
|
ReflectionTestUtils.setField(cmd, "algorithm", "roundrobin");
|
||||||
|
ReflectionTestUtils.setField(cmd, "lbProtocol", NetUtils.SSL_PROTO);
|
||||||
|
when(loadBalancerMock.getAlgorithm()).thenReturn("roundrobin");
|
||||||
|
when(loadBalancerMock.getLbProtocol()).thenReturn(NetUtils.SSL_PROTO);
|
||||||
|
|
||||||
|
lbr.updateLoadBalancerRule(cmd);
|
||||||
|
|
||||||
|
Mockito.verify(lbr, never()).applyLoadBalancerConfig(lbRuleId);
|
||||||
|
Mockito.verify(_lbCertMapDao, never()).remove(anyLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = CloudRuntimeException.class)
|
||||||
|
public void testUpdateLoadBalancerRule5() throws Exception {
|
||||||
|
setupUpdateLoadBalancerRule();
|
||||||
|
|
||||||
|
// Update protocol from SSL to TCP, throws an exception
|
||||||
|
UpdateLoadBalancerRuleCmd cmd = new UpdateLoadBalancerRuleCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, ApiConstants.ID, lbRuleId);
|
||||||
|
ReflectionTestUtils.setField(cmd, "lbProtocol", NetUtils.TCP_PROTO);
|
||||||
|
when(loadBalancerMock.getLbProtocol()).thenReturn(NetUtils.SSL_PROTO).thenReturn(NetUtils.TCP_PROTO);
|
||||||
|
Mockito.doThrow(ResourceUnavailableException.class).when(lbr).applyLoadBalancerConfig(lbRuleId);
|
||||||
|
|
||||||
|
List<Network.Provider> providers = Arrays.asList(Network.Provider.VirtualRouter);
|
||||||
|
when(_networkDao.findById(anyLong())).thenReturn(networkMock);
|
||||||
|
when(_networkMgr.getProvidersForServiceInNetwork(networkMock, Network.Service.Lb)).thenReturn(providers);
|
||||||
|
|
||||||
|
lbr.updateLoadBalancerRule(cmd);
|
||||||
|
|
||||||
|
Mockito.verify(_lbCertMapDao, never()).remove(anyLong());
|
||||||
|
Mockito.verify(lbr, times(1)).applyLoadBalancerConfig(lbRuleId);
|
||||||
|
Mockito.verify(loadBalancerMock, times(1)).setLbProtocol(NetUtils.TCP_PROTO);
|
||||||
|
Mockito.verify(loadBalancerMock, times(1)).setLbProtocol(NetUtils.SSL_PROTO);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,7 @@ import com.cloud.network.dao.FirewallRulesDao;
|
|||||||
import com.cloud.network.dao.IPAddressDao;
|
import com.cloud.network.dao.IPAddressDao;
|
||||||
import com.cloud.network.dao.LoadBalancerDao;
|
import com.cloud.network.dao.LoadBalancerDao;
|
||||||
import com.cloud.network.dao.LoadBalancerVMMapDao;
|
import com.cloud.network.dao.LoadBalancerVMMapDao;
|
||||||
|
import com.cloud.network.dao.LoadBalancerVO;
|
||||||
import com.cloud.network.dao.MonitoringServiceDao;
|
import com.cloud.network.dao.MonitoringServiceDao;
|
||||||
import com.cloud.network.dao.NetworkDao;
|
import com.cloud.network.dao.NetworkDao;
|
||||||
import com.cloud.network.dao.NetworkVO;
|
import com.cloud.network.dao.NetworkVO;
|
||||||
@ -54,6 +55,8 @@ import com.cloud.network.dao.Site2SiteVpnGatewayDao;
|
|||||||
import com.cloud.network.dao.UserIpv6AddressDao;
|
import com.cloud.network.dao.UserIpv6AddressDao;
|
||||||
import com.cloud.network.dao.VirtualRouterProviderDao;
|
import com.cloud.network.dao.VirtualRouterProviderDao;
|
||||||
import com.cloud.network.dao.VpnUserDao;
|
import com.cloud.network.dao.VpnUserDao;
|
||||||
|
import com.cloud.network.lb.LoadBalancingRule;
|
||||||
|
import com.cloud.network.lb.LoadBalancingRulesManager;
|
||||||
import com.cloud.network.rules.dao.PortForwardingRulesDao;
|
import com.cloud.network.rules.dao.PortForwardingRulesDao;
|
||||||
import com.cloud.network.vpc.VpcVO;
|
import com.cloud.network.vpc.VpcVO;
|
||||||
import com.cloud.network.vpc.dao.VpcDao;
|
import com.cloud.network.vpc.dao.VpcDao;
|
||||||
@ -67,6 +70,7 @@ import com.cloud.storage.dao.VolumeDao;
|
|||||||
import com.cloud.user.dao.UserDao;
|
import com.cloud.user.dao.UserDao;
|
||||||
import com.cloud.user.dao.UserStatisticsDao;
|
import com.cloud.user.dao.UserStatisticsDao;
|
||||||
import com.cloud.user.dao.UserStatsLogDao;
|
import com.cloud.user.dao.UserStatsLogDao;
|
||||||
|
import com.cloud.utils.net.NetUtils;
|
||||||
import com.cloud.vm.DomainRouterVO;
|
import com.cloud.vm.DomainRouterVO;
|
||||||
import com.cloud.vm.VirtualMachine;
|
import com.cloud.vm.VirtualMachine;
|
||||||
import com.cloud.vm.VirtualMachineManager;
|
import com.cloud.vm.VirtualMachineManager;
|
||||||
@ -259,6 +263,9 @@ public class VirtualNetworkApplianceManagerImplTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private BGPService bgpService;
|
private BGPService bgpService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private LoadBalancingRulesManager _lbMgr;
|
||||||
|
|
||||||
// @InjectMocks
|
// @InjectMocks
|
||||||
// private VirtualNetworkApplianceManagerImpl virtualNetworkApplianceManagerImpl;
|
// private VirtualNetworkApplianceManagerImpl virtualNetworkApplianceManagerImpl;
|
||||||
|
|
||||||
@ -391,4 +398,21 @@ public class VirtualNetworkApplianceManagerImplTest {
|
|||||||
|
|
||||||
Mockito.verify(_commandSetupHelper).createBgpPeersCommands(bgpPeers, router, cmds, network);
|
Mockito.verify(_commandSetupHelper).createBgpPeersCommands(bgpPeers, router, cmds, network);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateWithLbRuleSslCertificates() {
|
||||||
|
StringBuilder loadBalancingData = new StringBuilder();
|
||||||
|
LoadBalancerVO loadBalancer = Mockito.mock(LoadBalancerVO.class);
|
||||||
|
when(loadBalancer.getLbProtocol()).thenReturn(NetUtils.SSL_PROTO);
|
||||||
|
when(loadBalancer.getId()).thenReturn(1L);
|
||||||
|
when(loadBalancer.getSourcePortStart()).thenReturn(443);
|
||||||
|
LoadBalancingRule.LbSslCert lbSslCert = Mockito.mock(LoadBalancingRule.LbSslCert.class);
|
||||||
|
when(lbSslCert.isRevoked()).thenReturn(false);
|
||||||
|
when(_lbMgr.getLbSslCert(1L)).thenReturn(lbSslCert);
|
||||||
|
String sourceIp = "1.2.3.4";
|
||||||
|
|
||||||
|
virtualNetworkApplianceManagerImpl.updateWithLbRuleSslCertificates(loadBalancingData, loadBalancer, sourceIp);
|
||||||
|
|
||||||
|
Assert.assertEquals(",sslcert=1_2_3_4-443.pem", loadBalancingData.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -201,6 +201,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
Mockito.when(_sshKeyPairDao.listKeyPairs(Mockito.anyLong(), Mockito.anyLong())).thenReturn(sshkeyList);
|
Mockito.when(_sshKeyPairDao.listKeyPairs(Mockito.anyLong(), Mockito.anyLong())).thenReturn(sshkeyList);
|
||||||
Mockito.when(_sshKeyPairDao.remove(Mockito.anyLong())).thenReturn(true);
|
Mockito.when(_sshKeyPairDao.remove(Mockito.anyLong())).thenReturn(true);
|
||||||
Mockito.when(userDataDao.removeByAccountId(Mockito.anyLong())).thenReturn(222);
|
Mockito.when(userDataDao.removeByAccountId(Mockito.anyLong())).thenReturn(222);
|
||||||
|
Mockito.when(sslCertDao.removeByAccountId(Mockito.anyLong())).thenReturn(333);
|
||||||
Mockito.doNothing().when(accountManagerImpl).deleteWebhooksForAccount(Mockito.anyLong());
|
Mockito.doNothing().when(accountManagerImpl).deleteWebhooksForAccount(Mockito.anyLong());
|
||||||
Mockito.doNothing().when(accountManagerImpl).verifyCallerPrivilegeForUserOrAccountOperations((Account) any());
|
Mockito.doNothing().when(accountManagerImpl).verifyCallerPrivilegeForUserOrAccountOperations((Account) any());
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,7 @@ import com.cloud.network.dao.AccountGuestVlanMapDao;
|
|||||||
import com.cloud.network.dao.IPAddressDao;
|
import com.cloud.network.dao.IPAddressDao;
|
||||||
import com.cloud.network.dao.NetworkDao;
|
import com.cloud.network.dao.NetworkDao;
|
||||||
import com.cloud.network.dao.RemoteAccessVpnDao;
|
import com.cloud.network.dao.RemoteAccessVpnDao;
|
||||||
|
import com.cloud.network.dao.SslCertDao;
|
||||||
import com.cloud.network.dao.VpnUserDao;
|
import com.cloud.network.dao.VpnUserDao;
|
||||||
import com.cloud.network.security.SecurityGroupManager;
|
import com.cloud.network.security.SecurityGroupManager;
|
||||||
import com.cloud.network.security.dao.SecurityGroupDao;
|
import com.cloud.network.security.dao.SecurityGroupDao;
|
||||||
@ -198,6 +199,8 @@ public class AccountManagetImplTestBase {
|
|||||||
@Mock
|
@Mock
|
||||||
UserDataDao userDataDao;
|
UserDataDao userDataDao;
|
||||||
@Mock
|
@Mock
|
||||||
|
SslCertDao sslCertDao;
|
||||||
|
@Mock
|
||||||
NetworkPermissionDao networkPermissionDaoMock;
|
NetworkPermissionDao networkPermissionDaoMock;
|
||||||
|
|
||||||
@Spy
|
@Spy
|
||||||
|
|||||||
@ -34,6 +34,13 @@ import com.cloud.utils.db.TransactionLegacy;
|
|||||||
import org.apache.cloudstack.api.command.user.loadbalancer.DeleteSslCertCmd;
|
import org.apache.cloudstack.api.command.user.loadbalancer.DeleteSslCertCmd;
|
||||||
import org.apache.cloudstack.api.command.user.loadbalancer.UploadSslCertCmd;
|
import org.apache.cloudstack.api.command.user.loadbalancer.UploadSslCertCmd;
|
||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.bouncycastle.openssl.PKCS8Generator;
|
||||||
|
import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
|
||||||
|
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8EncryptorBuilder;
|
||||||
|
import org.bouncycastle.operator.OperatorCreationException;
|
||||||
|
import org.bouncycastle.operator.OutputEncryptor;
|
||||||
|
import org.bouncycastle.util.io.pem.PemObject;
|
||||||
|
import org.bouncycastle.util.io.pem.PemWriter;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Assume;
|
import org.junit.Assume;
|
||||||
@ -44,9 +51,13 @@ import org.mockito.Mockito;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -207,7 +218,7 @@ public class CertServiceTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Test
|
@Test
|
||||||
/**
|
/**
|
||||||
* Given a Self-signed Certificate with encrypted key, upload should succeed
|
* Given a Self-signed Certificate with encrypted key, upload should succeed
|
||||||
*/
|
*/
|
||||||
@ -456,7 +467,7 @@ public class CertServiceTest {
|
|||||||
Assert.fail("Given an encrypted private key with a bad password. Upload should fail.");
|
Assert.fail("Given an encrypted private key with a bad password. Upload should fail.");
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
Assert.assertTrue("Did not expect message: " + e.getMessage(),
|
Assert.assertTrue("Did not expect message: " + e.getMessage(),
|
||||||
e.getMessage().contains("Parsing certificate/key failed: Invalid Key format."));
|
e.getMessage().contains("Parsing certificate/key failed: exception using cipher - please check password and data."));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -544,7 +555,7 @@ public class CertServiceTest {
|
|||||||
Assert.fail("Given a private key which has a different algorithm than the certificate, upload should fail");
|
Assert.fail("Given a private key which has a different algorithm than the certificate, upload should fail");
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
Assert.assertTrue("Did not expect message: " + e.getMessage(),
|
Assert.assertTrue("Did not expect message: " + e.getMessage(),
|
||||||
e.getMessage().contains("Parsing certificate/key failed: Invalid Key format."));
|
e.getMessage().contains("Public and private key have different algorithms"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -821,4 +832,283 @@ public class CertServiceTest {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String generateEncryptedPrivateKey(String password) throws NoSuchAlgorithmException, OperatorCreationException, IOException {
|
||||||
|
// Generate RSA key pair
|
||||||
|
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
|
||||||
|
kpg.initialize(2048);
|
||||||
|
KeyPair keyPair = kpg.generateKeyPair();
|
||||||
|
|
||||||
|
// Build encryptor (AES-256-CBC is FIPS-approved)
|
||||||
|
OutputEncryptor encryptor = new JceOpenSSLPKCS8EncryptorBuilder(PKCS8Generator.AES_256_CBC)
|
||||||
|
.setPassword(password.toCharArray())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Wrap the private key into PKCS#8 format and encrypt
|
||||||
|
JcaPKCS8Generator gen = new JcaPKCS8Generator(keyPair.getPrivate(), encryptor);
|
||||||
|
PemObject pemObject = gen.generate();
|
||||||
|
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
try (PemWriter pemWriter = new PemWriter(stringWriter)) {
|
||||||
|
pemWriter.writeObject(pemObject);
|
||||||
|
}
|
||||||
|
return stringWriter.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseEncryptedPrivateKey() throws Exception{
|
||||||
|
String password = "strongpassword";
|
||||||
|
String key = generateEncryptedPrivateKey(password);
|
||||||
|
final CertServiceImpl certService = new CertServiceImpl();
|
||||||
|
certService.parsePrivateKey(key, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validateCertAndChainsWithEncryptedKey() {
|
||||||
|
String password = "strongpassword";
|
||||||
|
String key = "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" +
|
||||||
|
"MIIFGzBVBgkqhkiG9w0BBQ0wSDAnBgkqhkiG9w0BBQwwGgQUiQiFcfHTx8EKYNHJ\n" +
|
||||||
|
"zOqT8/9AkaQCAggAMB0GCWCGSAFlAwQBKgQQKXBglXgHYSWK20BxSFUVLQSCBMBr\n" +
|
||||||
|
"ro2dXjsEoZfglccP5YWRPETSXntMdjAd39ftiWSXwQWZmht9/t+hSK+qZnGX/8VI\n" +
|
||||||
|
"0OR7x+8SBDqZAb9mYZzPPcUd/k+KLpQAFBSFrWVle40MY1OyZqEdQe3ELDERS919\n" +
|
||||||
|
"WRGmjTYUomL1zCAIrx27Woq5iiZkqsXmCcQwKRkCSNbTXjDe6gXtO9ePuMgvSiGg\n" +
|
||||||
|
"q2rhBZv82AYoc/IHzftsoS53Sda96RE93MK12+L48E5gxbqeHUJeGhn1hxxkqFcj\n" +
|
||||||
|
"cL/z817M6a9BEJkNlS4sZk3+Fg1RYBTx7CKYzR8WAf+LvasdO5ijPrNcqc6DzIIn\n" +
|
||||||
|
"tL0Kj/Gjp6rFP83IfezCtVdYi/dRLR9dNROJt7aIaeXnYdYF8o+vmWZm5H4bZeun\n" +
|
||||||
|
"czadKzd4EfvatHXi7Zq/cV/mh/NitUfnYMR5LUnX9pjNRkr2uqYx5AiO6aPQoR9G\n" +
|
||||||
|
"Gv1ubkUtug/rDoywwol7XGWxnDNbB4fvXRIGsyZYDh9J1CX+sv693ZeRx1J48vhT\n" +
|
||||||
|
"s+gZug8oG5DfSLCVaJDuIyHQGKuRLnh6LawUkyCA7Q/9vgmXnXo+0hJ5dYQw21fj\n" +
|
||||||
|
"M5yHrOt/tway5tJgDwuD778r3Y4w1H9Yt42J3tZL3gOIOyYhHad2M/emh5Khh/m/\n" +
|
||||||
|
"VK8eM86OQeo/zp+RddM4ckaUxKe/bFBqj9KvhzHsFTAuirT7be3+Ye1iBqKLvCgO\n" +
|
||||||
|
"yOTY14J1NbrvGmUs6yq3JxTkzl4+A23SPlHQE16j3UzCz0qnNTYLruUibL940uXu\n" +
|
||||||
|
"rnBcOuW6uM4yc+X3Aqo7xL3kzW/9waCd/VG/btJLNPKSDDRuuKQ7NEPjS+xajmqh\n" +
|
||||||
|
"WVMzzcMj4wVvTz6vnZNm9u9Yu/ACpzHTD+hVeFZhIscVCdT+LncEumLHHhrLQS3h\n" +
|
||||||
|
"9gVlv0MvSrWH6sl3oQEnA5ceEI4LfH6eT++IGAdKJTqkpAwSEtSEV+P/dETRNnsH\n" +
|
||||||
|
"TsKNEdylH++9Ljhkt68971cLGHf9yuzVU75BPFybngcNFZu3+YUDWY0fBwqwE0OI\n" +
|
||||||
|
"FXeqPhnN2UfAoeqCwz2KtPf2ig0a34S6Rxne9/XewlCsKEGSrdYG8mm4eJzsP69/\n" +
|
||||||
|
"5qw1MDO1nvt0B5jSly3vHcHGvgiDtG+vsfGqC1TA8eaTSq/UkUAKfoGg1DkL8olz\n" +
|
||||||
|
"b7jB24748Oh87Ksz12yeyY5T1edpoDcScCRLwIb0vNMKqIUe1aCEdTl08UHV3CbG\n" +
|
||||||
|
"7rnRLWE+9/Csij2fpkx0mEDeXdLxeSvkw5K8ha26s52MR4WhW0EUN74FJOMrTej3\n" +
|
||||||
|
"0jtcTC/bThc5jmQDaSQJbaiSIEKl8sdA0u8oTzBD2B1F9gkrZNZpE7hz670tysQs\n" +
|
||||||
|
"2Z0AxDcxQ7Qfkytg52MfJvLf0jxuNqjfbmQqkQsT+yUkjT6AmOgUMGP4zojP8ErY\n" +
|
||||||
|
"AvAqgurefHMS/HA8BUT7qxt300cTYaAONUlAJ/qAJ/YoHOI5yqWzBFJsr95NC13t\n" +
|
||||||
|
"rGqiOOLGtSIxk4WwdUX0u9TW8Hk6pWnl6MkyAn+a3RqKfrJ2tfKMjsO3iqu3Dlvz\n" +
|
||||||
|
"72RD5LsGcnhfKQ/TdswEA1EKdHBBjnDQOGdWNNTXnn41XoNNKneFjlFgJc8AXyoN\n" +
|
||||||
|
"fHvkc2aKb86WdpcANxK3\n" +
|
||||||
|
"-----END ENCRYPTED PRIVATE KEY-----";
|
||||||
|
String certificate = "-----BEGIN CERTIFICATE-----\n" +
|
||||||
|
"MIIERTCCAi0CFF2Ro8QjYcOCALfEqL1zs2T0cQzyMA0GCSqGSIb3DQEBCwUAMF4x\n" +
|
||||||
|
"CzAJBgNVBAYTAlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxEzARBgNVBAoM\n" +
|
||||||
|
"CkNsb3VkU3RhY2sxDzANBgNVBAsMBkFwYWNoZTEPMA0GA1UEAwwGQXBhY2hlMCAX\n" +
|
||||||
|
"DTI1MDYxNzA3MzQxOFoYDzIxMjUwNTI0MDczNDE4WjBeMQswCQYDVQQGEwJYWDEL\n" +
|
||||||
|
"MAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMRMwEQYDVQQKDApDbG91ZFN0YWNrMQ8w\n" +
|
||||||
|
"DQYDVQQLDAZBcGFjaGUxDzANBgNVBAMMBkFwYWNoZTCCASIwDQYJKoZIhvcNAQEB\n" +
|
||||||
|
"BQADggEPADCCAQoCggEBAMXpfAyO1m+YolspmNL64cMJ0mW4QiJUrrNxYyIaakfW\n" +
|
||||||
|
"/qs78hMlf8V82T94ayoMs2fpkjf69QsXTZoOZoUkaz58Wz9Z860OMAD/wguGz7EX\n" +
|
||||||
|
"Bk+OTEDhXP9NAkY99TqscWS3bm6XSu3w0cOwjwLtV72VsT2UA1d0hpVI4kVTbI56\n" +
|
||||||
|
"RZ1ymboyu/mhp2dqZu+Ewh8n7PMYvDO6hGuqsM5We2WLdSCmPZKtmbQ8CRj0fwJI\n" +
|
||||||
|
"CZZEafFEBwLhW3F15SRZLxQApzqMTlmbk9edEgOfJZqMrr+F8jguce7Qry6FcbkU\n" +
|
||||||
|
"6x4oRyykuz5pi5mPjaTxQyY4NWsCHojlQ0kz0VeBUX0CAwEAATANBgkqhkiG9w0B\n" +
|
||||||
|
"AQsFAAOCAgEAJAUldK70IoyA0jokXfAyLXNRX43/UfmQMu3xvVYI9OPk8f6CrBIm\n" +
|
||||||
|
"g79cA3pGPNxyIReqFxDk+wXW+/iPCgOwv+YYODPEMZi1Cc8WQJ4OGzovD5hep7TA\n" +
|
||||||
|
"pg6jo16LdKpOQM6C9XUce3vZf6t487PCgg8SzldqhMMC97Kw+DAxYg+JRd28jfIB\n" +
|
||||||
|
"RAtpOCzqKqWp7lQ1YwS9M/VI0mYtmiuQbaz1to4qBPcCbR1GsLsmqMmTUkbYYyFF\n" +
|
||||||
|
"fgvInITyW+0NV/UwgiNFxU+k9T2H1lfvqj6hVRwwj7i84xAu4Y/N9zP/UKXxU93N\n" +
|
||||||
|
"ogoHabfGcsFEygyTkFuI4XG/Ppc3c8CJV2NbVQixe5Wdt1Yc9qMkbq+OdGvsOhbt\n" +
|
||||||
|
"T2+Qz5JZ7w0LsYONzuCRbaDpJiAg2MiALe3L1RzEya57/PylgUeH6gMbPyuQ2EyL\n" +
|
||||||
|
"pTUQ1imV3tTlkxjy7niu/IeqgcQOA2cx8Fwok+ECLvxc47noUlgPcROz5i43+IYA\n" +
|
||||||
|
"frvGqDfZCeKXKuAi//8wBl2tptMMmLpkS4mW/8Pijcx3JuxC6ySeOFAVgPjq4krw\n" +
|
||||||
|
"dGl+IBNwKNcsUu5/3uj/2h85w56Ys8uxeLkLqEq+9yHlwxexGJG0qJ2QcXFnOxCC\n" +
|
||||||
|
"qz+L2k3m0+Yu5zUFsMCTgEwQeR6CUfW9/GtPunZtvwHOSbVus0DvnSE=\n" +
|
||||||
|
"-----END CERTIFICATE-----";
|
||||||
|
String certChains = "-----BEGIN CERTIFICATE-----\n" +
|
||||||
|
"MIIFQzCCAysCFEVQffqr0ScjpyZ6pmDsOOu71t70MA0GCSqGSIb3DQEBCwUAMF4x\n" +
|
||||||
|
"CzAJBgNVBAYTAlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxEzARBgNVBAoM\n" +
|
||||||
|
"CkNsb3VkU3RhY2sxDzANBgNVBAsMBkFwYWNoZTEPMA0GA1UEAwwGQXBhY2hlMB4X\n" +
|
||||||
|
"DTI1MDYxNjEwMjc1NloXDTMwMDYxNTEwMjc1NlowXjELMAkGA1UEBhMCWFgxCzAJ\n" +
|
||||||
|
"BgNVBAgMAlhYMQswCQYDVQQHDAJYWDETMBEGA1UECgwKQ2xvdWRTdGFjazEPMA0G\n" +
|
||||||
|
"A1UECwwGQXBhY2hlMQ8wDQYDVQQDDAZBcGFjaGUwggIiMA0GCSqGSIb3DQEBAQUA\n" +
|
||||||
|
"A4ICDwAwggIKAoICAQCLiQmSjrht15R1F+r79m/LZN5hsfQBGp+dy+yrtsWfOOur\n" +
|
||||||
|
"RdXAwgbLxxsyKMQKWCQxlRI7wdhqh0L0ZBrIr9MjltYqsqLAoLmgY4eG/f6G8YGr\n" +
|
||||||
|
"O/rxzfwTLbCeaIseF/OMA6Sz125HXYp1bltYK4LsuC7tihZXbeVa5pUGs3Jwgcfx\n" +
|
||||||
|
"LYm4eB42Hp7Eg05uL8LbwT/1AjcwoWkTewKAWXA83zgLRDFDbl1t0IPHI4cdVvia\n" +
|
||||||
|
"BNwNbG49ZCF6OgmokSarQSe4Vbems1u9T9pAySXAVjEYBqFjKWyswpdr782uNLmB\n" +
|
||||||
|
"lCGm0pDeJ9/WASxbTJr7k9H6ZpnaHr54DG6ZqennWMz8w6r2pf7bp/EGZ3mZQ4s3\n" +
|
||||||
|
"5ylSP4cQt8CSSI8k2CflPGUyytUAiWlDS3qSyIuAOPKXDg7wIpcbwcu4VMeKnH0Z\n" +
|
||||||
|
"x7Uu9j1UDZEZoSu6UI/VInTl47k1/ECD+AO9yBzZSv+pTQmO3/Im3CcxsTHmVd5s\n" +
|
||||||
|
"Tl0CJ/jWNpo9DAMtmGvt6CBWBXGRsO2XNk7djRcq2CubiCpvODg+7CcR6CiZK73L\n" +
|
||||||
|
"1aOisLiq3+ofiJSSXRRuKtJlkQ4eSPSbYWkNJcKmIhbCoYOdH/Pe3/+RHjvNc1kO\n" +
|
||||||
|
"OUb+icmfzcMVAs3C5jybpazsfjDNQZXWAFx4FLDcqOVbrCwom+tMukw+hzlZnwID\n" +
|
||||||
|
"AQABMA0GCSqGSIb3DQEBCwUAA4ICAQAdexoMwn+Ol1A3+NOHk9WJZX+t2Q8/9wWb\n" +
|
||||||
|
"K+jSVleSfXXWsB1mC3fABVJQdCxtXCH3rpt4w7FK6aUes9VjqAHap4tt9WBq0Wqq\n" +
|
||||||
|
"vvMURFHfllxEM31Z35kBOCSQY9xwpGV1rRh/zYs4h55YixomR3nXNZ9cI8xzlSCi\n" +
|
||||||
|
"sMG0mv0y+yxPohKrZj3AzLYz/M11SimSoyRPIANI+cUg1nJXyQoHzVHWEp1Nj0HB\n" +
|
||||||
|
"M/GW05cxsWea7f5YcAW1JQI3FOkpwb72fIZOtMDa4PO8IYWXJAeAc/chw745/MTi\n" +
|
||||||
|
"Rvl2NT4RZBAcrSNbhCOzRPG/ZiG+ArQuCluZ9HHAXRBMTtlLk5DO4+XxZlyGpjwf\n" +
|
||||||
|
"uKniK8dccy9uU0ho73p9SNDhXH0yb9Naj8vd9NWzCUYaaBXt/92cIyhaAHAVFxJu\n" +
|
||||||
|
"o6jr2FLbnhSGF9EO/tHvF7LxZv1dnbInvlWHwoFQjwmoeB+e17lHBdPMnWnPKBZe\n" +
|
||||||
|
"jA2VH/IzGCucWuWQhruummO5GT8Z6F4jBwvafBo+QARKPZgEBpx3LycXrpkYI3LT\n" +
|
||||||
|
"GGOpGCxFt5tVZOEsC/jQ5rIljNSeTzWmzfNRn/yRUW97uWsrzcQIBAUtu/pQnyFQ\n" +
|
||||||
|
"WCnC1ipCp1zhJsXAFUKuqEfLngXodOvC4tAOr76h11S57o5lN4506Poq2mWgAZe/\n" +
|
||||||
|
"JZr9MEn1+w==\n" +
|
||||||
|
"-----END CERTIFICATE-----\n" +
|
||||||
|
"-----BEGIN CERTIFICATE-----\n" +
|
||||||
|
"MIIFnzCCA4egAwIBAgIUcUNMqgWoDLsvMj0YmEudj60EG5swDQYJKoZIhvcNAQEL\n" +
|
||||||
|
"BQAwXjELMAkGA1UEBhMCWFgxCzAJBgNVBAgMAlhYMQswCQYDVQQHDAJYWDETMBEG\n" +
|
||||||
|
"A1UECgwKQ2xvdWRTdGFjazEPMA0GA1UECwwGQXBhY2hlMQ8wDQYDVQQDDAZBcGFj\n" +
|
||||||
|
"aGUwIBcNMjUwNjE2MTAyNzM2WhgPMjEyNTA1MjMxMDI3MzZaMF4xCzAJBgNVBAYT\n" +
|
||||||
|
"AlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxEzARBgNVBAoMCkNsb3VkU3Rh\n" +
|
||||||
|
"Y2sxDzANBgNVBAsMBkFwYWNoZTEPMA0GA1UEAwwGQXBhY2hlMIICIjANBgkqhkiG\n" +
|
||||||
|
"9w0BAQEFAAOCAg8AMIICCgKCAgEAwVQaePulUM523gKw168ToYp+gt05bXbu4Gg8\n" +
|
||||||
|
"uaRDKhnRAX1sEgYwkQ36Q+iTDEM9sKRma8lMNMIqkZMQdk6sIGX6BL+6wUOb7mL0\n" +
|
||||||
|
"5+I0yO9i8ooaGgNaeNvZftNIRlLsnPMGJaeom2/66XV4CsMqoZKaJ1H/I8N+bAeD\n" +
|
||||||
|
"GvrBx+B4l9D3G390nQvot9JUzrJgGuLl0KDHapvhlR39cCgEfIii02uX1iy0qXlV\n" +
|
||||||
|
"b+G1kLvpeC7T+lsJxondPJ69aO3lbDv/izyWw7qqBC57UhT/oKDxJmjQqklqzhgt\n" +
|
||||||
|
"nM/p3YE7M0nkRi3LnRmsZBz7o1DRf+M29zypKzXVk1aJflL46AtLMmpDIzVrEB2M\n" +
|
||||||
|
"q7o47rstXusYRYsBCqGTgdI1fV/CkDsZY5XkPZh2dsjZCHIS4P03OqFGsc6PQha2\n" +
|
||||||
|
"+y2AhV1pvywkDl48kPKSukHfV1RtaPZUZtcQKztwHH+aFfo9mD8z0H2HcExdXKzd\n" +
|
||||||
|
"jhRhI9ZSwFj3HEN9f5P8fS3lf5+fV7EEbG4NisieBj/UivW6QiTHpLD7wRLIUt2g\n" +
|
||||||
|
"XgXNF0lfJzYHbIcxQ6kfC5McU2fu6mUC+p/pNN8G0POS3S2T55tEUqLL4N0SadQy\n" +
|
||||||
|
"N1TZlTd2xTn+Hb6WlG0f5m97xGcNlGHKBvntFrHvOIfkEQ9ne3MlOO1Gjlintowo\n" +
|
||||||
|
"fRGf15kCAwEAAaNTMFEwHQYDVR0OBBYEFM4WEQJpN9M07Q8CHq+5owG93Dj8MB8G\n" +
|
||||||
|
"A1UdIwQYMBaAFM4WEQJpN9M07Q8CHq+5owG93Dj8MA8GA1UdEwEB/wQFMAMBAf8w\n" +
|
||||||
|
"DQYJKoZIhvcNAQELBQADggIBABr5RKGc/rKx4fOgyXNJR4aCJCTtPZKs4AUCCBYz\n" +
|
||||||
|
"cWOjJYGNvThOPSVx51nAD8+YP2BwkOIPfhl2u/+vQSSbz4OlauXLki2DUN8E2OFe\n" +
|
||||||
|
"gJfxPDzWOfAo/QOJcyHwSlnIQjiZzG2lK3eyf5IFnfQILQzDvXaUYvMBkl2hb5Q7\n" +
|
||||||
|
"44H6tRw78uuf/KsT4rY0bBFMN5DayjiyvoIDUvzCRqcb2KOi9DnZ7pXjduL7tO0j\n" +
|
||||||
|
"PhlQ24B77LVUUAvydIGUzmbhGC2VvY1qE7uaYgYtgSUZ0zSjJrHjUjVLMzRouNP7\n" +
|
||||||
|
"jpbBQRAcP4FDcOFZBHogunA0hxQdm0d8u3LqDYPNS0rpfW0ddU/72nfBX4bnoDEN\n" +
|
||||||
|
"+anw4wOgFuUcoEThALWZ9ESVKxXQ9Fpvd6FRW8fLLqhXAuli1BqP1c1WRxagldYe\n" +
|
||||||
|
"nPGm/FGZyJ2xOak9Uigi9NAQ/vX6CEfgcJgFZmCo8EKH0d4Ut72vGUcPqiUhT2EI\n" +
|
||||||
|
"AFAd6drSyoUdXXniSMWky9Vrt+qtLuAD1nhHTv8ZPdItXokoiD6ea/4xrbUZn0qY\n" +
|
||||||
|
"lLMDyfY76UVF0ruTR2Q6IdSq/zSggdwgkTooOW4XZcRf5l/ZnoeVQ1QH9C85SIKH\n" +
|
||||||
|
"IKZwPeGUm+EntmpuCBDmQSHLRCGEThd64iOAjqLR6arLj4TBJzBrZsGHFJbm0OcI\n" +
|
||||||
|
"dwa9\n" +
|
||||||
|
"-----END CERTIFICATE-----";
|
||||||
|
final CertServiceImpl certService = new CertServiceImpl();
|
||||||
|
certService.validate(certificate, key, password, certChains, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validateCertAndChainsWithUnencryptedKey() {
|
||||||
|
String key = "-----BEGIN PRIVATE KEY-----\n" +
|
||||||
|
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCph7jsoMCQirRn\n" +
|
||||||
|
"3obuvgnnefTXRQYd9tF9k2aCVkTiiisvC39px7MGdgvDXADhD9fmR7oyXVQlfNu0\n" +
|
||||||
|
"rXjjgsVT3r4bv+DVi81YGXnuU7h10yCOZJt21i6QGHN1CS0/TAfg0UhlACCEYNRx\n" +
|
||||||
|
"kB0klwUcj/jk/AKil1DoUGpvAm2gZsek/njb76/AeIfxc+Es4ZOPCVqQOHp6gI0q\n" +
|
||||||
|
"t6KDMkUwv8fyzrpScygMUPVYrLmm6D0pn8yd3ihW07wGxMjND6UgOnao8t6H3LaM\n" +
|
||||||
|
"Pe7eqSFzxunF9NFFjnUrKcHZZSledDM/37Kbqb/8T5f+4SwjioS1OdPCh8ApdiXq\n" +
|
||||||
|
"HNUwYkALAgMBAAECggEAK5JiiQ7X7053B6s96uaVDRVfRGTNKa5iMXBNDHq3wbHZ\n" +
|
||||||
|
"X4IJAVr+PE7ivxdKco3r45fT11X9ZpUsssdTJsZZiTDak69BTiFcaaRCnmqOIlpd\n" +
|
||||||
|
"J7vb6TMrTIW8RvxQ0M/txm6DuNHLibqJX5a2pszZ13l5cwECfF9/v/XLJTTukCbu\n" +
|
||||||
|
"6D/f3fBVFl1tM8y9saOEYLkdb4dILWY61bVSDNswgprz2EV1SFnk5jxz2FuBrM/Q\n" +
|
||||||
|
"+7hINvjDcaRvcm59hRb1rkljv7S10VoNw/CFkU451csJkUe4vWZwB8lZK/XxLQG0\n" +
|
||||||
|
"HEdS1zU1XY8H8Y1RCrxjGRyiiWsBtUThhWYlPrGCoQKBgQDkP09YAlKqXhT69Kx5\n" +
|
||||||
|
"keg2i1jV2hA73zWbWXt9xp5jG5r3pl3m170DvKL93YIDnHtpTC56mlzGrzS7DSTN\n" +
|
||||||
|
"p0buY9Qb3fkJxunCpPVFo0HMFkpeR77ax0v34NzSohlRLKFo5R2M1cmDfbVbnSSl\n" +
|
||||||
|
"MB57FfRRMxzjrk+dJvjOeJsxjwKBgQC+JLb4B8CZjpurXYg3ySiRqFsCqkqob+kf\n" +
|
||||||
|
"9dR+rWvcR6vMTEyha0hUlDvTikDepU2smYR4oPHfdcXF9lAJ7T02UmQDeizAqR68\n" +
|
||||||
|
"u9e+yS0q3tdRnPPZmXJfaDCXG1hKMqF4YA5Vs0XAjleF3zHB+vBLrnlPpShtd/Mu\n" +
|
||||||
|
"sWTpxICTxQKBgQDSr/n+pE5IQwYczOO0aFGwn5pF9L9NdPHXz5aleETV+TJn7WL6\n" +
|
||||||
|
"ZiRsoaDWs7SCvtxQS2kP9RM0t5/2FeDmEMXx4aZ2fsSWGM3IxVo+iL+Aswa81n8/\n" +
|
||||||
|
"Ff5y9lb/+29hNdBcsjk/ukwEG3Lf+UNNVAie15oppgPByzJkPwgmFsAy0wKBgHDX\n" +
|
||||||
|
"/TZp82WuerhSw/rHiSoYjhqg0bnw4Ju1Gy0q4q5SYqTWS0wpDT4U0wSSMjlwRQ6/\n" +
|
||||||
|
"9RxZ9/G0RXFc4tdhUkig0PY3VcPpGnLL0BhL8GBW69ZlnVpwdK4meV/UPKucLLPx\n" +
|
||||||
|
"3dACmszSLSMn+LG0qVNg8mHQFJQS8eGuKcOKePw5AoGACuxtefROKdKOALh4lTi2\n" +
|
||||||
|
"VOwPZ+1jxsm6lKNccIEvbUpe3UXPgNWpJiDX8mUcob4/NBLzmV3BUVKbG7Exbo5J\n" +
|
||||||
|
"LoMfp7OsztWUFwt7YAvRfS8fHdhkEsxEf3T72ADieH5ZAuXFF+K0H3r6HtWPD4ws\n" +
|
||||||
|
"mTJjGP4+Bl/dFakA5FJcjHg=\n" +
|
||||||
|
"-----END PRIVATE KEY-----";
|
||||||
|
String certificate = "-----BEGIN CERTIFICATE-----\n" +
|
||||||
|
"MIIERTCCAi0CFF2Ro8QjYcOCALfEqL1zs2T0cQzzMA0GCSqGSIb3DQEBCwUAMF4x\n" +
|
||||||
|
"CzAJBgNVBAYTAlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxEzARBgNVBAoM\n" +
|
||||||
|
"CkNsb3VkU3RhY2sxDzANBgNVBAsMBkFwYWNoZTEPMA0GA1UEAwwGQXBhY2hlMCAX\n" +
|
||||||
|
"DTI1MDYxNzA5MTE0N1oYDzIxMjUwNTI0MDkxMTQ3WjBeMQswCQYDVQQGEwJYWDEL\n" +
|
||||||
|
"MAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMRMwEQYDVQQKDApDbG91ZFN0YWNrMQ8w\n" +
|
||||||
|
"DQYDVQQLDAZBcGFjaGUxDzANBgNVBAMMBkFwYWNoZTCCASIwDQYJKoZIhvcNAQEB\n" +
|
||||||
|
"BQADggEPADCCAQoCggEBAKmHuOygwJCKtGfehu6+Ced59NdFBh320X2TZoJWROKK\n" +
|
||||||
|
"Ky8Lf2nHswZ2C8NcAOEP1+ZHujJdVCV827SteOOCxVPevhu/4NWLzVgZee5TuHXT\n" +
|
||||||
|
"II5km3bWLpAYc3UJLT9MB+DRSGUAIIRg1HGQHSSXBRyP+OT8AqKXUOhQam8CbaBm\n" +
|
||||||
|
"x6T+eNvvr8B4h/Fz4Szhk48JWpA4enqAjSq3ooMyRTC/x/LOulJzKAxQ9Visuabo\n" +
|
||||||
|
"PSmfzJ3eKFbTvAbEyM0PpSA6dqjy3ofctow97t6pIXPG6cX00UWOdSspwdllKV50\n" +
|
||||||
|
"Mz/fspupv/xPl/7hLCOKhLU508KHwCl2Jeoc1TBiQAsCAwEAATANBgkqhkiG9w0B\n" +
|
||||||
|
"AQsFAAOCAgEAOKaT7cp1P/B67cT0pQ+ZO7dazoomvwbznpUDPlX+h2f9pPYvBoOJ\n" +
|
||||||
|
"qul0Np3zft3sR4M1uxRNuayhd+oFMNx0J3CJVxc6fpUvc0IvNAgy0C6IeAlTTH6V\n" +
|
||||||
|
"Tiy8X5YeD1SAg0wJkqZQzXC+8Ao+LPacdhnz7wUSV1j4ILlVZcfvISaaZUFidERT\n" +
|
||||||
|
"nP18syUWSodTULXTKB8M8z/9t6KFWXJDJGXLKBMoX3DCSx9QG5GDMuyu9XWf3bBH\n" +
|
||||||
|
"ZHZse02mh0x83hV34Bpa1Yr98PsGvQm7GUXiLenFO57wzWaInxBkS6sF4OWreiMI\n" +
|
||||||
|
"lN94CtBXtMxtC5C50WthNGBJHg3dXKeF3O6F8z8EkkqpKyJtJ3IoAXTHGEh5fxp0\n" +
|
||||||
|
"tsbOEqJ540XbtD82UWYA4bVY1h0Tb1SaV7fylZkuYXZ+rl6G0S7roPVYbrjRsP9t\n" +
|
||||||
|
"FCGko35WkhkI0OpNoTremH+H1U/nBowMm6tSfZ0ZWa/4NnLacXhPjDJkEhu7RlA4\n" +
|
||||||
|
"JYeYKe4dj4hLdcHCUFuP8Tdv1P20SGQQOaHUXYbHP5Er3EHZxzI13JwHiO+FKuYP\n" +
|
||||||
|
"igIqbCdBd8smTzdbit0f6OfKOyNXDDxN+E1VKAHSquYuxMcj+njKTQ1ihpXnTLpo\n" +
|
||||||
|
"ZP3NoLZ6gAQIjEgHHsLeZ24HCbiFfUpwWSPNNcr6X5qQelt5leNGsIU=\n" +
|
||||||
|
"-----END CERTIFICATE-----";
|
||||||
|
String certChains = "-----BEGIN CERTIFICATE-----\n" +
|
||||||
|
"MIIFQzCCAysCFEVQffqr0ScjpyZ6pmDsOOu71t70MA0GCSqGSIb3DQEBCwUAMF4x\n" +
|
||||||
|
"CzAJBgNVBAYTAlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxEzARBgNVBAoM\n" +
|
||||||
|
"CkNsb3VkU3RhY2sxDzANBgNVBAsMBkFwYWNoZTEPMA0GA1UEAwwGQXBhY2hlMB4X\n" +
|
||||||
|
"DTI1MDYxNjEwMjc1NloXDTMwMDYxNTEwMjc1NlowXjELMAkGA1UEBhMCWFgxCzAJ\n" +
|
||||||
|
"BgNVBAgMAlhYMQswCQYDVQQHDAJYWDETMBEGA1UECgwKQ2xvdWRTdGFjazEPMA0G\n" +
|
||||||
|
"A1UECwwGQXBhY2hlMQ8wDQYDVQQDDAZBcGFjaGUwggIiMA0GCSqGSIb3DQEBAQUA\n" +
|
||||||
|
"A4ICDwAwggIKAoICAQCLiQmSjrht15R1F+r79m/LZN5hsfQBGp+dy+yrtsWfOOur\n" +
|
||||||
|
"RdXAwgbLxxsyKMQKWCQxlRI7wdhqh0L0ZBrIr9MjltYqsqLAoLmgY4eG/f6G8YGr\n" +
|
||||||
|
"O/rxzfwTLbCeaIseF/OMA6Sz125HXYp1bltYK4LsuC7tihZXbeVa5pUGs3Jwgcfx\n" +
|
||||||
|
"LYm4eB42Hp7Eg05uL8LbwT/1AjcwoWkTewKAWXA83zgLRDFDbl1t0IPHI4cdVvia\n" +
|
||||||
|
"BNwNbG49ZCF6OgmokSarQSe4Vbems1u9T9pAySXAVjEYBqFjKWyswpdr782uNLmB\n" +
|
||||||
|
"lCGm0pDeJ9/WASxbTJr7k9H6ZpnaHr54DG6ZqennWMz8w6r2pf7bp/EGZ3mZQ4s3\n" +
|
||||||
|
"5ylSP4cQt8CSSI8k2CflPGUyytUAiWlDS3qSyIuAOPKXDg7wIpcbwcu4VMeKnH0Z\n" +
|
||||||
|
"x7Uu9j1UDZEZoSu6UI/VInTl47k1/ECD+AO9yBzZSv+pTQmO3/Im3CcxsTHmVd5s\n" +
|
||||||
|
"Tl0CJ/jWNpo9DAMtmGvt6CBWBXGRsO2XNk7djRcq2CubiCpvODg+7CcR6CiZK73L\n" +
|
||||||
|
"1aOisLiq3+ofiJSSXRRuKtJlkQ4eSPSbYWkNJcKmIhbCoYOdH/Pe3/+RHjvNc1kO\n" +
|
||||||
|
"OUb+icmfzcMVAs3C5jybpazsfjDNQZXWAFx4FLDcqOVbrCwom+tMukw+hzlZnwID\n" +
|
||||||
|
"AQABMA0GCSqGSIb3DQEBCwUAA4ICAQAdexoMwn+Ol1A3+NOHk9WJZX+t2Q8/9wWb\n" +
|
||||||
|
"K+jSVleSfXXWsB1mC3fABVJQdCxtXCH3rpt4w7FK6aUes9VjqAHap4tt9WBq0Wqq\n" +
|
||||||
|
"vvMURFHfllxEM31Z35kBOCSQY9xwpGV1rRh/zYs4h55YixomR3nXNZ9cI8xzlSCi\n" +
|
||||||
|
"sMG0mv0y+yxPohKrZj3AzLYz/M11SimSoyRPIANI+cUg1nJXyQoHzVHWEp1Nj0HB\n" +
|
||||||
|
"M/GW05cxsWea7f5YcAW1JQI3FOkpwb72fIZOtMDa4PO8IYWXJAeAc/chw745/MTi\n" +
|
||||||
|
"Rvl2NT4RZBAcrSNbhCOzRPG/ZiG+ArQuCluZ9HHAXRBMTtlLk5DO4+XxZlyGpjwf\n" +
|
||||||
|
"uKniK8dccy9uU0ho73p9SNDhXH0yb9Naj8vd9NWzCUYaaBXt/92cIyhaAHAVFxJu\n" +
|
||||||
|
"o6jr2FLbnhSGF9EO/tHvF7LxZv1dnbInvlWHwoFQjwmoeB+e17lHBdPMnWnPKBZe\n" +
|
||||||
|
"jA2VH/IzGCucWuWQhruummO5GT8Z6F4jBwvafBo+QARKPZgEBpx3LycXrpkYI3LT\n" +
|
||||||
|
"GGOpGCxFt5tVZOEsC/jQ5rIljNSeTzWmzfNRn/yRUW97uWsrzcQIBAUtu/pQnyFQ\n" +
|
||||||
|
"WCnC1ipCp1zhJsXAFUKuqEfLngXodOvC4tAOr76h11S57o5lN4506Poq2mWgAZe/\n" +
|
||||||
|
"JZr9MEn1+w==\n" +
|
||||||
|
"-----END CERTIFICATE-----\n" +
|
||||||
|
"-----BEGIN CERTIFICATE-----\n" +
|
||||||
|
"MIIFnzCCA4egAwIBAgIUcUNMqgWoDLsvMj0YmEudj60EG5swDQYJKoZIhvcNAQEL\n" +
|
||||||
|
"BQAwXjELMAkGA1UEBhMCWFgxCzAJBgNVBAgMAlhYMQswCQYDVQQHDAJYWDETMBEG\n" +
|
||||||
|
"A1UECgwKQ2xvdWRTdGFjazEPMA0GA1UECwwGQXBhY2hlMQ8wDQYDVQQDDAZBcGFj\n" +
|
||||||
|
"aGUwIBcNMjUwNjE2MTAyNzM2WhgPMjEyNTA1MjMxMDI3MzZaMF4xCzAJBgNVBAYT\n" +
|
||||||
|
"AlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxEzARBgNVBAoMCkNsb3VkU3Rh\n" +
|
||||||
|
"Y2sxDzANBgNVBAsMBkFwYWNoZTEPMA0GA1UEAwwGQXBhY2hlMIICIjANBgkqhkiG\n" +
|
||||||
|
"9w0BAQEFAAOCAg8AMIICCgKCAgEAwVQaePulUM523gKw168ToYp+gt05bXbu4Gg8\n" +
|
||||||
|
"uaRDKhnRAX1sEgYwkQ36Q+iTDEM9sKRma8lMNMIqkZMQdk6sIGX6BL+6wUOb7mL0\n" +
|
||||||
|
"5+I0yO9i8ooaGgNaeNvZftNIRlLsnPMGJaeom2/66XV4CsMqoZKaJ1H/I8N+bAeD\n" +
|
||||||
|
"GvrBx+B4l9D3G390nQvot9JUzrJgGuLl0KDHapvhlR39cCgEfIii02uX1iy0qXlV\n" +
|
||||||
|
"b+G1kLvpeC7T+lsJxondPJ69aO3lbDv/izyWw7qqBC57UhT/oKDxJmjQqklqzhgt\n" +
|
||||||
|
"nM/p3YE7M0nkRi3LnRmsZBz7o1DRf+M29zypKzXVk1aJflL46AtLMmpDIzVrEB2M\n" +
|
||||||
|
"q7o47rstXusYRYsBCqGTgdI1fV/CkDsZY5XkPZh2dsjZCHIS4P03OqFGsc6PQha2\n" +
|
||||||
|
"+y2AhV1pvywkDl48kPKSukHfV1RtaPZUZtcQKztwHH+aFfo9mD8z0H2HcExdXKzd\n" +
|
||||||
|
"jhRhI9ZSwFj3HEN9f5P8fS3lf5+fV7EEbG4NisieBj/UivW6QiTHpLD7wRLIUt2g\n" +
|
||||||
|
"XgXNF0lfJzYHbIcxQ6kfC5McU2fu6mUC+p/pNN8G0POS3S2T55tEUqLL4N0SadQy\n" +
|
||||||
|
"N1TZlTd2xTn+Hb6WlG0f5m97xGcNlGHKBvntFrHvOIfkEQ9ne3MlOO1Gjlintowo\n" +
|
||||||
|
"fRGf15kCAwEAAaNTMFEwHQYDVR0OBBYEFM4WEQJpN9M07Q8CHq+5owG93Dj8MB8G\n" +
|
||||||
|
"A1UdIwQYMBaAFM4WEQJpN9M07Q8CHq+5owG93Dj8MA8GA1UdEwEB/wQFMAMBAf8w\n" +
|
||||||
|
"DQYJKoZIhvcNAQELBQADggIBABr5RKGc/rKx4fOgyXNJR4aCJCTtPZKs4AUCCBYz\n" +
|
||||||
|
"cWOjJYGNvThOPSVx51nAD8+YP2BwkOIPfhl2u/+vQSSbz4OlauXLki2DUN8E2OFe\n" +
|
||||||
|
"gJfxPDzWOfAo/QOJcyHwSlnIQjiZzG2lK3eyf5IFnfQILQzDvXaUYvMBkl2hb5Q7\n" +
|
||||||
|
"44H6tRw78uuf/KsT4rY0bBFMN5DayjiyvoIDUvzCRqcb2KOi9DnZ7pXjduL7tO0j\n" +
|
||||||
|
"PhlQ24B77LVUUAvydIGUzmbhGC2VvY1qE7uaYgYtgSUZ0zSjJrHjUjVLMzRouNP7\n" +
|
||||||
|
"jpbBQRAcP4FDcOFZBHogunA0hxQdm0d8u3LqDYPNS0rpfW0ddU/72nfBX4bnoDEN\n" +
|
||||||
|
"+anw4wOgFuUcoEThALWZ9ESVKxXQ9Fpvd6FRW8fLLqhXAuli1BqP1c1WRxagldYe\n" +
|
||||||
|
"nPGm/FGZyJ2xOak9Uigi9NAQ/vX6CEfgcJgFZmCo8EKH0d4Ut72vGUcPqiUhT2EI\n" +
|
||||||
|
"AFAd6drSyoUdXXniSMWky9Vrt+qtLuAD1nhHTv8ZPdItXokoiD6ea/4xrbUZn0qY\n" +
|
||||||
|
"lLMDyfY76UVF0ruTR2Q6IdSq/zSggdwgkTooOW4XZcRf5l/ZnoeVQ1QH9C85SIKH\n" +
|
||||||
|
"IKZwPeGUm+EntmpuCBDmQSHLRCGEThd64iOAjqLR6arLj4TBJzBrZsGHFJbm0OcI\n" +
|
||||||
|
"dwa9\n" +
|
||||||
|
"-----END CERTIFICATE-----";
|
||||||
|
final CertServiceImpl certService = new CertServiceImpl();
|
||||||
|
certService.validate(certificate, key, null, certChains, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
import logging
|
import logging
|
||||||
import os.path
|
import os.path
|
||||||
|
from os import listdir
|
||||||
import re
|
import re
|
||||||
from cs.CsDatabag import CsDataBag
|
from cs.CsDatabag import CsDataBag
|
||||||
from .CsProcess import CsProcess
|
from .CsProcess import CsProcess
|
||||||
@ -25,6 +26,7 @@ from . import CsHelper
|
|||||||
HAPROXY_CONF_T = "/etc/haproxy/haproxy.cfg.new"
|
HAPROXY_CONF_T = "/etc/haproxy/haproxy.cfg.new"
|
||||||
HAPROXY_CONF_P = "/etc/haproxy/haproxy.cfg"
|
HAPROXY_CONF_P = "/etc/haproxy/haproxy.cfg"
|
||||||
|
|
||||||
|
SSL_CERTS_DIR = "/etc/cloudstack/ssl/"
|
||||||
|
|
||||||
class CsLoadBalancer(CsDataBag):
|
class CsLoadBalancer(CsDataBag):
|
||||||
""" Manage Load Balancer entries """
|
""" Manage Load Balancer entries """
|
||||||
@ -34,6 +36,9 @@ class CsLoadBalancer(CsDataBag):
|
|||||||
return
|
return
|
||||||
if 'configuration' not in list(self.dbag['config'][0].keys()):
|
if 'configuration' not in list(self.dbag['config'][0].keys()):
|
||||||
return
|
return
|
||||||
|
if 'ssl_certs' in list(self.dbag['config'][0].keys()):
|
||||||
|
self._create_pem_for_sslcert(self.dbag['config'][0]['ssl_certs'])
|
||||||
|
|
||||||
config = self.dbag['config'][0]['configuration']
|
config = self.dbag['config'][0]['configuration']
|
||||||
file1 = CsFile(HAPROXY_CONF_T)
|
file1 = CsFile(HAPROXY_CONF_T)
|
||||||
file1.empty()
|
file1.empty()
|
||||||
@ -43,6 +48,11 @@ class CsLoadBalancer(CsDataBag):
|
|||||||
file1.commit()
|
file1.commit()
|
||||||
file2 = CsFile(HAPROXY_CONF_P)
|
file2 = CsFile(HAPROXY_CONF_P)
|
||||||
if not file2.compare(file1):
|
if not file2.compare(file1):
|
||||||
|
# Verify new haproxy config before haproxy restart/reload
|
||||||
|
haproxy_err = self._verify_haproxy_config(HAPROXY_CONF_T)
|
||||||
|
if haproxy_err:
|
||||||
|
raise Exception("haproxy config is invalid with error \n%s" % haproxy_err)
|
||||||
|
|
||||||
CsHelper.copy(HAPROXY_CONF_T, HAPROXY_CONF_P)
|
CsHelper.copy(HAPROXY_CONF_T, HAPROXY_CONF_P)
|
||||||
|
|
||||||
proc = CsProcess(['/run/haproxy.pid'])
|
proc = CsProcess(['/run/haproxy.pid'])
|
||||||
@ -82,3 +92,29 @@ class CsLoadBalancer(CsDataBag):
|
|||||||
ip = path[0]
|
ip = path[0]
|
||||||
port = path[1]
|
port = path[1]
|
||||||
firewall.append(["filter", "", "-A INPUT -p tcp -m tcp -d %s --dport %s -m state --state NEW -j ACCEPT" % (ip, port)])
|
firewall.append(["filter", "", "-A INPUT -p tcp -m tcp -d %s --dport %s -m state --state NEW -j ACCEPT" % (ip, port)])
|
||||||
|
|
||||||
|
def _create_pem_for_sslcert(self, ssl_certs):
|
||||||
|
logging.debug("CsLoadBalancer:: creating new pem files in %s and cleaning up it" % SSL_CERTS_DIR)
|
||||||
|
if not os.path.exists(SSL_CERTS_DIR):
|
||||||
|
CsHelper.execute("mkdir -p %s" % SSL_CERTS_DIR)
|
||||||
|
cert_names = []
|
||||||
|
for cert in ssl_certs:
|
||||||
|
cert_names.append(cert['name'] + ".pem")
|
||||||
|
file = CsFile("%s/%s.pem" % (SSL_CERTS_DIR, cert['name']))
|
||||||
|
file.empty()
|
||||||
|
file.add("%s\n" % cert['cert'].replace("\r\n", "\n"))
|
||||||
|
if 'chain' in cert.keys():
|
||||||
|
file.add("%s\n" % cert['chain'].replace("\r\n", "\n"))
|
||||||
|
file.add("%s\n" % cert['key'].replace("\r\n", "\n"))
|
||||||
|
file.commit()
|
||||||
|
for f in listdir(SSL_CERTS_DIR):
|
||||||
|
if f not in cert_names:
|
||||||
|
CsHelper.execute("rm -rf %s/%s" % (SSL_CERTS_DIR, f))
|
||||||
|
|
||||||
|
def _verify_haproxy_config(self, config):
|
||||||
|
ret = CsHelper.execute2("haproxy -c -f %s" % config)
|
||||||
|
if ret.returncode:
|
||||||
|
stdout, stderr = ret.communicate()
|
||||||
|
logging.error("haproxy config is invalid with error: %s" % stderr)
|
||||||
|
return stderr
|
||||||
|
return ""
|
||||||
|
|||||||
568
test/integration/smoke/test_ssl_offloading.py
Normal file
568
test/integration/smoke/test_ssl_offloading.py
Normal file
@ -0,0 +1,568 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from marvin.codes import FAILED
|
||||||
|
from marvin.cloudstackTestCase import cloudstackTestCase
|
||||||
|
from marvin.lib.utils import wait_until
|
||||||
|
from marvin.lib.base import (Account,
|
||||||
|
Project,
|
||||||
|
UserData,
|
||||||
|
SslCertificate,
|
||||||
|
Template,
|
||||||
|
NetworkOffering,
|
||||||
|
ServiceOffering,
|
||||||
|
VirtualMachine,
|
||||||
|
Network,
|
||||||
|
VPC,
|
||||||
|
VpcOffering,
|
||||||
|
PublicIPAddress,
|
||||||
|
LoadBalancerRule)
|
||||||
|
from marvin.lib.common import (get_domain, get_zone, get_test_template)
|
||||||
|
from nose.plugins.attrib import attr
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
_multiprocess_shared_ = True
|
||||||
|
|
||||||
|
DOMAIN = "test-ssl-offloading.cloudstack.org"
|
||||||
|
CONTENT = "Test page"
|
||||||
|
FULL_CHAIN = "/tmp/full_chain.crt"
|
||||||
|
|
||||||
|
CERT = {
|
||||||
|
"privatekey": """-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCph7jsoMCQirRn
|
||||||
|
3obuvgnnefTXRQYd9tF9k2aCVkTiiisvC39px7MGdgvDXADhD9fmR7oyXVQlfNu0
|
||||||
|
rXjjgsVT3r4bv+DVi81YGXnuU7h10yCOZJt21i6QGHN1CS0/TAfg0UhlACCEYNRx
|
||||||
|
kB0klwUcj/jk/AKil1DoUGpvAm2gZsek/njb76/AeIfxc+Es4ZOPCVqQOHp6gI0q
|
||||||
|
t6KDMkUwv8fyzrpScygMUPVYrLmm6D0pn8yd3ihW07wGxMjND6UgOnao8t6H3LaM
|
||||||
|
Pe7eqSFzxunF9NFFjnUrKcHZZSledDM/37Kbqb/8T5f+4SwjioS1OdPCh8ApdiXq
|
||||||
|
HNUwYkALAgMBAAECggEAK5JiiQ7X7053B6s96uaVDRVfRGTNKa5iMXBNDHq3wbHZ
|
||||||
|
X4IJAVr+PE7ivxdKco3r45fT11X9ZpUsssdTJsZZiTDak69BTiFcaaRCnmqOIlpd
|
||||||
|
J7vb6TMrTIW8RvxQ0M/txm6DuNHLibqJX5a2pszZ13l5cwECfF9/v/XLJTTukCbu
|
||||||
|
6D/f3fBVFl1tM8y9saOEYLkdb4dILWY61bVSDNswgprz2EV1SFnk5jxz2FuBrM/Q
|
||||||
|
+7hINvjDcaRvcm59hRb1rkljv7S10VoNw/CFkU451csJkUe4vWZwB8lZK/XxLQG0
|
||||||
|
HEdS1zU1XY8H8Y1RCrxjGRyiiWsBtUThhWYlPrGCoQKBgQDkP09YAlKqXhT69Kx5
|
||||||
|
keg2i1jV2hA73zWbWXt9xp5jG5r3pl3m170DvKL93YIDnHtpTC56mlzGrzS7DSTN
|
||||||
|
p0buY9Qb3fkJxunCpPVFo0HMFkpeR77ax0v34NzSohlRLKFo5R2M1cmDfbVbnSSl
|
||||||
|
MB57FfRRMxzjrk+dJvjOeJsxjwKBgQC+JLb4B8CZjpurXYg3ySiRqFsCqkqob+kf
|
||||||
|
9dR+rWvcR6vMTEyha0hUlDvTikDepU2smYR4oPHfdcXF9lAJ7T02UmQDeizAqR68
|
||||||
|
u9e+yS0q3tdRnPPZmXJfaDCXG1hKMqF4YA5Vs0XAjleF3zHB+vBLrnlPpShtd/Mu
|
||||||
|
sWTpxICTxQKBgQDSr/n+pE5IQwYczOO0aFGwn5pF9L9NdPHXz5aleETV+TJn7WL6
|
||||||
|
ZiRsoaDWs7SCvtxQS2kP9RM0t5/2FeDmEMXx4aZ2fsSWGM3IxVo+iL+Aswa81n8/
|
||||||
|
Ff5y9lb/+29hNdBcsjk/ukwEG3Lf+UNNVAie15oppgPByzJkPwgmFsAy0wKBgHDX
|
||||||
|
/TZp82WuerhSw/rHiSoYjhqg0bnw4Ju1Gy0q4q5SYqTWS0wpDT4U0wSSMjlwRQ6/
|
||||||
|
9RxZ9/G0RXFc4tdhUkig0PY3VcPpGnLL0BhL8GBW69ZlnVpwdK4meV/UPKucLLPx
|
||||||
|
3dACmszSLSMn+LG0qVNg8mHQFJQS8eGuKcOKePw5AoGACuxtefROKdKOALh4lTi2
|
||||||
|
VOwPZ+1jxsm6lKNccIEvbUpe3UXPgNWpJiDX8mUcob4/NBLzmV3BUVKbG7Exbo5J
|
||||||
|
LoMfp7OsztWUFwt7YAvRfS8fHdhkEsxEf3T72ADieH5ZAuXFF+K0H3r6HtWPD4ws
|
||||||
|
mTJjGP4+Bl/dFakA5FJcjHg=
|
||||||
|
-----END PRIVATE KEY-----""",
|
||||||
|
"certificate": """-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFKjCCAxKgAwIBAgIUJ7BtN56KI8OuzbbM8SdtCLCB2UgwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwXjELMAkGA1UEBhMCWFgxCzAJBgNVBAgMAlhYMQswCQYDVQQHDAJYWDETMBEG
|
||||||
|
A1UECgwKQ2xvdWRTdGFjazEPMA0GA1UECwwGQXBhY2hlMQ8wDQYDVQQDDAZBcGFj
|
||||||
|
aGUwHhcNMjUwNjIzMTMxMzA3WhcNMzUwNjIxMTMxMzA3WjBoMQswCQYDVQQGEwJY
|
||||||
|
WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMQ8wDQYDVQQKDAZBcGFjaGUxEzAR
|
||||||
|
BgNVBAsMCkNsb3VkU3RhY2sxGTAXBgNVBAMMECouY2xvdWRzdGFjay5vcmcwggEi
|
||||||
|
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCph7jsoMCQirRn3obuvgnnefTX
|
||||||
|
RQYd9tF9k2aCVkTiiisvC39px7MGdgvDXADhD9fmR7oyXVQlfNu0rXjjgsVT3r4b
|
||||||
|
v+DVi81YGXnuU7h10yCOZJt21i6QGHN1CS0/TAfg0UhlACCEYNRxkB0klwUcj/jk
|
||||||
|
/AKil1DoUGpvAm2gZsek/njb76/AeIfxc+Es4ZOPCVqQOHp6gI0qt6KDMkUwv8fy
|
||||||
|
zrpScygMUPVYrLmm6D0pn8yd3ihW07wGxMjND6UgOnao8t6H3LaMPe7eqSFzxunF
|
||||||
|
9NFFjnUrKcHZZSledDM/37Kbqb/8T5f+4SwjioS1OdPCh8ApdiXqHNUwYkALAgMB
|
||||||
|
AAGjgdUwgdIwKwYDVR0RBCQwIoIQKi5jbG91ZHN0YWNrLm9yZ4IOY2xvdWRzdGFj
|
||||||
|
ay5vcmcwHQYDVR0OBBYEFCcq7jrdsqTD+Xi85DCqjYdL1gOqMIGDBgNVHSMEfDB6
|
||||||
|
oWKkYDBeMQswCQYDVQQGEwJYWDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMRMw
|
||||||
|
EQYDVQQKDApDbG91ZFN0YWNrMQ8wDQYDVQQLDAZBcGFjaGUxDzANBgNVBAMMBkFw
|
||||||
|
YWNoZYIURVB9+qvRJyOnJnqmYOw467vW3vQwDQYJKoZIhvcNAQELBQADggIBACld
|
||||||
|
lEXgn/A4/kZQbLwwMxBvaoPDDaDaYVpPbOoPw7a8YkrL0rmPIc04PyX9GAqxdC+c
|
||||||
|
qaEXvmp3I+BdT13XGcBosXO8uEQ3kses9F3MhOHORPS2mJag7t4eLnNX/0CgKTlR
|
||||||
|
6yC2Gu7d3xPNJ+CKMxekdoF31StEFNAYI/La/q3D+IGsRCbrVu3xpPaw2XlXI7Ro
|
||||||
|
RU7yebVmQPSNc75bm8Ydo1cdYtz9h8PVnc+6ThhSrdS3jYScj9DrX5ZJaKuZqSlu
|
||||||
|
0ZqFXoBflme+cYB7nb9HqnIO67r9vzd2dTcErJVAk5jQqG5Y38d1tingDx1A5opU
|
||||||
|
z4BkXEbHNV6VXYUQ5VE0dXO2sNvXVJrstwMPE8d3EvbX/1gWj8kuymbskrCjySE4
|
||||||
|
4Yztkb0dsJkVU793lz3EV75DsXvj3gevK049nPv2Grt1+rTgFNa6NJnLvKIKk/mv
|
||||||
|
fWjxbK2b/AAJ1ci6xtw/vKmIWoEu6uEMIJmhfBwuP+VnVJWJbmYXpNW/L5g21B76
|
||||||
|
Fn8RuQa3mlm5lZrxEcJ/b6fF+2NPJwj7sh6l688VtNXoVSSyXUeV5HwqCv+YMjKn
|
||||||
|
CtwpEN/eNHMbrkJvgYwSoOzqhV/wpmNi28S7MOm66JMECHOXOhk/eX2chIEjiVna
|
||||||
|
MXhvr/Twfj2N4gNVtcgXkrk39HEYjk5+uF7SdNf4
|
||||||
|
-----END CERTIFICATE-----""",
|
||||||
|
"certchain": """-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFQzCCAysCFEVQffqr0ScjpyZ6pmDsOOu71t70MA0GCSqGSIb3DQEBCwUAMF4x
|
||||||
|
CzAJBgNVBAYTAlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxEzARBgNVBAoM
|
||||||
|
CkNsb3VkU3RhY2sxDzANBgNVBAsMBkFwYWNoZTEPMA0GA1UEAwwGQXBhY2hlMB4X
|
||||||
|
DTI1MDYxNjEwMjc1NloXDTMwMDYxNTEwMjc1NlowXjELMAkGA1UEBhMCWFgxCzAJ
|
||||||
|
BgNVBAgMAlhYMQswCQYDVQQHDAJYWDETMBEGA1UECgwKQ2xvdWRTdGFjazEPMA0G
|
||||||
|
A1UECwwGQXBhY2hlMQ8wDQYDVQQDDAZBcGFjaGUwggIiMA0GCSqGSIb3DQEBAQUA
|
||||||
|
A4ICDwAwggIKAoICAQCLiQmSjrht15R1F+r79m/LZN5hsfQBGp+dy+yrtsWfOOur
|
||||||
|
RdXAwgbLxxsyKMQKWCQxlRI7wdhqh0L0ZBrIr9MjltYqsqLAoLmgY4eG/f6G8YGr
|
||||||
|
O/rxzfwTLbCeaIseF/OMA6Sz125HXYp1bltYK4LsuC7tihZXbeVa5pUGs3Jwgcfx
|
||||||
|
LYm4eB42Hp7Eg05uL8LbwT/1AjcwoWkTewKAWXA83zgLRDFDbl1t0IPHI4cdVvia
|
||||||
|
BNwNbG49ZCF6OgmokSarQSe4Vbems1u9T9pAySXAVjEYBqFjKWyswpdr782uNLmB
|
||||||
|
lCGm0pDeJ9/WASxbTJr7k9H6ZpnaHr54DG6ZqennWMz8w6r2pf7bp/EGZ3mZQ4s3
|
||||||
|
5ylSP4cQt8CSSI8k2CflPGUyytUAiWlDS3qSyIuAOPKXDg7wIpcbwcu4VMeKnH0Z
|
||||||
|
x7Uu9j1UDZEZoSu6UI/VInTl47k1/ECD+AO9yBzZSv+pTQmO3/Im3CcxsTHmVd5s
|
||||||
|
Tl0CJ/jWNpo9DAMtmGvt6CBWBXGRsO2XNk7djRcq2CubiCpvODg+7CcR6CiZK73L
|
||||||
|
1aOisLiq3+ofiJSSXRRuKtJlkQ4eSPSbYWkNJcKmIhbCoYOdH/Pe3/+RHjvNc1kO
|
||||||
|
OUb+icmfzcMVAs3C5jybpazsfjDNQZXWAFx4FLDcqOVbrCwom+tMukw+hzlZnwID
|
||||||
|
AQABMA0GCSqGSIb3DQEBCwUAA4ICAQAdexoMwn+Ol1A3+NOHk9WJZX+t2Q8/9wWb
|
||||||
|
K+jSVleSfXXWsB1mC3fABVJQdCxtXCH3rpt4w7FK6aUes9VjqAHap4tt9WBq0Wqq
|
||||||
|
vvMURFHfllxEM31Z35kBOCSQY9xwpGV1rRh/zYs4h55YixomR3nXNZ9cI8xzlSCi
|
||||||
|
sMG0mv0y+yxPohKrZj3AzLYz/M11SimSoyRPIANI+cUg1nJXyQoHzVHWEp1Nj0HB
|
||||||
|
M/GW05cxsWea7f5YcAW1JQI3FOkpwb72fIZOtMDa4PO8IYWXJAeAc/chw745/MTi
|
||||||
|
Rvl2NT4RZBAcrSNbhCOzRPG/ZiG+ArQuCluZ9HHAXRBMTtlLk5DO4+XxZlyGpjwf
|
||||||
|
uKniK8dccy9uU0ho73p9SNDhXH0yb9Naj8vd9NWzCUYaaBXt/92cIyhaAHAVFxJu
|
||||||
|
o6jr2FLbnhSGF9EO/tHvF7LxZv1dnbInvlWHwoFQjwmoeB+e17lHBdPMnWnPKBZe
|
||||||
|
jA2VH/IzGCucWuWQhruummO5GT8Z6F4jBwvafBo+QARKPZgEBpx3LycXrpkYI3LT
|
||||||
|
GGOpGCxFt5tVZOEsC/jQ5rIljNSeTzWmzfNRn/yRUW97uWsrzcQIBAUtu/pQnyFQ
|
||||||
|
WCnC1ipCp1zhJsXAFUKuqEfLngXodOvC4tAOr76h11S57o5lN4506Poq2mWgAZe/
|
||||||
|
JZr9MEn1+w==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFnzCCA4egAwIBAgIUcUNMqgWoDLsvMj0YmEudj60EG5swDQYJKoZIhvcNAQEL
|
||||||
|
BQAwXjELMAkGA1UEBhMCWFgxCzAJBgNVBAgMAlhYMQswCQYDVQQHDAJYWDETMBEG
|
||||||
|
A1UECgwKQ2xvdWRTdGFjazEPMA0GA1UECwwGQXBhY2hlMQ8wDQYDVQQDDAZBcGFj
|
||||||
|
aGUwIBcNMjUwNjE2MTAyNzM2WhgPMjEyNTA1MjMxMDI3MzZaMF4xCzAJBgNVBAYT
|
||||||
|
AlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxEzARBgNVBAoMCkNsb3VkU3Rh
|
||||||
|
Y2sxDzANBgNVBAsMBkFwYWNoZTEPMA0GA1UEAwwGQXBhY2hlMIICIjANBgkqhkiG
|
||||||
|
9w0BAQEFAAOCAg8AMIICCgKCAgEAwVQaePulUM523gKw168ToYp+gt05bXbu4Gg8
|
||||||
|
uaRDKhnRAX1sEgYwkQ36Q+iTDEM9sKRma8lMNMIqkZMQdk6sIGX6BL+6wUOb7mL0
|
||||||
|
5+I0yO9i8ooaGgNaeNvZftNIRlLsnPMGJaeom2/66XV4CsMqoZKaJ1H/I8N+bAeD
|
||||||
|
GvrBx+B4l9D3G390nQvot9JUzrJgGuLl0KDHapvhlR39cCgEfIii02uX1iy0qXlV
|
||||||
|
b+G1kLvpeC7T+lsJxondPJ69aO3lbDv/izyWw7qqBC57UhT/oKDxJmjQqklqzhgt
|
||||||
|
nM/p3YE7M0nkRi3LnRmsZBz7o1DRf+M29zypKzXVk1aJflL46AtLMmpDIzVrEB2M
|
||||||
|
q7o47rstXusYRYsBCqGTgdI1fV/CkDsZY5XkPZh2dsjZCHIS4P03OqFGsc6PQha2
|
||||||
|
+y2AhV1pvywkDl48kPKSukHfV1RtaPZUZtcQKztwHH+aFfo9mD8z0H2HcExdXKzd
|
||||||
|
jhRhI9ZSwFj3HEN9f5P8fS3lf5+fV7EEbG4NisieBj/UivW6QiTHpLD7wRLIUt2g
|
||||||
|
XgXNF0lfJzYHbIcxQ6kfC5McU2fu6mUC+p/pNN8G0POS3S2T55tEUqLL4N0SadQy
|
||||||
|
N1TZlTd2xTn+Hb6WlG0f5m97xGcNlGHKBvntFrHvOIfkEQ9ne3MlOO1Gjlintowo
|
||||||
|
fRGf15kCAwEAAaNTMFEwHQYDVR0OBBYEFM4WEQJpN9M07Q8CHq+5owG93Dj8MB8G
|
||||||
|
A1UdIwQYMBaAFM4WEQJpN9M07Q8CHq+5owG93Dj8MA8GA1UdEwEB/wQFMAMBAf8w
|
||||||
|
DQYJKoZIhvcNAQELBQADggIBABr5RKGc/rKx4fOgyXNJR4aCJCTtPZKs4AUCCBYz
|
||||||
|
cWOjJYGNvThOPSVx51nAD8+YP2BwkOIPfhl2u/+vQSSbz4OlauXLki2DUN8E2OFe
|
||||||
|
gJfxPDzWOfAo/QOJcyHwSlnIQjiZzG2lK3eyf5IFnfQILQzDvXaUYvMBkl2hb5Q7
|
||||||
|
44H6tRw78uuf/KsT4rY0bBFMN5DayjiyvoIDUvzCRqcb2KOi9DnZ7pXjduL7tO0j
|
||||||
|
PhlQ24B77LVUUAvydIGUzmbhGC2VvY1qE7uaYgYtgSUZ0zSjJrHjUjVLMzRouNP7
|
||||||
|
jpbBQRAcP4FDcOFZBHogunA0hxQdm0d8u3LqDYPNS0rpfW0ddU/72nfBX4bnoDEN
|
||||||
|
+anw4wOgFuUcoEThALWZ9ESVKxXQ9Fpvd6FRW8fLLqhXAuli1BqP1c1WRxagldYe
|
||||||
|
nPGm/FGZyJ2xOak9Uigi9NAQ/vX6CEfgcJgFZmCo8EKH0d4Ut72vGUcPqiUhT2EI
|
||||||
|
AFAd6drSyoUdXXniSMWky9Vrt+qtLuAD1nhHTv8ZPdItXokoiD6ea/4xrbUZn0qY
|
||||||
|
lLMDyfY76UVF0ruTR2Q6IdSq/zSggdwgkTooOW4XZcRf5l/ZnoeVQ1QH9C85SIKH
|
||||||
|
IKZwPeGUm+EntmpuCBDmQSHLRCGEThd64iOAjqLR6arLj4TBJzBrZsGHFJbm0OcI
|
||||||
|
dwa9
|
||||||
|
-----END CERTIFICATE-----""",
|
||||||
|
"enabledrevocationcheck": False
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install apache2 via userdata
|
||||||
|
USER_DATA="""I2Nsb3VkLWNvbmZpZwpydW5jbWQ6CiAgLSBzdWRvIGFwdC1nZXQgdXBkYXRlCiAgLSBzdWRvIGFw
|
||||||
|
dC1nZXQgaW5zdGFsbCAteSBhcGFjaGUyCiAgLSBzdWRvIHN5c3RlbWN0bCBlbmFibGUgYXBhY2hl
|
||||||
|
MgogIC0gc3VkbyBzeXN0ZW1jdGwgc3RhcnQgYXBhY2hlMgogIC0gZWNobyAiVGVzdCBwYWdlIiB8
|
||||||
|
c3VkbyB0ZWUgL3Zhci93d3cvaHRtbC90ZXN0Lmh0bWwK"""
|
||||||
|
# #cloud-config
|
||||||
|
# runcmd:
|
||||||
|
# - sudo apt-get update
|
||||||
|
# - sudo apt-get install -y apache2
|
||||||
|
# - sudo systemctl enable apache2
|
||||||
|
# - sudo systemctl start apache2
|
||||||
|
# - echo "Test page" |sudo tee /var/www/html/test.html
|
||||||
|
|
||||||
|
class TestSslOffloading(cloudstackTestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
|
||||||
|
testClient = super(TestSslOffloading, cls).getClsTestClient()
|
||||||
|
cls.apiclient = testClient.getApiClient()
|
||||||
|
cls.services = testClient.getParsedTestDataConfig()
|
||||||
|
cls._cleanup = []
|
||||||
|
|
||||||
|
# Get Zone, Domain and templates
|
||||||
|
cls.domain = get_domain(cls.apiclient)
|
||||||
|
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
|
||||||
|
cls.hypervisor = testClient.getHypervisorInfo()
|
||||||
|
|
||||||
|
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
|
||||||
|
|
||||||
|
# Save full chain as a file
|
||||||
|
with open(FULL_CHAIN, "w", encoding="utf-8") as f:
|
||||||
|
f.write(CERT["certchain"])
|
||||||
|
|
||||||
|
# Register template if needed
|
||||||
|
if cls.hypervisor.lower() == 'simulator':
|
||||||
|
cls.template = get_test_template(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.zone.id,
|
||||||
|
cls.hypervisor)
|
||||||
|
else:
|
||||||
|
cls.template = Template.register(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["test_templates_cloud_init"][cls.hypervisor.lower()],
|
||||||
|
zoneid=cls.zone.id,
|
||||||
|
hypervisor=cls.hypervisor,
|
||||||
|
)
|
||||||
|
cls.template.download(cls.apiclient)
|
||||||
|
cls._cleanup.append(cls.template)
|
||||||
|
|
||||||
|
if cls.template == FAILED:
|
||||||
|
assert False, "get_test_template() failed to return template"
|
||||||
|
|
||||||
|
# Create service offering
|
||||||
|
cls.service_offering = ServiceOffering.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["service_offerings"]["big"] # 512MB memory
|
||||||
|
)
|
||||||
|
cls._cleanup.append(cls.service_offering)
|
||||||
|
|
||||||
|
# Create network offering
|
||||||
|
cls.services["isolated_network_offering"]["egress_policy"] = "true"
|
||||||
|
cls.network_offering = NetworkOffering.create(cls.apiclient,
|
||||||
|
cls.services["isolated_network_offering"],
|
||||||
|
conservemode=True)
|
||||||
|
cls.network_offering.update(cls.apiclient, state='Enabled')
|
||||||
|
|
||||||
|
cls._cleanup.append(cls.network_offering)
|
||||||
|
|
||||||
|
#Create an account, network, VM and IP addresses
|
||||||
|
cls.account = Account.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["account"],
|
||||||
|
admin=True,
|
||||||
|
domainid=cls.domain.id
|
||||||
|
)
|
||||||
|
cls._cleanup.append(cls.account)
|
||||||
|
cls.user = cls.account.user[0]
|
||||||
|
cls.userapiclient = cls.testClient.getUserApiClient(cls.user.username, cls.domain.name)
|
||||||
|
|
||||||
|
cls.logger = logging.getLogger("TestSslOffloading")
|
||||||
|
cls.stream_handler = logging.StreamHandler()
|
||||||
|
cls.logger.setLevel(logging.DEBUG)
|
||||||
|
cls.logger.addHandler(cls.stream_handler)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.apiclient = self.testClient.getApiClient()
|
||||||
|
self.cleanup = []
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(TestSslOffloading, self).tearDown()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
super(TestSslOffloading, cls).tearDownClass()
|
||||||
|
# Remove full chain file
|
||||||
|
if os.path.exists(FULL_CHAIN):
|
||||||
|
os.remove(FULL_CHAIN)
|
||||||
|
|
||||||
|
def wait_for_service_ready(self, command, expected, retries=60):
|
||||||
|
output = None
|
||||||
|
self.logger.debug("======================================")
|
||||||
|
self.logger.debug("Checking output of command '%s', expected result: '%s'" % (command, expected))
|
||||||
|
def check_output():
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(command + ' 2>&1', shell=True).strip().decode('utf-8')
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.debug("Failed to get output of command '%s': '%s'" % (command, e))
|
||||||
|
if expected is None:
|
||||||
|
self.logger.debug("But it is expected")
|
||||||
|
return True, None
|
||||||
|
return False, None
|
||||||
|
self.logger.debug("Output of command '%s' is '%s'" % (command, output))
|
||||||
|
if expected is None:
|
||||||
|
self.logger.debug("But it is expected to be None")
|
||||||
|
return False, None
|
||||||
|
return (expected in output), None
|
||||||
|
|
||||||
|
res, _ = wait_until(10, retries, check_output)
|
||||||
|
if not res:
|
||||||
|
self.fail("Failed to wait for http server to show content '%s'. The output is '%s'" % (expected, output))
|
||||||
|
|
||||||
|
@attr(tags = ["advanced", "advancedns", "smoke"], required_hardware="true")
|
||||||
|
def test_01_ssl_offloading_isolated_network(self):
|
||||||
|
"""Test to create Load balancing rule with SSL offloading"""
|
||||||
|
|
||||||
|
# Validate:
|
||||||
|
# 1. Create isolated network and vm instance
|
||||||
|
# 2. create LB with port 80 -> 80, verify the website (should get expected content)
|
||||||
|
# 3. create LB with port 443 -> 80, verify the website (should not work)
|
||||||
|
# 4. add cert to LB with port 443
|
||||||
|
# 5. verify the website (should get expected content)
|
||||||
|
# 6. remove cert from LB with port 443
|
||||||
|
# 7. delete SSL certificate
|
||||||
|
|
||||||
|
# Register Userdata
|
||||||
|
self.userdata = UserData.register(self.apiclient,
|
||||||
|
name="test-userdata",
|
||||||
|
userdata=USER_DATA,
|
||||||
|
account=self.account.name,
|
||||||
|
domainid=self.account.domainid
|
||||||
|
)
|
||||||
|
|
||||||
|
# Upload SSL Certificate
|
||||||
|
self.sslcert = SslCertificate.create(self.apiclient,
|
||||||
|
CERT,
|
||||||
|
name="test-ssl-certificate",
|
||||||
|
account=self.account.name,
|
||||||
|
domainid=self.account.domainid)
|
||||||
|
|
||||||
|
# 1. Create network
|
||||||
|
self.network = Network.create(self.apiclient,
|
||||||
|
zoneid=self.zone.id,
|
||||||
|
services=self.services["network"],
|
||||||
|
domainid=self.domain.id,
|
||||||
|
account=self.account.name,
|
||||||
|
networkofferingid=self.network_offering.id)
|
||||||
|
self.cleanup.append(self.network)
|
||||||
|
|
||||||
|
self.services["virtual_machine"]["networkids"] = [str(self.network.id)]
|
||||||
|
|
||||||
|
# Create vm instance
|
||||||
|
self.vm_1 = VirtualMachine.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["virtual_machine"],
|
||||||
|
templateid=self.template.id,
|
||||||
|
accountid=self.account.name,
|
||||||
|
domainid=self.account.domainid,
|
||||||
|
userdataid=self.userdata.userdata.id,
|
||||||
|
serviceofferingid=self.service_offering.id
|
||||||
|
)
|
||||||
|
self.cleanup.append(self.vm_1)
|
||||||
|
|
||||||
|
self.public_ip = PublicIPAddress.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.account.name,
|
||||||
|
self.zone.id,
|
||||||
|
self.account.domainid,
|
||||||
|
self.services["virtual_machine"],
|
||||||
|
self.network.id)
|
||||||
|
|
||||||
|
# 2. create LB with port 80 -> 80, verify the website (should get expected content).
|
||||||
|
# firewall is open by default
|
||||||
|
lb_http = {
|
||||||
|
"name": "http",
|
||||||
|
"alg": "roundrobin",
|
||||||
|
"privateport": 80,
|
||||||
|
"publicport": 80,
|
||||||
|
"protocol": "tcp"
|
||||||
|
}
|
||||||
|
lb_rule_http = LoadBalancerRule.create(
|
||||||
|
self.apiclient,
|
||||||
|
lb_http,
|
||||||
|
self.public_ip.ipaddress.id,
|
||||||
|
accountid=self.account.name,
|
||||||
|
domainid=self.domain.id,
|
||||||
|
networkid=self.network.id
|
||||||
|
)
|
||||||
|
lb_rule_http.assign(self.apiclient, [self.vm_1])
|
||||||
|
command = "curl -sL --connect-timeout 3 http://%s/test.html" % self.public_ip.ipaddress.ipaddress
|
||||||
|
# wait 10 minutes until the webpage is available. it returns "503 Service Unavailable" if not available
|
||||||
|
self.wait_for_service_ready(command, CONTENT, 60)
|
||||||
|
|
||||||
|
# 3. create LB with port 443 -> 80, verify the website (should not work)
|
||||||
|
# firewall is open by default
|
||||||
|
lb_https = {
|
||||||
|
"name": "https",
|
||||||
|
"alg": "roundrobin",
|
||||||
|
"privateport": 80,
|
||||||
|
"publicport": 443,
|
||||||
|
"protocol": "ssl"
|
||||||
|
}
|
||||||
|
lb_rule_https = LoadBalancerRule.create(
|
||||||
|
self.apiclient,
|
||||||
|
lb_https,
|
||||||
|
self.public_ip.ipaddress.id,
|
||||||
|
accountid=self.account.name,
|
||||||
|
domainid=self.domain.id,
|
||||||
|
networkid=self.network.id
|
||||||
|
)
|
||||||
|
lb_rule_https.assign(self.apiclient, [self.vm_1])
|
||||||
|
|
||||||
|
command = "curl -L --connect-timeout 3 -k --resolve %s:443:%s https://%s/test.html" % (DOMAIN, self.public_ip.ipaddress.ipaddress, DOMAIN)
|
||||||
|
self.wait_for_service_ready(command, None, 1)
|
||||||
|
|
||||||
|
command = "curl -L --connect-timeout 3 --resolve %s:443:%s https://%s/test.html" % (DOMAIN, self.public_ip.ipaddress.ipaddress, DOMAIN)
|
||||||
|
self.wait_for_service_ready(command, None, 1)
|
||||||
|
|
||||||
|
# 4. add cert to LB with port 443
|
||||||
|
lb_rule_https.assignCert(self.apiclient, self.sslcert.id)
|
||||||
|
|
||||||
|
# 5. verify the website (should get expected content)
|
||||||
|
command = "curl -L --connect-timeout 3 --resolve %s:443:%s https://%s/test.html" % (DOMAIN, self.public_ip.ipaddress.ipaddress, DOMAIN)
|
||||||
|
self.wait_for_service_ready(command, None, 1)
|
||||||
|
|
||||||
|
command = "curl -sL --connect-timeout 3 -k --resolve %s:443:%s https://%s/test.html" % (DOMAIN, self.public_ip.ipaddress.ipaddress, DOMAIN)
|
||||||
|
self.wait_for_service_ready(command, CONTENT, 1)
|
||||||
|
|
||||||
|
command = "curl -sL --connect-timeout 3 --cacert %s --resolve %s:443:%s https://%s/test.html" % (FULL_CHAIN, DOMAIN, self.public_ip.ipaddress.ipaddress, DOMAIN)
|
||||||
|
self.wait_for_service_ready(command, CONTENT, 1)
|
||||||
|
|
||||||
|
# 6. remove cert from LB with port 443
|
||||||
|
lb_rule_https.removeCert(self.apiclient)
|
||||||
|
|
||||||
|
# 7. delete SSL certificate
|
||||||
|
self.sslcert.delete(self.apiclient)
|
||||||
|
|
||||||
|
@attr(tags = ["advanced", "advancedns", "smoke"], required_hardware="true")
|
||||||
|
def test_02_ssl_offloading_project_vpc(self):
|
||||||
|
"""Test to create Load balancing rule with SSL offloading in VPC in user project"""
|
||||||
|
|
||||||
|
# Validate:
|
||||||
|
# 1. Create VPC, VPC tier and vm instance
|
||||||
|
# 2. create LB with port 80 -> 80, verify the website (should get expected content)
|
||||||
|
# 3. create LB with port 443 -> 80, verify the website (should not work)
|
||||||
|
# 4. add cert to LB with port 443
|
||||||
|
# 5. verify the website (should get expected content)
|
||||||
|
# 6. remove cert from LB with port 443
|
||||||
|
# 7. delete SSL certificate
|
||||||
|
|
||||||
|
# Create project by user
|
||||||
|
self.project = Project.create(
|
||||||
|
self.userapiclient,
|
||||||
|
self.services["project"]
|
||||||
|
)
|
||||||
|
self.cleanup.append(self.project)
|
||||||
|
|
||||||
|
# Register Userdata by user
|
||||||
|
self.userdata = UserData.register(self.userapiclient,
|
||||||
|
name="test-user-userdata",
|
||||||
|
userdata=USER_DATA,
|
||||||
|
projectid=self.project.id
|
||||||
|
)
|
||||||
|
|
||||||
|
# Upload SSL Certificate by user
|
||||||
|
self.sslcert = SslCertificate.create(self.userapiclient,
|
||||||
|
CERT,
|
||||||
|
name="test-user-ssl-certificate",
|
||||||
|
projectid=self.project.id
|
||||||
|
)
|
||||||
|
|
||||||
|
# 1. Create VPC and VPC tier
|
||||||
|
vpcOffering = VpcOffering.list(self.userapiclient, name="Default VPC offering")
|
||||||
|
self.assertTrue(vpcOffering is not None and len(
|
||||||
|
vpcOffering) > 0, "No VPC offerings found")
|
||||||
|
|
||||||
|
self.vpc = VPC.create(
|
||||||
|
apiclient=self.userapiclient,
|
||||||
|
services=self.services["vpc_vpn"]["vpc"],
|
||||||
|
vpcofferingid=vpcOffering[0].id,
|
||||||
|
zoneid=self.zone.id,
|
||||||
|
projectid=self.project.id
|
||||||
|
)
|
||||||
|
self.cleanup.append(self.vpc)
|
||||||
|
|
||||||
|
networkOffering = NetworkOffering.list(
|
||||||
|
self.userapiclient, name="DefaultIsolatedNetworkOfferingForVpcNetworks")
|
||||||
|
self.assertTrue(networkOffering is not None and len(
|
||||||
|
networkOffering) > 0, "No VPC based network offering")
|
||||||
|
|
||||||
|
self.network = Network.create(
|
||||||
|
apiclient=self.userapiclient,
|
||||||
|
services=self.services["vpc_vpn"]["network_1"],
|
||||||
|
networkofferingid=networkOffering[0].id,
|
||||||
|
zoneid=self.zone.id,
|
||||||
|
vpcid=self.vpc.id,
|
||||||
|
projectid=self.project.id
|
||||||
|
)
|
||||||
|
self.cleanup.append(self.network)
|
||||||
|
|
||||||
|
self.services["virtual_machine"]["networkids"] = [str(self.network.id)]
|
||||||
|
|
||||||
|
# Create vm instance
|
||||||
|
self.vm_2 = VirtualMachine.create(
|
||||||
|
self.userapiclient,
|
||||||
|
self.services["virtual_machine"],
|
||||||
|
templateid=self.template.id,
|
||||||
|
userdataid=self.userdata.userdata.id,
|
||||||
|
serviceofferingid=self.service_offering.id,
|
||||||
|
projectid=self.project.id
|
||||||
|
)
|
||||||
|
self.cleanup.append(self.vm_2)
|
||||||
|
|
||||||
|
self.public_ip = PublicIPAddress.create(
|
||||||
|
self.userapiclient,
|
||||||
|
zoneid=self.zone.id,
|
||||||
|
services=self.services["virtual_machine"],
|
||||||
|
networkid=self.network.id,
|
||||||
|
vpcid=self.vpc.id,
|
||||||
|
projectid=self.project.id
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2. create LB with port 80 -> 80, verify the website (should get expected content).
|
||||||
|
# firewall is open by default
|
||||||
|
lb_http = {
|
||||||
|
"name": "http",
|
||||||
|
"alg": "roundrobin",
|
||||||
|
"privateport": 80,
|
||||||
|
"publicport": 80,
|
||||||
|
"protocol": "tcp"
|
||||||
|
}
|
||||||
|
lb_rule_http = LoadBalancerRule.create(
|
||||||
|
self.userapiclient,
|
||||||
|
lb_http,
|
||||||
|
self.public_ip.ipaddress.id,
|
||||||
|
networkid=self.network.id,
|
||||||
|
projectid=self.project.id
|
||||||
|
)
|
||||||
|
lb_rule_http.assign(self.userapiclient, [self.vm_2])
|
||||||
|
command = "curl -sL --connect-timeout 3 http://%s/test.html" % self.public_ip.ipaddress.ipaddress
|
||||||
|
# wait 10 minutes until the webpage is available. it returns "503 Service Unavailable" if not available
|
||||||
|
self.wait_for_service_ready(command, CONTENT, 60)
|
||||||
|
|
||||||
|
# 3. create LB with port 443 -> 80, verify the website (should not work)
|
||||||
|
# firewall is open by default
|
||||||
|
lb_https = {
|
||||||
|
"name": "https",
|
||||||
|
"alg": "roundrobin",
|
||||||
|
"privateport": 80,
|
||||||
|
"publicport": 443,
|
||||||
|
"protocol": "ssl"
|
||||||
|
}
|
||||||
|
lb_rule_https = LoadBalancerRule.create(
|
||||||
|
self.userapiclient,
|
||||||
|
lb_https,
|
||||||
|
self.public_ip.ipaddress.id,
|
||||||
|
networkid=self.network.id,
|
||||||
|
projectid=self.project.id
|
||||||
|
)
|
||||||
|
lb_rule_https.assign(self.userapiclient, [self.vm_2])
|
||||||
|
|
||||||
|
command = "curl -L --connect-timeout 3 -k --resolve %s:443:%s https://%s/test.html" % (DOMAIN, self.public_ip.ipaddress.ipaddress, DOMAIN)
|
||||||
|
self.wait_for_service_ready(command, None, 1)
|
||||||
|
|
||||||
|
command = "curl -L --connect-timeout 3 --resolve %s:443:%s https://%s/test.html" % (DOMAIN, self.public_ip.ipaddress.ipaddress, DOMAIN)
|
||||||
|
self.wait_for_service_ready(command, None, 1)
|
||||||
|
|
||||||
|
# 4. add cert to LB with port 443
|
||||||
|
lb_rule_https.assignCert(self.userapiclient, self.sslcert.id)
|
||||||
|
|
||||||
|
# 5. verify the website (should get expected content)
|
||||||
|
command = "curl -L --connect-timeout 3 --resolve %s:443:%s https://%s/test.html" % (DOMAIN, self.public_ip.ipaddress.ipaddress, DOMAIN)
|
||||||
|
self.wait_for_service_ready(command, None, 1)
|
||||||
|
|
||||||
|
command = "curl -sL --connect-timeout 3 -k --resolve %s:443:%s https://%s/test.html" % (DOMAIN, self.public_ip.ipaddress.ipaddress, DOMAIN)
|
||||||
|
self.wait_for_service_ready(command, CONTENT, 1)
|
||||||
|
|
||||||
|
command = "curl -sL --connect-timeout 3 --cacert %s --resolve %s:443:%s https://%s/test.html" % (FULL_CHAIN, DOMAIN, self.public_ip.ipaddress.ipaddress, DOMAIN)
|
||||||
|
self.wait_for_service_ready(command, CONTENT, 1)
|
||||||
|
|
||||||
|
# 6. remove cert from LB with port 443
|
||||||
|
lb_rule_https.removeCert(self.userapiclient)
|
||||||
|
|
||||||
|
# 7. delete SSL certificate
|
||||||
|
self.sslcert.delete(self.userapiclient)
|
||||||
@ -164,9 +164,10 @@ class CSConnection(object):
|
|||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
response = requests.post(url,
|
response = requests.post(url,
|
||||||
params=payload,
|
data=payload,
|
||||||
cert=self.certPath,
|
cert=self.certPath,
|
||||||
verify=self.httpsFlag)
|
verify=self.httpsFlag)
|
||||||
|
self.logger.debug("=======Got POST response : %s=======" % response)
|
||||||
return response
|
return response
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.__lastError = e
|
self.__lastError = e
|
||||||
|
|||||||
@ -1068,7 +1068,7 @@ test_data = {
|
|||||||
"displaytext": "ubuntu 22.04 kvm",
|
"displaytext": "ubuntu 22.04 kvm",
|
||||||
"format": "raw",
|
"format": "raw",
|
||||||
"hypervisor": "kvm",
|
"hypervisor": "kvm",
|
||||||
"ostype": "Other Linux (64-bit)",
|
"ostype": "Ubuntu 22.04 LTS",
|
||||||
"url": "https://cloud-images.ubuntu.com/releases/jammy/release/ubuntu-22.04-server-cloudimg-amd64.img",
|
"url": "https://cloud-images.ubuntu.com/releases/jammy/release/ubuntu-22.04-server-cloudimg-amd64.img",
|
||||||
"requireshvm": "True",
|
"requireshvm": "True",
|
||||||
"ispublic": "True",
|
"ispublic": "True",
|
||||||
|
|||||||
@ -3080,6 +3080,9 @@ class LoadBalancerRule:
|
|||||||
if "openfirewall" in services:
|
if "openfirewall" in services:
|
||||||
cmd.openfirewall = services["openfirewall"]
|
cmd.openfirewall = services["openfirewall"]
|
||||||
|
|
||||||
|
if "protocol" in services:
|
||||||
|
cmd.protocol = services["protocol"]
|
||||||
|
|
||||||
if projectid:
|
if projectid:
|
||||||
cmd.projectid = projectid
|
cmd.projectid = projectid
|
||||||
|
|
||||||
@ -3188,6 +3191,22 @@ class LoadBalancerRule:
|
|||||||
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
||||||
return apiclient.listLoadBalancerRuleInstances(cmd)
|
return apiclient.listLoadBalancerRuleInstances(cmd)
|
||||||
|
|
||||||
|
def assignCert(self, apiclient, certId, forced=None):
|
||||||
|
""""""
|
||||||
|
cmd = assignCertToLoadBalancer.assignCertToLoadBalancerCmd()
|
||||||
|
cmd.lbruleid = self.id
|
||||||
|
cmd.certid = certId
|
||||||
|
if forced is not None:
|
||||||
|
cmd.forced = forced
|
||||||
|
return apiclient.assignCertToLoadBalancer(cmd)
|
||||||
|
|
||||||
|
def removeCert(self, apiclient):
|
||||||
|
"""Removes a certificate from a load balancer rule"""
|
||||||
|
|
||||||
|
cmd = removeCertFromLoadBalancer.removeCertFromLoadBalancerCmd()
|
||||||
|
cmd.lbruleid = self.id
|
||||||
|
return apiclient.removeCertFromLoadBalancer(cmd)
|
||||||
|
|
||||||
|
|
||||||
class Cluster:
|
class Cluster:
|
||||||
"""Manage Cluster life cycle"""
|
"""Manage Cluster life cycle"""
|
||||||
@ -8016,3 +8035,60 @@ class GpuDevice:
|
|||||||
cmd.id = self.id
|
cmd.id = self.id
|
||||||
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
||||||
return (apiclient.updateGpuDevice(cmd))
|
return (apiclient.updateGpuDevice(cmd))
|
||||||
|
|
||||||
|
|
||||||
|
class SslCertificate:
|
||||||
|
|
||||||
|
def __init__(self, items):
|
||||||
|
self.__dict__.update(items)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, apiclient, services, name, certificate=None, privatekey=None,
|
||||||
|
certchain=None, password=None, enabledrevocationcheck=None,
|
||||||
|
account=None, domainid=None, projectid=None):
|
||||||
|
"""Upload SSL certificate"""
|
||||||
|
cmd = uploadSslCert.uploadSslCertCmd()
|
||||||
|
cmd.name = name
|
||||||
|
|
||||||
|
if certificate:
|
||||||
|
cmd.certificate = certificate
|
||||||
|
elif "certificate" in services:
|
||||||
|
cmd.certificate = services["certificate"]
|
||||||
|
|
||||||
|
if privatekey:
|
||||||
|
cmd.privatekey = privatekey
|
||||||
|
elif "privatekey" in services:
|
||||||
|
cmd.privatekey = services["privatekey"]
|
||||||
|
|
||||||
|
if certchain:
|
||||||
|
cmd.certchain = certchain
|
||||||
|
elif "certchain" in services:
|
||||||
|
cmd.certchain = services["certchain"]
|
||||||
|
|
||||||
|
if password:
|
||||||
|
cmd.password = password
|
||||||
|
elif "password" in services:
|
||||||
|
cmd.password = services["password"]
|
||||||
|
|
||||||
|
if enabledrevocationcheck is not None:
|
||||||
|
cmd.enabledrevocationcheck = enabledrevocationcheck
|
||||||
|
elif "enabledrevocationcheck" in services:
|
||||||
|
cmd.enabledrevocationcheck = services["enabledrevocationcheck"]
|
||||||
|
|
||||||
|
if account:
|
||||||
|
cmd.account = account
|
||||||
|
|
||||||
|
if projectid:
|
||||||
|
cmd.projectid = projectid
|
||||||
|
|
||||||
|
if domainid:
|
||||||
|
cmd.domainid = domainid
|
||||||
|
|
||||||
|
return SslCertificate(apiclient.uploadSslCert(cmd, method='POST').__dict__)
|
||||||
|
|
||||||
|
def delete(self, apiclient):
|
||||||
|
"""Delete SSL Certificate"""
|
||||||
|
|
||||||
|
cmd = deleteSslCert.deleteSslCertCmd()
|
||||||
|
cmd.id = self.id
|
||||||
|
apiclient.deleteSslCert(cmd)
|
||||||
|
|||||||
@ -505,10 +505,12 @@
|
|||||||
"label.category": "Category",
|
"label.category": "Category",
|
||||||
"label.certchain": "Chain",
|
"label.certchain": "Chain",
|
||||||
"label.certificate": "Certificate",
|
"label.certificate": "Certificate",
|
||||||
|
"label.certificate.chain": "Certificate chain",
|
||||||
"label.certificate.upload": "Certificate uploaded.",
|
"label.certificate.upload": "Certificate uploaded.",
|
||||||
"label.certificate.upload.failed": "Certificate upload failed",
|
"label.certificate.upload.failed": "Certificate upload failed",
|
||||||
"label.certificate.upload.failed.description": "Failed to update SSL Certificate. Failed to pass certificate validation check.",
|
"label.certificate.upload.failed.description": "Failed to update SSL Certificate. Failed to pass certificate validation check.",
|
||||||
"label.certificateid": "Certificate ID",
|
"label.certificateid": "Certificate ID",
|
||||||
|
"label.certificates": "Certificates",
|
||||||
"label.chainsize": "Chain size",
|
"label.chainsize": "Chain size",
|
||||||
"label.change": "Change",
|
"label.change": "Change",
|
||||||
"label.change.affinity": "Change affinity",
|
"label.change.affinity": "Change affinity",
|
||||||
@ -972,6 +974,7 @@
|
|||||||
"label.enable.vpn": "Enable remote access VPN",
|
"label.enable.vpn": "Enable remote access VPN",
|
||||||
"label.enable.webhook": "Enable Webhook",
|
"label.enable.webhook": "Enable Webhook",
|
||||||
"label.enabled": "Enabled",
|
"label.enabled": "Enabled",
|
||||||
|
"label.enabled.revocation.check": "Enables revocation checking for certificates",
|
||||||
"label.encrypt": "Encrypt",
|
"label.encrypt": "Encrypt",
|
||||||
"label.encryptroot": "Encrypt Root Disk",
|
"label.encryptroot": "Encrypt Root Disk",
|
||||||
"label.end": "End",
|
"label.end": "End",
|
||||||
@ -1480,6 +1483,7 @@
|
|||||||
"label.make.user.project.owner": "Make User project owner",
|
"label.make.user.project.owner": "Make User project owner",
|
||||||
"label.makeredundant": "Make redundant",
|
"label.makeredundant": "Make redundant",
|
||||||
"label.manage": "Manage",
|
"label.manage": "Manage",
|
||||||
|
"label.manage.ssl.cert": "Manage SSL certificate",
|
||||||
"label.manage.vpn.user": "Manage VPN Users",
|
"label.manage.vpn.user": "Manage VPN Users",
|
||||||
"label.managed.instances": "Managed Instances",
|
"label.managed.instances": "Managed Instances",
|
||||||
"label.managed.volumes": "Managed Volumes",
|
"label.managed.volumes": "Managed Volumes",
|
||||||
@ -2053,6 +2057,7 @@
|
|||||||
"label.remove.vpc.offering": "Remove VPC Offering",
|
"label.remove.vpc.offering": "Remove VPC Offering",
|
||||||
"label.removed": "Removed",
|
"label.removed": "Removed",
|
||||||
"label.removing": "Removing",
|
"label.removing": "Removing",
|
||||||
|
"label.replace": "Replace",
|
||||||
"label.replace.acl": "Replace ACL",
|
"label.replace.acl": "Replace ACL",
|
||||||
"label.report.bug": "Ask a question or Report an issue",
|
"label.report.bug": "Ask a question or Report an issue",
|
||||||
"label.request": "Request",
|
"label.request": "Request",
|
||||||
@ -2312,6 +2317,7 @@
|
|||||||
"label.uefi.supported": "UEFI supported",
|
"label.uefi.supported": "UEFI supported",
|
||||||
"label.unregister.extension": "Unregister Extension",
|
"label.unregister.extension": "Unregister Extension",
|
||||||
"label.usediops": "IOPS used",
|
"label.usediops": "IOPS used",
|
||||||
|
"label.userdata": "User Data",
|
||||||
"label.user.data.id": "User Data ID",
|
"label.user.data.id": "User Data ID",
|
||||||
"label.user.data.name": "User Data name",
|
"label.user.data.name": "User Data name",
|
||||||
"label.user.data.details": "User Data details",
|
"label.user.data.details": "User Data details",
|
||||||
@ -2327,6 +2333,8 @@
|
|||||||
"label.ssh.port": "SSH port",
|
"label.ssh.port": "SSH port",
|
||||||
"label.sshkeypair": "New SSH key pair",
|
"label.sshkeypair": "New SSH key pair",
|
||||||
"label.sshkeypairs": "SSH key pairs",
|
"label.sshkeypairs": "SSH key pairs",
|
||||||
|
"label.ssl": "SSL",
|
||||||
|
"label.sslcertificate": "SSL certificate",
|
||||||
"label.sslcertificates": "SSL certificates",
|
"label.sslcertificates": "SSL certificates",
|
||||||
"label.sslverification": "SSL verification",
|
"label.sslverification": "SSL verification",
|
||||||
"label.standard.us.keyboard": "Standard (US) keyboard",
|
"label.standard.us.keyboard": "Standard (US) keyboard",
|
||||||
@ -2587,6 +2595,7 @@
|
|||||||
"label.upload.icon": "Upload icon",
|
"label.upload.icon": "Upload icon",
|
||||||
"label.upload.iso.from.local": "Upload ISO from local",
|
"label.upload.iso.from.local": "Upload ISO from local",
|
||||||
"label.upload.resource.icon": "Upload icon",
|
"label.upload.resource.icon": "Upload icon",
|
||||||
|
"label.upload.ssl.certificate": "Upload SSL cerficicate",
|
||||||
"label.upload.template.from.local": "Upload Template from local",
|
"label.upload.template.from.local": "Upload Template from local",
|
||||||
"label.upload.volume": "Upload volume",
|
"label.upload.volume": "Upload volume",
|
||||||
"label.upload.volume.from.local": "Upload Volume from local",
|
"label.upload.volume.from.local": "Upload Volume from local",
|
||||||
@ -2996,6 +3005,8 @@
|
|||||||
"message.remove.ip.v6.firewall.rule.failed": "Failed to remove IPv6 firewall rule",
|
"message.remove.ip.v6.firewall.rule.failed": "Failed to remove IPv6 firewall rule",
|
||||||
"message.remove.ip.v6.firewall.rule.processing": "Removing IPv6 firewall rule...",
|
"message.remove.ip.v6.firewall.rule.processing": "Removing IPv6 firewall rule...",
|
||||||
"message.remove.ip.v6.firewall.rule.success": "Removed IPv6 firewall rule",
|
"message.remove.ip.v6.firewall.rule.success": "Removed IPv6 firewall rule",
|
||||||
|
"message.remove.sslcert.failed": "Failed to remove SSL certificate from load balancer",
|
||||||
|
"message.remove.sslcert.processing": "Removing SSL certificate from load balancer...",
|
||||||
"message.add.netris.controller": "Add Netris Provider",
|
"message.add.netris.controller": "Add Netris Provider",
|
||||||
"message.add.nsx.controller": "Add NSX Provider",
|
"message.add.nsx.controller": "Add NSX Provider",
|
||||||
"message.add.network": "Add a new network for Zone: <b><span id=\"zone_name\"></span></b>",
|
"message.add.network": "Add a new network for Zone: <b><span id=\"zone_name\"></span></b>",
|
||||||
@ -3047,6 +3058,8 @@
|
|||||||
"message.allowed": "Allowed",
|
"message.allowed": "Allowed",
|
||||||
"message.alert.show.all.stats.data": "This may return a lot of data depending on VM statistics and retention settings",
|
"message.alert.show.all.stats.data": "This may return a lot of data depending on VM statistics and retention settings",
|
||||||
"message.apply.success": "Apply Successfully",
|
"message.apply.success": "Apply Successfully",
|
||||||
|
"message.assign.sslcert.failed": "Failed to assign SSL certificate",
|
||||||
|
"message.assign.sslcert.processing": "Assigning SSL certificate...",
|
||||||
"message.assign.instance.another": "Please specify the Account type, domain, Account name and Network (optional) of the new Account. <br> If the default NIC of the Instance is on a shared Network, CloudStack will check if the Network can be used by the new Account if you do not specify one Network. <br> If the default NIC of the Instance is on a isolated Network, and the new Account has more one isolated Networks, you should specify one.",
|
"message.assign.instance.another": "Please specify the Account type, domain, Account name and Network (optional) of the new Account. <br> If the default NIC of the Instance is on a shared Network, CloudStack will check if the Network can be used by the new Account if you do not specify one Network. <br> If the default NIC of the Instance is on a isolated Network, and the new Account has more one isolated Networks, you should specify one.",
|
||||||
"message.assign.vm.failed": "Failed to assign Instance",
|
"message.assign.vm.failed": "Failed to assign Instance",
|
||||||
"message.assign.vm.processing": "Assigning Instance...",
|
"message.assign.vm.processing": "Assigning Instance...",
|
||||||
@ -3762,6 +3775,7 @@
|
|||||||
"message.success.add.vpc.network": "Successfully added a VPC network",
|
"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.vpn.gateway": "Successfully added VPN gateway",
|
||||||
|
"message.success.assign.sslcert": "Successfully assigned SSL certificate",
|
||||||
"message.success.assign.vm": "Successfully assigned Instance",
|
"message.success.assign.vm": "Successfully assigned Instance",
|
||||||
"message.success.apply.network.policy": "Successfully applied Network Policy",
|
"message.success.apply.network.policy": "Successfully applied Network Policy",
|
||||||
"message.success.apply.tungsten.tag": "Successfully applied Tag",
|
"message.success.apply.tungsten.tag": "Successfully applied Tag",
|
||||||
@ -3840,6 +3854,7 @@
|
|||||||
"message.success.release.ip": "Successfully released IP",
|
"message.success.release.ip": "Successfully released IP",
|
||||||
"message.success.release.dedicated.bgp.peer": "Successfully released dedicated BGP peer",
|
"message.success.release.dedicated.bgp.peer": "Successfully released dedicated BGP peer",
|
||||||
"message.success.release.dedicated.ipv4.subnet": "Successfully released dedicated IPv4 subnet",
|
"message.success.release.dedicated.ipv4.subnet": "Successfully released dedicated IPv4 subnet",
|
||||||
|
"message.success.remove.sslcert": "Successfully removed SSL certificate from load balancer",
|
||||||
"message.success.remove.egress.rule": "Successfully removed egress rule",
|
"message.success.remove.egress.rule": "Successfully removed egress rule",
|
||||||
"message.success.remove.objectstore.objects": "Successfully removed selected object(s)",
|
"message.success.remove.objectstore.objects": "Successfully removed selected object(s)",
|
||||||
"message.success.remove.objectstore.directory": "Successfully removed selected directory",
|
"message.success.remove.objectstore.directory": "Successfully removed selected directory",
|
||||||
@ -3888,6 +3903,7 @@
|
|||||||
"message.success.upload.description": "This ISO file has been uploaded. Please check its status in the Templates menu.",
|
"message.success.upload.description": "This ISO file has been uploaded. Please check its status in the Templates menu.",
|
||||||
"message.success.upload.icon": "Successfully uploaded icon for ",
|
"message.success.upload.icon": "Successfully uploaded icon for ",
|
||||||
"message.success.upload.iso.description": "This ISO file has been uploaded. Please check its status in the images > ISOs menu.",
|
"message.success.upload.iso.description": "This ISO file has been uploaded. Please check its status in the images > ISOs menu.",
|
||||||
|
"message.success.upload.ssl.cert": "Successfully uploaded SSL certificate",
|
||||||
"message.success.upload.template.description": "This Template file has been uploaded. Please check its status in the Templates menu.",
|
"message.success.upload.template.description": "This Template file has been uploaded. Please check its status in the Templates menu.",
|
||||||
"message.success.upload.volume.description": "This volume has been uploaded. Please check its status in the volumes menu.",
|
"message.success.upload.volume.description": "This volume has been uploaded. Please check its status in the volumes menu.",
|
||||||
"message.suspend.project": "Are you sure you want to suspend this project?",
|
"message.suspend.project": "Are you sure you want to suspend this project?",
|
||||||
|
|||||||
@ -85,7 +85,7 @@ export default {
|
|||||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/ResourceLimitTab.vue')))
|
component: shallowRef(defineAsyncComponent(() => import('@/components/view/ResourceLimitTab.vue')))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'certificate',
|
name: 'certificates',
|
||||||
component: shallowRef(defineAsyncComponent(() => import('@/views/iam/SSLCertificateTab.vue')))
|
component: shallowRef(defineAsyncComponent(() => import('@/views/iam/SSLCertificateTab.vue')))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -46,6 +46,10 @@ export default {
|
|||||||
'listProjectRoles' in store.getters.apis
|
'listProjectRoles' in store.getters.apis
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'certificates',
|
||||||
|
component: shallowRef(defineAsyncComponent(() => import('@/views/iam/SSLCertificateTab.vue')))
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'limits',
|
name: 'limits',
|
||||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/ResourceCountUsage.vue')))
|
component: shallowRef(defineAsyncComponent(() => import('@/components/view/ResourceCountUsage.vue')))
|
||||||
|
|||||||
@ -284,6 +284,7 @@
|
|||||||
<a-select-option value="tcp-proxy">{{ $t('label.tcp.proxy') }}</a-select-option>
|
<a-select-option value="tcp-proxy">{{ $t('label.tcp.proxy') }}</a-select-option>
|
||||||
<a-select-option value="tcp">{{ $t('label.tcp') }}</a-select-option>
|
<a-select-option value="tcp">{{ $t('label.tcp') }}</a-select-option>
|
||||||
<a-select-option value="udp">{{ $t('label.udp') }}</a-select-option>
|
<a-select-option value="udp">{{ $t('label.udp') }}</a-select-option>
|
||||||
|
<a-select-option value="ssl">{{ $t('label.ssl') }}</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</div>
|
</div>
|
||||||
<div :span="24" class="action-button">
|
<div :span="24" class="action-button">
|
||||||
|
|||||||
@ -17,6 +17,17 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<a-row :gutter="12">
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<a-button
|
||||||
|
shape="round"
|
||||||
|
style="left: 10px; float: right;margin-bottom: 10px; z-index: 8"
|
||||||
|
@click="() => { showUploadForm = true }">
|
||||||
|
<template #icon><plus-outlined /></template>
|
||||||
|
{{ $t('label.upload.ssl.certificate') }}
|
||||||
|
</a-button>
|
||||||
|
</a-spin>
|
||||||
|
</a-row>
|
||||||
<a-row :gutter="12">
|
<a-row :gutter="12">
|
||||||
<a-col :md="24" :lg="24">
|
<a-col :md="24" :lg="24">
|
||||||
<a-table
|
<a-table
|
||||||
@ -68,16 +79,120 @@
|
|||||||
</a-list>
|
</a-list>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
|
<a-modal
|
||||||
|
v-if="showUploadForm"
|
||||||
|
:visible="showUploadForm"
|
||||||
|
:title="$t('label.upload.ssl.certificate')"
|
||||||
|
:maskClosable="false"
|
||||||
|
:closable="true"
|
||||||
|
:footer="null"
|
||||||
|
@cancel="() => { showUploadForm = false }"
|
||||||
|
centered
|
||||||
|
width="30vw">
|
||||||
|
|
||||||
|
<a-form
|
||||||
|
layout="vertical"
|
||||||
|
:ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
@finish="uploadSslCert"
|
||||||
|
v-ctrl-enter="uploadSslCert"
|
||||||
|
>
|
||||||
|
<a-form-item name="name" ref="name">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.name')" :tooltip="apiParams.name.description" tooltipPlacement="bottom"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
id="name"
|
||||||
|
:placeholder="apiParams.name.description"
|
||||||
|
name="name"
|
||||||
|
v-model:value="form.name"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item name="certificate" ref="certificate" :required="true">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.certificate')" :tooltip="apiParams.certificate.description" tooltipPlacement="bottom"/>
|
||||||
|
</template>
|
||||||
|
<a-textarea
|
||||||
|
id="certificate"
|
||||||
|
rows="2"
|
||||||
|
:placeholder="apiParams.certificate.description"
|
||||||
|
v-focus="true"
|
||||||
|
name="certificate"
|
||||||
|
v-model:value="form.certificate"
|
||||||
|
></a-textarea>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item name="privatekey" ref="privatekey" :required="true">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.privatekey')" :tooltip="apiParams.privatekey.description" tooltipPlacement="bottom"/>
|
||||||
|
</template>
|
||||||
|
<a-textarea
|
||||||
|
id="privatekey"
|
||||||
|
rows="2"
|
||||||
|
:placeholder="apiParams.privatekey.description"
|
||||||
|
name="privatekey"
|
||||||
|
v-model:value="form.privatekey"
|
||||||
|
></a-textarea>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item name="certchain" ref="certchain">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.certificate.chain')" :tooltip="apiParams.certchain.description" tooltipPlacement="bottom"/>
|
||||||
|
</template>
|
||||||
|
<a-textarea
|
||||||
|
id="certchain"
|
||||||
|
rows="2"
|
||||||
|
:placeholder="apiParams.certchain.description"
|
||||||
|
name="certchain"
|
||||||
|
v-model:value="form.certchain"
|
||||||
|
></a-textarea>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item name="password" ref="password">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.password')" :tooltip="apiParams.password.description" tooltipPlacement="bottom"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
v-model:value="form.password"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item name="enabledrevocationcheck" ref="enabledrevocationcheck">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.enabled.revocation.check')" :tooltip="apiParams.enabledrevocationcheck.description" tooltipPlacement="bottom"/>
|
||||||
|
</template>
|
||||||
|
<a-checkbox v-model:checked="form.enabledrevocationcheck"></a-checkbox>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="showUploadForm = false" class="close-button">
|
||||||
|
{{ $t('label.cancel' ) }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" ref="submit" :loading="uploading" @click="uploadSslCert">
|
||||||
|
{{ $t('label.submit' ) }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getAPI, postAPI } from '@/api'
|
import { getAPI, postAPI } from '@/api'
|
||||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||||
|
import TooltipLabel from '@/components/widgets/TooltipLabel.vue'
|
||||||
|
import { ref, reactive, toRaw } from 'vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SSLCertificate',
|
name: 'SSLCertificate',
|
||||||
components: {
|
components: {
|
||||||
|
TooltipLabel,
|
||||||
TooltipButton
|
TooltipButton
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
@ -90,7 +205,9 @@ export default {
|
|||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
quickview: false,
|
quickview: false,
|
||||||
loading: false
|
loading: false,
|
||||||
|
uploading: false,
|
||||||
|
showUploadForm: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
@ -127,6 +244,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.apiParams = this.$getApiParams('uploadSslCert')
|
||||||
|
},
|
||||||
created () {
|
created () {
|
||||||
this.columns = [
|
this.columns = [
|
||||||
{
|
{
|
||||||
@ -149,14 +269,28 @@ export default {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
this.detailColumn = ['name', 'certificate', 'certchain']
|
this.detailColumn = ['name', 'certificate', 'certchain']
|
||||||
|
this.initForm()
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
initForm () {
|
||||||
|
this.formRef = ref()
|
||||||
|
this.form = reactive({})
|
||||||
|
this.rules = reactive({
|
||||||
|
certificate: [{ required: true, message: this.$t('label.required') }],
|
||||||
|
privatekey: [{ required: true, message: this.$t('label.required') }]
|
||||||
|
})
|
||||||
|
},
|
||||||
fetchData () {
|
fetchData () {
|
||||||
const params = {}
|
const params = {}
|
||||||
params.page = this.page
|
params.page = this.page
|
||||||
params.pageSize = this.pageSize
|
params.pageSize = this.pageSize
|
||||||
params.accountid = this.resource.id
|
if (this.$route.meta.name === 'account') {
|
||||||
|
params.accountid = this.resource.id
|
||||||
|
delete params.projectid
|
||||||
|
} else { // project
|
||||||
|
params.projectid = this.resource.id
|
||||||
|
}
|
||||||
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
|
|
||||||
@ -224,6 +358,46 @@ export default {
|
|||||||
self.onDelete(row)
|
self.onDelete(row)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
uploadSslCert () {
|
||||||
|
if (this.uploading) return
|
||||||
|
this.formRef.value.validate().then(() => {
|
||||||
|
const formValues = toRaw(this.form)
|
||||||
|
this.uploading = true
|
||||||
|
const params = {
|
||||||
|
name: formValues.name,
|
||||||
|
certificate: formValues.certificate,
|
||||||
|
privatekey: formValues.privatekey
|
||||||
|
}
|
||||||
|
if (formValues.enabledrevocationcheck != null && formValues.enabledrevocationcheck) {
|
||||||
|
params.enabledrevocationcheck = 'true'
|
||||||
|
} else {
|
||||||
|
params.enabledrevocationcheck = 'false'
|
||||||
|
}
|
||||||
|
if (this.$route.meta.name === 'account') {
|
||||||
|
params.account = this.resource.name
|
||||||
|
params.domainid = this.resource.domainid
|
||||||
|
} else { // project
|
||||||
|
params.projectid = this.resource.id
|
||||||
|
}
|
||||||
|
if (formValues.password) {
|
||||||
|
params.password = formValues.password
|
||||||
|
}
|
||||||
|
if (formValues.certchain) {
|
||||||
|
params.certchain = formValues.certchain
|
||||||
|
}
|
||||||
|
postAPI('uploadSslCert', params).then(json => {
|
||||||
|
this.$notification.success({
|
||||||
|
message: this.$t('message.success.upload.ssl.cert')
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.fetchData()
|
||||||
|
this.uploading = false
|
||||||
|
this.showUploadForm = false
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,6 +67,7 @@
|
|||||||
<a-select-option v-if="lbProvider !== 'Netris'" value="tcp-proxy" :label="$t('label.tcp.proxy')">{{ $t('label.tcp.proxy') }}</a-select-option>
|
<a-select-option v-if="lbProvider !== 'Netris'" value="tcp-proxy" :label="$t('label.tcp.proxy')">{{ $t('label.tcp.proxy') }}</a-select-option>
|
||||||
<a-select-option value="tcp" :label="$t('label.tcp')">{{ $t('label.tcp') }}</a-select-option>
|
<a-select-option value="tcp" :label="$t('label.tcp')">{{ $t('label.tcp') }}</a-select-option>
|
||||||
<a-select-option value="udp" :label="$t('label.udp')">{{ $t('label.udp') }}</a-select-option>
|
<a-select-option value="udp" :label="$t('label.udp')">{{ $t('label.udp') }}</a-select-option>
|
||||||
|
<a-select-option value="ssl" :label="$t('label.ssl')">{{ $t('label.ssl') }}</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form__item">
|
<div class="form__item">
|
||||||
@ -84,6 +85,12 @@
|
|||||||
<a-select-option value="no">{{ $t('label.no') }}</a-select-option>
|
<a-select-option value="no">{{ $t('label.no') }}</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form__item" v-if="newRule.protocol === 'ssl'" >
|
||||||
|
<div class="form__label">{{ $t('label.sslcertificate') }}</div>
|
||||||
|
<a-button :disabled="!('createLoadBalancerRule' in $store.getters.apis)" type="primary" @click="handleOpenAddSslCertModal(null)">
|
||||||
|
{{ this.selectedSsl.id != null ? this.selectedSsl.name : $t('label.add') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
<div class="form__item" v-if="!newRule.autoscale || newRule.autoscale === 'no'">
|
<div class="form__item" v-if="!newRule.autoscale || newRule.autoscale === 'no'">
|
||||||
<div class="form__label" style="white-space: nowrap;">{{ $t('label.add.vms') }}</div>
|
<div class="form__label" style="white-space: nowrap;">{{ $t('label.add.vms') }}</div>
|
||||||
<a-button :disabled="!('createLoadBalancerRule' in $store.getters.apis)" type="primary" @click="handleOpenAddVMModal">
|
<a-button :disabled="!('createLoadBalancerRule' in $store.getters.apis)" type="primary" @click="handleOpenAddVMModal">
|
||||||
@ -139,6 +146,12 @@
|
|||||||
{{ returnStickinessLabel(record.id) }}
|
{{ returnStickinessLabel(record.id) }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="column.key === 'sslcert'">
|
||||||
|
<a-button :disabled="record.protocol !== 'ssl'" @click="() => { selectedRule = record; handleOpenAddSslCertModal(record) }">
|
||||||
|
<template #icon><plus-outlined /></template>
|
||||||
|
{{ $t('label.manage') }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
<template v-if="column.key === 'autoscale'">
|
<template v-if="column.key === 'autoscale'">
|
||||||
<div>
|
<div>
|
||||||
<router-link :to="{ path: '/autoscalevmgroup/' + record.autoscalevmgroup.id }" v-if='record.autoscalevmgroup'>
|
<router-link :to="{ path: '/autoscalevmgroup/' + record.autoscalevmgroup.id }" v-if='record.autoscalevmgroup'>
|
||||||
@ -435,6 +448,7 @@
|
|||||||
<a-select-option value="tcp-proxy" :label="$t('label.tcp.proxy')">{{ $t('label.tcp.proxy') }}</a-select-option>
|
<a-select-option value="tcp-proxy" :label="$t('label.tcp.proxy')">{{ $t('label.tcp.proxy') }}</a-select-option>
|
||||||
<a-select-option value="tcp" :label="$t('label.tcp')">{{ $t('label.tcp') }}</a-select-option>
|
<a-select-option value="tcp" :label="$t('label.tcp')">{{ $t('label.tcp') }}</a-select-option>
|
||||||
<a-select-option value="udp" :label="$t('label.udp')">{{ $t('label.udp') }}</a-select-option>
|
<a-select-option value="udp" :label="$t('label.udp')">{{ $t('label.udp') }}</a-select-option>
|
||||||
|
<a-select-option value="ssl" :label="$t('label.ssl')">{{ $t('label.ssl') }}</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</div>
|
</div>
|
||||||
<div :span="24" class="action-button">
|
<div :span="24" class="action-button">
|
||||||
@ -552,6 +566,60 @@
|
|||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
|
<a-modal
|
||||||
|
:title="$t('label.manage.ssl.cert')"
|
||||||
|
:maskClosable="false"
|
||||||
|
:closable="true"
|
||||||
|
v-if="addSslCertModalVisible"
|
||||||
|
:visible="addSslCertModalVisible"
|
||||||
|
width="30vw"
|
||||||
|
@cancel="addSslCertModalVisible = false"
|
||||||
|
@ok="addSslCertModalVisible = false"
|
||||||
|
:cancelButtonProps="{ style: { display: 'none' } }"
|
||||||
|
>
|
||||||
|
<a-row v-show="showAssignedSsl && assignedSslCert !== 'None'">
|
||||||
|
<a-col :span="8">
|
||||||
|
<div class="form__label">{{ $t("label.current") + ' ' + $t('label.sslcertificate') }}</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="10">
|
||||||
|
<div>{{ assignedSslCert }}</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-button :disabled="!deleteSslButtonVisible" type="danger" @click="removeSslFromLbRule()">
|
||||||
|
<template #icon><delete-outlined /></template>
|
||||||
|
{{ $t('label.remove') }}
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row style="margin-top: 16px">
|
||||||
|
<a-col :span="8">
|
||||||
|
<div class="form__label">{{ $t("label.new") + ' ' + $t('label.sslcertificate') }}</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="10">
|
||||||
|
<div class="form__item">
|
||||||
|
<a-select v-model:value="selectedSsl.name" style="width: 80%;" @change="selectssl">
|
||||||
|
<a-select-option
|
||||||
|
v-for="sslcert in sslcerts.data"
|
||||||
|
:key="sslcert.id">{{ sslcert.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<div>
|
||||||
|
<a-button v-show="addSslButtonVisible && assignedSslCert !== 'None'" type="primary" @click="addSslTolbRule()">
|
||||||
|
<template #icon><swap-outlined /></template>
|
||||||
|
{{ $t('label.replace') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button v-show="addSslButtonVisible && assignedSslCert === 'None'" type="primary" @click="addSslTolbRule()">
|
||||||
|
<template #icon><plus-outlined /></template>
|
||||||
|
{{ $t('label.assign') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
<a-modal
|
<a-modal
|
||||||
:title="$t('label.select.tier')"
|
:title="$t('label.select.tier')"
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
@ -791,6 +859,20 @@ export default {
|
|||||||
totalCount: 0,
|
totalCount: 0,
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
|
sslcerts: {
|
||||||
|
loading: false,
|
||||||
|
data: []
|
||||||
|
},
|
||||||
|
selectedSsl: {
|
||||||
|
name: '',
|
||||||
|
id: null
|
||||||
|
},
|
||||||
|
addSslCertModalVisible: false,
|
||||||
|
showAssignedSsl: false,
|
||||||
|
currentAccountId: null,
|
||||||
|
assignedSslCert: 'None',
|
||||||
|
deleteSslButtonVisible: true,
|
||||||
|
addSslButtonVisible: true,
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
title: this.$t('label.name'),
|
title: this.$t('label.name'),
|
||||||
@ -824,6 +906,10 @@ export default {
|
|||||||
key: 'add',
|
key: 'add',
|
||||||
title: this.$t('label.add.vms')
|
title: this.$t('label.add.vms')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'sslcert',
|
||||||
|
title: this.$t('label.sslcertificate')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'autoscale',
|
key: 'autoscale',
|
||||||
title: this.$t('label.autoscale')
|
title: this.$t('label.autoscale')
|
||||||
@ -1092,6 +1178,147 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
fetchSslCerts () {
|
||||||
|
this.sslcerts.loading = true
|
||||||
|
this.sslcerts.data = []
|
||||||
|
// First get the account id
|
||||||
|
getAPI('listAccounts', {
|
||||||
|
name: this.resource.account,
|
||||||
|
domainid: this.resource.domainid
|
||||||
|
}).then(json => {
|
||||||
|
const accounts = json.listaccountsresponse.account || []
|
||||||
|
if (accounts.length > 0) {
|
||||||
|
// Now fetch all the ssl certs for this account
|
||||||
|
this.currentAccountId = accounts[0].id
|
||||||
|
getAPI('listSslCerts', {
|
||||||
|
accountid: this.currentAccountId
|
||||||
|
}).then(json => {
|
||||||
|
json.listsslcertsresponse.sslcert.forEach(entry => this.sslcerts.data.push(entry))
|
||||||
|
if (json.listsslcertsresponse.sslcert && json.listsslcertsresponse.sslcert.length > 0 && this.selectedSsl.id == null) {
|
||||||
|
this.selectedSsl.name = json.listsslcertsresponse.sslcert[0].name
|
||||||
|
this.selectedSsl.id = json.listsslcertsresponse.sslcert[0].id
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.sslcerts.loading = false
|
||||||
|
})
|
||||||
|
if (this.selectedRule !== null) {
|
||||||
|
this.getCurrentAssignedSslCert()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getCurrentAssignedSslCert () {
|
||||||
|
getAPI('listSslCerts', {
|
||||||
|
accountid: this.currentAccountId,
|
||||||
|
lbruleid: this.selectedRule.id
|
||||||
|
}).then(json => {
|
||||||
|
if (json.listsslcertsresponse.sslcert && json.listsslcertsresponse.sslcert.length > 0) {
|
||||||
|
this.assignedSslCert = json.listsslcertsresponse.sslcert[0].name
|
||||||
|
this.deleteSslButtonVisible = true
|
||||||
|
} else {
|
||||||
|
this.assignedSslCert = 'None'
|
||||||
|
this.deleteSslButtonVisible = false
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
selectssl (e) {
|
||||||
|
this.selectedSsl.id = e
|
||||||
|
const sslcert = this.sslcerts.data.find(entry => entry.id === this.selectedSsl.id)
|
||||||
|
if (sslcert) {
|
||||||
|
this.selectedSsl.name = sslcert.name
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleAddSslCert (data) {
|
||||||
|
this.addSslCert(data, this.selectedSsl.id)
|
||||||
|
},
|
||||||
|
addSslTolbRule () {
|
||||||
|
this.visible = false
|
||||||
|
this.addSslCert(this.selectedRule.id, this.selectedSsl.id)
|
||||||
|
},
|
||||||
|
addSslCert (lbRuleId, certId) {
|
||||||
|
this.disableSslAddDeleteButtons()
|
||||||
|
getAPI('assignCertToLoadBalancer', {
|
||||||
|
lbruleid: lbRuleId,
|
||||||
|
certid: certId,
|
||||||
|
forced: true
|
||||||
|
}).then(response => {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId: response.assigncerttoloadbalancerresponse.jobid,
|
||||||
|
successMessage: this.$t('message.success.assign.sslcert'),
|
||||||
|
successMethod: () => {
|
||||||
|
if (this.selectedRule !== null) {
|
||||||
|
this.getCurrentAssignedSslCert()
|
||||||
|
}
|
||||||
|
this.enableSslAddDeleteButtons()
|
||||||
|
},
|
||||||
|
errorMessage: this.$t('message.assign.sslcert.failed'),
|
||||||
|
errorMethod: () => {
|
||||||
|
},
|
||||||
|
loadingMessage: this.$t('message.assign.sslcert.processing'),
|
||||||
|
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||||
|
catchMethod: (e) => {
|
||||||
|
this.closeModal()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removeSslFromLbRule () {
|
||||||
|
this.disableSslAddDeleteButtons()
|
||||||
|
getAPI('removeCertFromLoadBalancer', {
|
||||||
|
lbruleid: this.selectedRule.id
|
||||||
|
}).then(response => {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId: response.removecertfromloadbalancerresponse.jobid,
|
||||||
|
successMessage: this.$t('message.success.remove.sslcert'),
|
||||||
|
successMethod: () => {
|
||||||
|
this.visible = true
|
||||||
|
this.getCurrentAssignedSslCert()
|
||||||
|
this.enableSslAddDeleteButtons()
|
||||||
|
},
|
||||||
|
errorMessage: this.$t('message.remove.sslcert.failed'),
|
||||||
|
errorMethod: () => {
|
||||||
|
this.visible = true
|
||||||
|
},
|
||||||
|
loadingMessage: this.$t('message.remove.sslcert.processing'),
|
||||||
|
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||||
|
catchMethod: () => {
|
||||||
|
this.closeModal()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
})
|
||||||
|
},
|
||||||
|
enableSslAddDeleteButtons () {
|
||||||
|
this.deleteSslButtonVisible = true
|
||||||
|
this.addSslButtonVisible = true
|
||||||
|
},
|
||||||
|
disableSslAddDeleteButtons () {
|
||||||
|
this.addSslButtonVisible = false
|
||||||
|
this.deleteSslButtonVisible = false
|
||||||
|
},
|
||||||
|
handleOpenAddSslCertModal (record) {
|
||||||
|
this.addSslCertModalVisible = true
|
||||||
|
if (record) {
|
||||||
|
this.showAssignedSsl = true
|
||||||
|
this.addSslButtonVisible = true
|
||||||
|
this.selectedSsl = {}
|
||||||
|
} else {
|
||||||
|
this.showAssignedSsl = false
|
||||||
|
this.addSslButtonVisible = false
|
||||||
|
}
|
||||||
|
this.fetchSslCerts()
|
||||||
|
},
|
||||||
returnAlgorithmName (name) {
|
returnAlgorithmName (name) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'leastconn':
|
case 'leastconn':
|
||||||
@ -1705,6 +1932,9 @@ export default {
|
|||||||
successMessage: this.$t('message.success.assign.vm'),
|
successMessage: this.$t('message.success.assign.vm'),
|
||||||
successMethod: () => {
|
successMethod: () => {
|
||||||
this.parentToggleLoading()
|
this.parentToggleLoading()
|
||||||
|
if (this.newRule.protocol === 'ssl' && this.selectedSsl.id !== null) {
|
||||||
|
this.handleAddSslCert(data)
|
||||||
|
}
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
this.closeModal()
|
this.closeModal()
|
||||||
},
|
},
|
||||||
@ -1780,6 +2010,7 @@ export default {
|
|||||||
this.addNetworkModalLoading = false
|
this.addNetworkModalLoading = false
|
||||||
this.addNetworkModalVisible = false
|
this.addNetworkModalVisible = false
|
||||||
this.selectedTierForAutoScaling = null
|
this.selectedTierForAutoScaling = null
|
||||||
|
this.addSslCertModalVisible = null
|
||||||
},
|
},
|
||||||
handleChangePage (page, pageSize) {
|
handleChangePage (page, pageSize) {
|
||||||
this.page = page
|
this.page = page
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user