From 1679d20266b2cc1122a255a7887e9eef324d75ef Mon Sep 17 00:00:00 2001 From: vishesh92 Date: Wed, 25 Mar 2026 14:34:24 +0530 Subject: [PATCH] Add root CA certs to default truststore for MS --- .../org/apache/cloudstack/ca/CAManager.java | 6 +++ .../apache/cloudstack/ca/CAManagerImpl.java | 50 ++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/org/apache/cloudstack/ca/CAManager.java b/api/src/main/java/org/apache/cloudstack/ca/CAManager.java index 2c1f9ae8d20..1abbb9e20e6 100644 --- a/api/src/main/java/org/apache/cloudstack/ca/CAManager.java +++ b/api/src/main/java/org/apache/cloudstack/ca/CAManager.java @@ -88,6 +88,12 @@ public interface CAManager extends CAService, Configurable, PluggableService { "The actual implementation will depend on the configured CA provider.", false); + ConfigKey CaInjectDefaultTruststore = new ConfigKey<>("Advanced", Boolean.class, + "ca.framework.inject.default.truststore", "true", + "When true, injects the CA provider's certificate into the JVM default truststore on management server startup. " + + "This allows outgoing HTTPS connections from the management server to trust servers with certificates signed by the configured CA. " + + "Restart management server(s) when changed.", true); + /** * Returns a list of available CA provider plugins * @return returns list of CAProvider diff --git a/server/src/main/java/org/apache/cloudstack/ca/CAManagerImpl.java b/server/src/main/java/org/apache/cloudstack/ca/CAManagerImpl.java index 2b4e7ddc9d4..fea0e713853 100644 --- a/server/src/main/java/org/apache/cloudstack/ca/CAManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/ca/CAManagerImpl.java @@ -22,6 +22,7 @@ import java.math.BigInteger; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.KeyStoreException; +import java.security.SecureRandom; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; import java.security.cert.CertificateParsingException; @@ -39,6 +40,8 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.ServerApiException; @@ -400,9 +403,54 @@ public class CAManagerImpl extends ManagerBase implements CAManager { logger.error("Failed to find valid configured CA provider, please check!"); return false; } + if (CaInjectDefaultTruststore.value()) { + injectCaCertIntoDefaultTruststore(); + } return true; } + private void injectCaCertIntoDefaultTruststore() { + try { + final List caCerts = configuredCaProvider.getCaCertificate(); + if (caCerts == null || caCerts.isEmpty()) { + logger.debug("No CA certificates found from the configured provider, skipping JVM truststore injection"); + return; + } + + final KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(null, null); + + // Copy existing default trusted certs + final TrustManagerFactory defaultTmf = TrustManagerFactory.getInstance("SunX509"); + defaultTmf.init((KeyStore) null); + final X509TrustManager defaultTm = (X509TrustManager) defaultTmf.getTrustManagers()[0]; + for (final X509Certificate cert : defaultTm.getAcceptedIssuers()) { + final String alias = cert.getSubjectX500Principal().getName(); + trustStore.setCertificateEntry(alias, cert); + } + + // Add CA provider's certificates + int count = 0; + for (final X509Certificate caCert : caCerts) { + final String alias = "cloudstack-ca-" + count; + trustStore.setCertificateEntry(alias, caCert); + count++; + logger.info("Injected CA certificate into JVM default truststore: subject={}, alias={}", + caCert.getSubjectX500Principal().getName(), alias); + } + + // Reinitialize default SSLContext with the updated truststore + final TrustManagerFactory updatedTmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + updatedTmf.init(trustStore); + final SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, updatedTmf.getTrustManagers(), new SecureRandom()); + SSLContext.setDefault(sslContext); + logger.info("Successfully injected {} CA certificate(s) into JVM default truststore", count); + } catch (final GeneralSecurityException | IOException e) { + logger.error("Failed to inject CA certificate into JVM default truststore", e); + } + } + @Override public boolean configure(final String name, final Map params) throws ConfigurationException { backgroundPollManager.submitTask(new CABackgroundTask(this, hostDao)); @@ -433,7 +481,7 @@ public class CAManagerImpl extends ManagerBase implements CAManager { public ConfigKey[] getConfigKeys() { return new ConfigKey[] {CAProviderPlugin, CertKeySize, CertSignatureAlgorithm, CertValidityPeriod, AutomaticCertRenewal, AllowHostIPInSysVMAgentCert, CABackgroundJobDelay, CertExpiryAlertPeriod, - CertManagementCustomSubjectAlternativeName + CertManagementCustomSubjectAlternativeName, CaInjectDefaultTruststore }; }