Skip to content

Commit 1d45b75

Browse files
committed
CLOUDSTACK-6499:
Made changes so that uploading custom certificate works for ssvm. 1. Reboot ssvm only when private key is passed meaning the server cert is passed. This is because while uploading the server cert is the last to be uploaded. And we want to propagate the entire chain once uploading is done. 2. Change the SecStorageSetupCommand sent to ssvm so that it also carries the root cert apart from having the chain and the server cert and key. 3. Change ssvm agent code to be able to configure root cert to the java key store. 4. Change ssvm configure ssl script to insert the chain certs correctly. 5. Fix order of chain certificates for apache webserver in SSVM 6. Remove double encoding and decoding for uploadCustomCertificate API from UI and server code respectively, so that API call without UI works fine 7. Java 1.7 - disable using SNI since copyTemplate doesnt work for SSL.
1 parent 42d48fe commit 1d45b75

9 files changed

Lines changed: 98 additions & 33 deletions

File tree

framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDao.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,6 @@ public interface KeystoreDao extends GenericDao<KeystoreVO, Long> {
2828
void save(String alias, String certificate, Integer index, String domainSuffix);
2929

3030
List<KeystoreVO> findCertChain();
31+
32+
List<KeystoreVO> findCertChain(String domainSuffix);
3133
}

framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDaoImpl.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
public class KeystoreDaoImpl extends GenericDaoBase<KeystoreVO, Long> implements KeystoreDao {
3939
protected final SearchBuilder<KeystoreVO> FindByNameSearch;
4040
protected final SearchBuilder<KeystoreVO> CertChainSearch;
41+
protected final SearchBuilder<KeystoreVO> CertChainSearchForDomainSuffix;
4142

4243
public KeystoreDaoImpl() {
4344
FindByNameSearch = createSearchBuilder();
@@ -47,6 +48,11 @@ public KeystoreDaoImpl() {
4748
CertChainSearch = createSearchBuilder();
4849
CertChainSearch.and("key", CertChainSearch.entity().getKey(), Op.NULL);
4950
CertChainSearch.done();
51+
52+
CertChainSearchForDomainSuffix = createSearchBuilder();
53+
CertChainSearchForDomainSuffix.and("key", CertChainSearchForDomainSuffix.entity().getKey(), Op.NULL);
54+
CertChainSearchForDomainSuffix.and("domainSuffix", CertChainSearchForDomainSuffix.entity().getDomainSuffix(), Op.EQ);
55+
CertChainSearchForDomainSuffix.done();
5056
}
5157

5258
@Override
@@ -64,6 +70,19 @@ public int compare(Object o1, Object o2) {
6470
return ks;
6571
}
6672

73+
@Override
74+
public List<KeystoreVO> findCertChain(String domainSuffix) {
75+
SearchCriteria<KeystoreVO> sc = CertChainSearchForDomainSuffix.create();
76+
sc.setParameters("domainSuffix", domainSuffix);
77+
List<KeystoreVO> ks = listBy(sc);
78+
Collections.sort(ks, new Comparator() { public int compare(Object o1, Object o2) {
79+
Integer seq1 = ((KeystoreVO)o1).getIndex();
80+
Integer seq2 = ((KeystoreVO)o2).getIndex();
81+
return seq1.compareTo(seq2);
82+
}});
83+
return ks;
84+
}
85+
6786
@Override
6887
public KeystoreVO findByName(String name) {
6988
assert (name != null);

framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManager.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,18 @@ public static class Certificates {
2828
private String privCert;
2929
@LogLevel(Log4jLevel.Off)
3030
private String certChain;
31+
@LogLevel(Log4jLevel.Off)
32+
private String rootCACert;
3133

3234
public Certificates() {
3335

3436
}
3537

36-
public Certificates(String prvKey, String privCert, String certChain) {
37-
privKey = prvKey;
38+
public Certificates(String prvKey, String privCert, String certChain, String rootCACert) {
39+
this.privKey = prvKey;
3840
this.privCert = privCert;
3941
this.certChain = certChain;
42+
this.rootCACert = rootCACert;
4043
}
4144

4245
public String getPrivKey() {
@@ -50,6 +53,10 @@ public String getPrivCert() {
5053
public String getCertChain() {
5154
return certChain;
5255
}
56+
57+
public String getRootCACert() {
58+
return rootCACert;
59+
}
5360
}
5461

5562
boolean validateCertificate(String certificate, String key, String domainSuffix);

framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManagerImpl.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@
2323
import java.security.cert.CertificateException;
2424
import java.security.spec.InvalidKeySpecException;
2525
import java.util.ArrayList;
26+
import java.util.Collections;
2627
import java.util.List;
2728
import java.util.regex.Matcher;
2829
import java.util.regex.Pattern;
2930

3031
import javax.ejb.Local;
3132
import javax.inject.Inject;
3233

34+
import org.apache.commons.lang.StringUtils;
3335
import org.apache.log4j.Logger;
3436
import org.springframework.stereotype.Component;
3537

@@ -129,17 +131,22 @@ public Certificates getCertificates(String name) {
129131
}
130132
String prvKey = ksVo.getKey();
131133
String prvCert = ksVo.getCertificate();
134+
String domainSuffix = ksVo.getDomainSuffix();
132135
String certChain = null;
133-
List<KeystoreVO> certchains = _ksDao.findCertChain();
136+
String rootCert = null;
137+
List<KeystoreVO> certchains = _ksDao.findCertChain(domainSuffix);
134138
if (certchains.size() > 0) {
135-
StringBuilder chains = new StringBuilder();
139+
ArrayList<String> chains = new ArrayList<String>();
136140
for (KeystoreVO cert : certchains) {
137-
chains.append(cert.getCertificate());
138-
chains.append("\n");
141+
if (chains.size() == 0) {// For the first time it will be length 0
142+
rootCert = cert.getCertificate();
143+
}
144+
chains.add(cert.getCertificate());
139145
}
140-
certChain = chains.toString();
146+
Collections.reverse(chains);
147+
certChain = StringUtils.join(chains, "\n");
141148
}
142-
Certificates certs = new Certificates(prvKey, prvCert, certChain);
149+
Certificates certs = new Certificates(prvKey, prvCert, certChain, rootCert);
143150
return certs;
144151
}
145152

server/src/com/cloud/server/ManagementServerImpl.java

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616
// under the License.
1717
package com.cloud.server;
1818

19-
import java.io.UnsupportedEncodingException;
2019
import java.lang.reflect.Field;
21-
import java.net.URLDecoder;
2220
import java.util.ArrayList;
2321
import java.util.Arrays;
2422
import java.util.Calendar;
@@ -3447,33 +3445,27 @@ public String uploadCertificate(UploadCustomCertificateCmd cmd) {
34473445

34483446
String certificate = cmd.getCertificate();
34493447
String key = cmd.getPrivateKey();
3450-
try {
3451-
if (certificate != null) {
3452-
certificate = URLDecoder.decode(certificate, "UTF-8");
3453-
}
3454-
if (key != null) {
3455-
key = URLDecoder.decode(key, "UTF-8");
3456-
}
3457-
} catch (UnsupportedEncodingException e) {
3458-
} finally {
3459-
}
34603448

34613449
if (cmd.getPrivateKey() != null && !_ksMgr.validateCertificate(certificate, key, cmd.getDomainSuffix())) {
34623450
throw new InvalidParameterValueException("Failed to pass certificate validation check");
34633451
}
34643452

34653453
if (cmd.getPrivateKey() != null) {
34663454
_ksMgr.saveCertificate(ConsoleProxyManager.CERTIFICATE_NAME, certificate, key, cmd.getDomainSuffix());
3455+
3456+
// Reboot ssvm here since private key is present - meaning server cert being passed
3457+
List<SecondaryStorageVmVO> alreadyRunning = _secStorageVmDao.getSecStorageVmListInStates(null, State.Running, State.Migrating, State.Starting);
3458+
for (SecondaryStorageVmVO ssVmVm : alreadyRunning) {
3459+
_secStorageVmMgr.rebootSecStorageVm(ssVmVm.getId());
3460+
}
34673461
} else {
34683462
_ksMgr.saveCertificate(cmd.getAlias(), certificate, cmd.getCertIndex(), cmd.getDomainSuffix());
34693463
}
34703464

34713465
_consoleProxyMgr.setManagementState(ConsoleProxyManagementState.ResetSuspending);
3472-
List<SecondaryStorageVmVO> alreadyRunning = _secStorageVmDao.getSecStorageVmListInStates(null, State.Running, State.Migrating, State.Starting);
3473-
for (SecondaryStorageVmVO ssVmVm : alreadyRunning) {
3474-
_secStorageVmMgr.rebootSecStorageVm(ssVmVm.getId());
3475-
}
3476-
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";
3466+
return "Certificate has been successfully updated, if its the server certificate we would reboot all " +
3467+
"running console proxy VMs and secondary storage VMs to propagate the new certificate, " +
3468+
"please give a few minutes for console access and storage services service to be up and working again";
34773469
}
34783470

34793471
@Override

services/secondary-storage/server/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,8 @@ private void configCerts(KeystoreManager.Certificates certs) {
11881188
} else {
11891189
String prvKey = certs.getPrivKey();
11901190
String pubCert = certs.getPrivCert();
1191+
String certChain = certs.getCertChain();
1192+
String rootCACert = certs.getRootCACert();
11911193

11921194
try {
11931195
File prvKeyFile = File.createTempFile("prvkey", null);
@@ -1203,10 +1205,34 @@ private void configCerts(KeystoreManager.Certificates certs) {
12031205
out.write(pubCert);
12041206
out.close();
12051207

1206-
configureSSL(prvkeyPath, pubCertFilePath, null);
1208+
String certChainFilePath = null, rootCACertFilePath = null;
1209+
File certChainFile = null, rootCACertFile = null;
1210+
if(certChain != null){
1211+
certChainFile = File.createTempFile("certchain", null);
1212+
certChainFilePath = certChainFile.getAbsolutePath();
1213+
out = new BufferedWriter(new FileWriter(certChainFile));
1214+
out.write(certChain);
1215+
out.close();
1216+
}
1217+
1218+
if(rootCACert != null){
1219+
rootCACertFile = File.createTempFile("rootcert", null);
1220+
rootCACertFilePath = rootCACertFile.getAbsolutePath();
1221+
out = new BufferedWriter(new FileWriter(rootCACertFile));
1222+
out.write(rootCACert);
1223+
out.close();
1224+
}
1225+
1226+
configureSSL(prvkeyPath, pubCertFilePath, certChainFilePath, rootCACertFilePath);
12071227

12081228
prvKeyFile.delete();
12091229
pubCertFile.delete();
1230+
if(certChainFile != null){
1231+
certChainFile.delete();
1232+
}
1233+
if(rootCACertFile != null){
1234+
rootCACertFile.delete();
1235+
}
12101236

12111237
} catch (IOException e) {
12121238
s_logger.debug("Failed to config ssl: " + e.toString());
@@ -2064,7 +2090,7 @@ private void configureSSL() {
20642090
}
20652091
}
20662092

2067-
private void configureSSL(String prvkeyPath, String prvCertPath, String certChainPath) {
2093+
private void configureSSL(String prvkeyPath, String prvCertPath, String certChainPath, String rootCACert) {
20682094
if (!_inSystemVM) {
20692095
return;
20702096
}
@@ -2076,6 +2102,9 @@ private void configureSSL(String prvkeyPath, String prvCertPath, String certChai
20762102
if (certChainPath != null) {
20772103
command.add("-t", certChainPath);
20782104
}
2105+
if (rootCACert != null) {
2106+
command.add("-u", rootCACert);
2107+
}
20792108
String result = command.execute();
20802109
if (result != null) {
20812110
s_logger.warn("Unable to configure httpd to use ssl");

systemvm/scripts/_run.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,4 @@ if [ "$(uname -m | grep '64')" == "" ]; then
6868
fi
6969
fi
7070

71-
java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -Dlog.home=$LOGHOME -mx${maxmem}m -cp $CP com.cloud.agent.AgentShell $keyvalues $@
71+
java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -Djsse.enableSNIExtension=false -Dlog.home=$LOGHOME -mx${maxmem}m -cp $CP com.cloud.agent.AgentShell $keyvalues $@

systemvm/scripts/config_ssl.sh

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ help() {
2424
printf " -k path of private key\n"
2525
printf " -p path of certificate of public key\n"
2626
printf " -t path of certificate chain\n"
27+
printf " -u path of root ca certificate \n"
2728
}
2829

2930

@@ -53,6 +54,10 @@ config_apache2_conf() {
5354
sed -i -e "s/NameVirtualHost .*:80/NameVirtualHost $ip:80/g" /etc/apache2/ports.conf
5455
sed -i 's/ssl-cert-snakeoil.key/cert_apache.key/' /etc/apache2/sites-available/default-ssl
5556
sed -i 's/ssl-cert-snakeoil.pem/cert_apache.crt/' /etc/apache2/sites-available/default-ssl
57+
if [ -f /etc/ssl/certs/cert_apache_chain.crt ]
58+
then
59+
sed -i -e "s/#SSLCertificateChainFile.*/SSLCertificateChainFile \/etc\/ssl\/certs\/cert_apache_chain.crt/" /etc/apache2/sites-available/default-ssl
60+
fi
5661
}
5762

5863
copy_certs() {
@@ -88,12 +93,13 @@ cccflag=
8893
customPrivKey=$(dirname $0)/certs/realhostip.key
8994
customPrivCert=$(dirname $0)/certs/realhostip.crt
9095
customCertChain=
96+
customCACert=
9197
publicIp=
9298
hostName=
9399
keyStore=$(dirname $0)/certs/realhostip.keystore
94100
aliasName="CPVMCertificate"
95101
storepass="vmops.com"
96-
while getopts 'i:h:k:p:t:c' OPTION
102+
while getopts 'i:h:k:p:t:u:c' OPTION
97103
do
98104
case $OPTION in
99105
c) cflag=1
@@ -107,6 +113,9 @@ do
107113
t) cccflag=1
108114
customCertChain="$OPTARG"
109115
;;
116+
u) ccacflag=1
117+
customCACert="$OPTARG"
118+
;;
110119
i) publicIp="$OPTARG"
111120
;;
112121
h) hostName="$OPTARG"
@@ -165,10 +174,10 @@ then
165174
exit 2
166175
fi
167176

168-
if [ -f "$customPrivCert" ]
177+
if [ -f "$customCACert" ]
169178
then
170179
keytool -delete -alias $aliasName -keystore $keyStore -storepass $storepass -noprompt
171-
keytool -import -alias $aliasName -keystore $keyStore -storepass $storepass -noprompt -file $customPrivCert
180+
keytool -import -alias $aliasName -keystore $keyStore -storepass $storepass -noprompt -file $customCACert
172181
fi
173182

174183
if [ -d /etc/apache2 ]

ui/scripts/ui-custom/physicalResources.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@
117117
type: "POST",
118118
url: createURL('uploadCustomCertificate'),
119119
data: {
120-
certificate: encodeURIComponent(args.data.certificate),
121-
privatekey: encodeURIComponent(args.data.privatekey),
120+
certificate: args.data.certificate,
121+
privatekey: args.data.privatekey,
122122
domainsuffix: args.data.domainsuffix
123123
},
124124
dataType: 'json',

0 commit comments

Comments
 (0)