Skip to content

Commit c23231f

Browse files
committed
Add root CA certs to default truststore for MS
1 parent c901d51 commit c23231f

File tree

2 files changed

+55
-1
lines changed

2 files changed

+55
-1
lines changed

api/src/main/java/org/apache/cloudstack/ca/CAManager.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ public interface CAManager extends CAService, Configurable, PluggableService {
8888
"The actual implementation will depend on the configured CA provider.",
8989
false);
9090

91+
ConfigKey<Boolean> CaInjectDefaultTruststore = new ConfigKey<>("Advanced", Boolean.class,
92+
"ca.framework.inject.default.truststore", "true",
93+
"When true, injects the CA provider's certificate into the JVM default truststore on management server startup. " +
94+
"This allows outgoing HTTPS connections from the management server to trust servers with certificates signed by the configured CA. " +
95+
"Restart management server(s) when changed.", true);
96+
9197
/**
9298
* Returns a list of available CA provider plugins
9399
* @return returns list of CAProvider

server/src/main/java/org/apache/cloudstack/ca/CAManagerImpl.java

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.security.GeneralSecurityException;
2323
import java.security.KeyStore;
2424
import java.security.KeyStoreException;
25+
import java.security.SecureRandom;
2526
import java.security.cert.CertificateExpiredException;
2627
import java.security.cert.CertificateNotYetValidException;
2728
import java.security.cert.CertificateParsingException;
@@ -39,6 +40,8 @@
3940
import javax.naming.ConfigurationException;
4041
import javax.net.ssl.SSLContext;
4142
import javax.net.ssl.SSLEngine;
43+
import javax.net.ssl.TrustManagerFactory;
44+
import javax.net.ssl.X509TrustManager;
4245

4346
import org.apache.cloudstack.api.ApiErrorCode;
4447
import org.apache.cloudstack.api.ServerApiException;
@@ -400,9 +403,54 @@ public boolean start() {
400403
logger.error("Failed to find valid configured CA provider, please check!");
401404
return false;
402405
}
406+
if (CaInjectDefaultTruststore.value()) {
407+
injectCaCertIntoDefaultTruststore();
408+
}
403409
return true;
404410
}
405411

412+
private void injectCaCertIntoDefaultTruststore() {
413+
try {
414+
final List<X509Certificate> caCerts = configuredCaProvider.getCaCertificate();
415+
if (caCerts == null || caCerts.isEmpty()) {
416+
logger.debug("No CA certificates found from the configured provider, skipping JVM truststore injection");
417+
return;
418+
}
419+
420+
final KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
421+
trustStore.load(null, null);
422+
423+
// Copy existing default trusted certs
424+
final TrustManagerFactory defaultTmf = TrustManagerFactory.getInstance("SunX509");
425+
defaultTmf.init((KeyStore) null);
426+
final X509TrustManager defaultTm = (X509TrustManager) defaultTmf.getTrustManagers()[0];
427+
for (final X509Certificate cert : defaultTm.getAcceptedIssuers()) {
428+
final String alias = cert.getSubjectX500Principal().getName();
429+
trustStore.setCertificateEntry(alias, cert);
430+
}
431+
432+
// Add CA provider's certificates
433+
int count = 0;
434+
for (final X509Certificate caCert : caCerts) {
435+
final String alias = "cloudstack-ca-" + count;
436+
trustStore.setCertificateEntry(alias, caCert);
437+
count++;
438+
logger.info("Injected CA certificate into JVM default truststore: subject={}, alias={}",
439+
caCert.getSubjectX500Principal().getName(), alias);
440+
}
441+
442+
// Reinitialize default SSLContext with the updated truststore
443+
final TrustManagerFactory updatedTmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
444+
updatedTmf.init(trustStore);
445+
final SSLContext sslContext = SSLContext.getInstance("TLS");
446+
sslContext.init(null, updatedTmf.getTrustManagers(), new SecureRandom());
447+
SSLContext.setDefault(sslContext);
448+
logger.info("Successfully injected {} CA certificate(s) into JVM default truststore", count);
449+
} catch (final GeneralSecurityException | IOException e) {
450+
logger.error("Failed to inject CA certificate into JVM default truststore", e);
451+
}
452+
}
453+
406454
@Override
407455
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
408456
backgroundPollManager.submitTask(new CABackgroundTask(this, hostDao));
@@ -433,7 +481,7 @@ public String getConfigComponentName() {
433481
public ConfigKey<?>[] getConfigKeys() {
434482
return new ConfigKey<?>[] {CAProviderPlugin, CertKeySize, CertSignatureAlgorithm, CertValidityPeriod,
435483
AutomaticCertRenewal, AllowHostIPInSysVMAgentCert, CABackgroundJobDelay, CertExpiryAlertPeriod,
436-
CertManagementCustomSubjectAlternativeName
484+
CertManagementCustomSubjectAlternativeName, CaInjectDefaultTruststore
437485
};
438486
}
439487

0 commit comments

Comments
 (0)