diff --git a/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDao.java b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDao.java index e60e4b004c6..67eea928864 100644 --- a/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDao.java +++ b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDao.java @@ -28,4 +28,6 @@ public interface KeystoreDao extends GenericDao { void save(String alias, String certificate, Integer index, String domainSuffix); List findCertChain(); + + List findCertChain(String domainSuffix); } diff --git a/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDaoImpl.java b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDaoImpl.java index cd24611a86e..b856582ac11 100644 --- a/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDaoImpl.java +++ b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDaoImpl.java @@ -38,6 +38,7 @@ import com.cloud.utils.exception.CloudRuntimeException; public class KeystoreDaoImpl extends GenericDaoBase implements KeystoreDao { protected final SearchBuilder FindByNameSearch; protected final SearchBuilder CertChainSearch; + protected final SearchBuilder CertChainSearchForDomainSuffix; public KeystoreDaoImpl() { FindByNameSearch = createSearchBuilder(); @@ -47,6 +48,11 @@ public class KeystoreDaoImpl extends GenericDaoBase implements CertChainSearch = createSearchBuilder(); CertChainSearch.and("key", CertChainSearch.entity().getKey(), Op.NULL); CertChainSearch.done(); + + CertChainSearchForDomainSuffix = createSearchBuilder(); + CertChainSearchForDomainSuffix.and("key", CertChainSearchForDomainSuffix.entity().getKey(), Op.NULL); + CertChainSearchForDomainSuffix.and("domainSuffix", CertChainSearchForDomainSuffix.entity().getDomainSuffix(), Op.EQ); + CertChainSearchForDomainSuffix.done(); } @Override @@ -64,6 +70,19 @@ public class KeystoreDaoImpl extends GenericDaoBase implements return ks; } + @Override + public List findCertChain(String domainSuffix) { + SearchCriteria sc = CertChainSearchForDomainSuffix.create(); + sc.setParameters("domainSuffix", domainSuffix); + List ks = listBy(sc); + Collections.sort(ks, new Comparator() { public int compare(Object o1, Object o2) { + Integer seq1 = ((KeystoreVO)o1).getIndex(); + Integer seq2 = ((KeystoreVO)o2).getIndex(); + return seq1.compareTo(seq2); + }}); + return ks; + } + @Override public KeystoreVO findByName(String name) { assert (name != null); diff --git a/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManager.java b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManager.java index 3b99947c4b5..c44347c85ef 100644 --- a/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManager.java +++ b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManager.java @@ -28,15 +28,18 @@ public interface KeystoreManager extends Manager { private String privCert; @LogLevel(Log4jLevel.Off) private String certChain; + @LogLevel(Log4jLevel.Off) + private String rootCACert; public Certificates() { } - public Certificates(String prvKey, String privCert, String certChain) { - privKey = prvKey; + public Certificates(String prvKey, String privCert, String certChain, String rootCACert) { + this.privKey = prvKey; this.privCert = privCert; this.certChain = certChain; + this.rootCACert = rootCACert; } public String getPrivKey() { @@ -50,6 +53,10 @@ public interface KeystoreManager extends Manager { public String getCertChain() { return certChain; } + + public String getRootCACert() { + return rootCACert; + } } boolean validateCertificate(String certificate, String key, String domainSuffix); diff --git a/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManagerImpl.java b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManagerImpl.java index 306083492af..374c080413f 100644 --- a/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManagerImpl.java +++ b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManagerImpl.java @@ -23,6 +23,7 @@ import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -30,6 +31,7 @@ import java.util.regex.Pattern; import javax.ejb.Local; import javax.inject.Inject; +import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -129,17 +131,22 @@ public class KeystoreManagerImpl extends ManagerBase implements KeystoreManager } String prvKey = ksVo.getKey(); String prvCert = ksVo.getCertificate(); + String domainSuffix = ksVo.getDomainSuffix(); String certChain = null; - List certchains = _ksDao.findCertChain(); + String rootCert = null; + List certchains = _ksDao.findCertChain(domainSuffix); if (certchains.size() > 0) { - StringBuilder chains = new StringBuilder(); + ArrayList chains = new ArrayList(); for (KeystoreVO cert : certchains) { - chains.append(cert.getCertificate()); - chains.append("\n"); + if (chains.size() == 0) {// For the first time it will be length 0 + rootCert = cert.getCertificate(); + } + chains.add(cert.getCertificate()); } - certChain = chains.toString(); + Collections.reverse(chains); + certChain = StringUtils.join(chains, "\n"); } - Certificates certs = new Certificates(prvKey, prvCert, certChain); + Certificates certs = new Certificates(prvKey, prvCert, certChain, rootCert); return certs; } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index e33c7c71086..081145f3295 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -16,9 +16,7 @@ // under the License. package com.cloud.server; -import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; -import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; @@ -3447,16 +3445,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe String certificate = cmd.getCertificate(); String key = cmd.getPrivateKey(); - try { - if (certificate != null) { - certificate = URLDecoder.decode(certificate, "UTF-8"); - } - if (key != null) { - key = URLDecoder.decode(key, "UTF-8"); - } - } catch (UnsupportedEncodingException e) { - } finally { - } if (cmd.getPrivateKey() != null && !_ksMgr.validateCertificate(certificate, key, cmd.getDomainSuffix())) { throw new InvalidParameterValueException("Failed to pass certificate validation check"); @@ -3464,16 +3452,20 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (cmd.getPrivateKey() != null) { _ksMgr.saveCertificate(ConsoleProxyManager.CERTIFICATE_NAME, certificate, key, cmd.getDomainSuffix()); + + // Reboot ssvm here since private key is present - meaning server cert being passed + List alreadyRunning = _secStorageVmDao.getSecStorageVmListInStates(null, State.Running, State.Migrating, State.Starting); + for (SecondaryStorageVmVO ssVmVm : alreadyRunning) { + _secStorageVmMgr.rebootSecStorageVm(ssVmVm.getId()); + } } else { _ksMgr.saveCertificate(cmd.getAlias(), certificate, cmd.getCertIndex(), cmd.getDomainSuffix()); } _consoleProxyMgr.setManagementState(ConsoleProxyManagementState.ResetSuspending); - List alreadyRunning = _secStorageVmDao.getSecStorageVmListInStates(null, State.Running, State.Migrating, State.Starting); - for (SecondaryStorageVmVO ssVmVm : alreadyRunning) { - _secStorageVmMgr.rebootSecStorageVm(ssVmVm.getId()); - } - return "Certificate has been updated, we will stop all running console proxy VMs and secondary storage VMs to propagate the new certificate, please give a few minutes for console access service to be up again"; + return "Certificate has been successfully updated, if its the server certificate we would reboot all " + + "running console proxy VMs and secondary storage VMs to propagate the new certificate, " + + "please give a few minutes for console access and storage services service to be up and working again"; } @Override diff --git a/services/secondary-storage/server/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java b/services/secondary-storage/server/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java index f35b3f544ef..6927f028cf2 100755 --- a/services/secondary-storage/server/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java +++ b/services/secondary-storage/server/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java @@ -1188,6 +1188,8 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S } else { String prvKey = certs.getPrivKey(); String pubCert = certs.getPrivCert(); + String certChain = certs.getCertChain(); + String rootCACert = certs.getRootCACert(); try { File prvKeyFile = File.createTempFile("prvkey", null); @@ -1203,10 +1205,34 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S out.write(pubCert); out.close(); - configureSSL(prvkeyPath, pubCertFilePath, null); + String certChainFilePath = null, rootCACertFilePath = null; + File certChainFile = null, rootCACertFile = null; + if(certChain != null){ + certChainFile = File.createTempFile("certchain", null); + certChainFilePath = certChainFile.getAbsolutePath(); + out = new BufferedWriter(new FileWriter(certChainFile)); + out.write(certChain); + out.close(); + } + + if(rootCACert != null){ + rootCACertFile = File.createTempFile("rootcert", null); + rootCACertFilePath = rootCACertFile.getAbsolutePath(); + out = new BufferedWriter(new FileWriter(rootCACertFile)); + out.write(rootCACert); + out.close(); + } + + configureSSL(prvkeyPath, pubCertFilePath, certChainFilePath, rootCACertFilePath); prvKeyFile.delete(); pubCertFile.delete(); + if(certChainFile != null){ + certChainFile.delete(); + } + if(rootCACertFile != null){ + rootCACertFile.delete(); + } } catch (IOException e) { s_logger.debug("Failed to config ssl: " + e.toString()); @@ -2064,7 +2090,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S } } - private void configureSSL(String prvkeyPath, String prvCertPath, String certChainPath) { + private void configureSSL(String prvkeyPath, String prvCertPath, String certChainPath, String rootCACert) { if (!_inSystemVM) { return; } @@ -2076,6 +2102,9 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S if (certChainPath != null) { command.add("-t", certChainPath); } + if (rootCACert != null) { + command.add("-u", rootCACert); + } String result = command.execute(); if (result != null) { s_logger.warn("Unable to configure httpd to use ssl"); diff --git a/systemvm/scripts/_run.sh b/systemvm/scripts/_run.sh index 1a37c7d372c..3792261c143 100755 --- a/systemvm/scripts/_run.sh +++ b/systemvm/scripts/_run.sh @@ -68,4 +68,4 @@ if [ "$(uname -m | grep '64')" == "" ]; then fi fi -java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -Dlog.home=$LOGHOME -mx${maxmem}m -cp $CP com.cloud.agent.AgentShell $keyvalues $@ +java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -Djsse.enableSNIExtension=false -Dlog.home=$LOGHOME -mx${maxmem}m -cp $CP com.cloud.agent.AgentShell $keyvalues $@ diff --git a/systemvm/scripts/config_ssl.sh b/systemvm/scripts/config_ssl.sh index e4747872693..69710554f1e 100755 --- a/systemvm/scripts/config_ssl.sh +++ b/systemvm/scripts/config_ssl.sh @@ -24,6 +24,7 @@ help() { printf " -k path of private key\n" printf " -p path of certificate of public key\n" printf " -t path of certificate chain\n" + printf " -u path of root ca certificate \n" } @@ -53,6 +54,10 @@ config_apache2_conf() { sed -i -e "s/NameVirtualHost .*:80/NameVirtualHost $ip:80/g" /etc/apache2/ports.conf sed -i 's/ssl-cert-snakeoil.key/cert_apache.key/' /etc/apache2/sites-available/default-ssl sed -i 's/ssl-cert-snakeoil.pem/cert_apache.crt/' /etc/apache2/sites-available/default-ssl + if [ -f /etc/ssl/certs/cert_apache_chain.crt ] + then + sed -i -e "s/#SSLCertificateChainFile.*/SSLCertificateChainFile \/etc\/ssl\/certs\/cert_apache_chain.crt/" /etc/apache2/sites-available/default-ssl + fi } copy_certs() { @@ -88,12 +93,13 @@ cccflag= customPrivKey=$(dirname $0)/certs/realhostip.key customPrivCert=$(dirname $0)/certs/realhostip.crt customCertChain= +customCACert= publicIp= hostName= keyStore=$(dirname $0)/certs/realhostip.keystore aliasName="CPVMCertificate" storepass="vmops.com" -while getopts 'i:h:k:p:t:c' OPTION +while getopts 'i:h:k:p:t:u:c' OPTION do case $OPTION in c) cflag=1 @@ -107,6 +113,9 @@ do t) cccflag=1 customCertChain="$OPTARG" ;; + u) ccacflag=1 + customCACert="$OPTARG" + ;; i) publicIp="$OPTARG" ;; h) hostName="$OPTARG" @@ -165,10 +174,10 @@ then exit 2 fi -if [ -f "$customPrivCert" ] +if [ -f "$customCACert" ] then keytool -delete -alias $aliasName -keystore $keyStore -storepass $storepass -noprompt - keytool -import -alias $aliasName -keystore $keyStore -storepass $storepass -noprompt -file $customPrivCert + keytool -import -alias $aliasName -keystore $keyStore -storepass $storepass -noprompt -file $customCACert fi if [ -d /etc/apache2 ] diff --git a/ui/scripts/ui-custom/physicalResources.js b/ui/scripts/ui-custom/physicalResources.js index fcc2f6ab5bd..06fcca71c72 100644 --- a/ui/scripts/ui-custom/physicalResources.js +++ b/ui/scripts/ui-custom/physicalResources.js @@ -117,8 +117,8 @@ type: "POST", url: createURL('uploadCustomCertificate'), data: { - certificate: encodeURIComponent(args.data.certificate), - privatekey: encodeURIComponent(args.data.privatekey), + certificate: args.data.certificate, + privatekey: args.data.privatekey, domainsuffix: args.data.domainsuffix }, dataType: 'json',