Refactor ca/cert patching logic
diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineGuru.java b/engine/api/src/main/java/com/cloud/vm/VirtualMachineGuru.java
index 7611df8..d6d7513 100644
--- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineGuru.java
+++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineGuru.java
@@ -20,7 +20,14 @@
 import com.cloud.agent.manager.Commands;
 import com.cloud.deploy.DeployDestination;
 import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.utils.PasswordGenerator;
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.cloudstack.ca.CAManager;
+import org.apache.cloudstack.framework.ca.Certificate;
+import org.apache.cloudstack.utils.security.CertUtils;
+import org.apache.cloudstack.utils.security.KeyStoreUtils;
 
+import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Base64;
 
@@ -72,4 +79,22 @@
         }
         return base64EncodedPublicKey;
     }
+
+    private static String getEncodedString(String certificate) {
+        return Base64.getEncoder().encodeToString(certificate.replace("\n", KeyStoreUtils.CERT_NEWLINE_ENCODER).replace(" ", KeyStoreUtils.CERT_SPACE_ENCODER).getBytes(StandardCharsets.UTF_8));
+    }
+
+    static void appendCertificateDetails(StringBuilder buf, Certificate certificate) {
+        try {
+            buf.append(" certificate=").append(getEncodedString(CertUtils.x509CertificateToPem(certificate.getClientCertificate())));
+            buf.append(" cacertificate=").append(getEncodedString(CertUtils.x509CertificatesToPem(certificate.getCaCertificates())));
+            if (certificate.getPrivateKey() != null) {
+                buf.append(" privatekey=").append(getEncodedString(CertUtils.privateKeyToPem(certificate.getPrivateKey())));
+            }
+        } catch (IOException e) {
+            throw new CloudRuntimeException("Failed to transform X509 cert to PEM format", e);
+        }
+        buf.append(" keystore_password=").append(getEncodedString(PasswordGenerator.generateRandomPassword(16)));
+        buf.append(" validity=").append(CAManager.CertValidityPeriod.value());
+    }
 }
diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
index da70408..4e4a25d 100755
--- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -1008,9 +1008,6 @@
         final String csr = caManager.generateKeyStoreAndCsr(vmHost, sshAccessDetails);
         if (!Strings.isNullOrEmpty(csr)) {
             final Map<String, String> ipAddressDetails = new HashMap<>(sshAccessDetails);
-            for (Map.Entry<String,String> e : ipAddressDetails.entrySet()) {
-                s_logger.info("PEARL - k = " + e.getKey() + " v: "+ e.getValue());
-            }
             ipAddressDetails.remove(NetworkElementCommand.ROUTER_NAME);
             final Certificate certificate = caManager.issueCertificate(csr, Arrays.asList(vm.getHostName(), vm.getInstanceName()),
                     new ArrayList<>(ipAddressDetails.values()), CAManager.CertValidityPeriod.value(), null);
@@ -1278,20 +1275,6 @@
                             if (s_logger.isDebugEnabled()) {
                                 s_logger.debug("Start completed for VM " + vm);
                             }
-                            final Host vmHost = _hostDao.findById(destHostId);
-                            if (vmHost != null && (VirtualMachine.Type.ConsoleProxy.equals(vm.getType()) ||
-                                    VirtualMachine.Type.SecondaryStorageVm.equals(vm.getType())) && caManager.canProvisionCertificates()) {
-                                final Map<String, String> sshAccessDetails = _networkMgr.getSystemVMAccessDetails(vm);
-                                for (int retries = 3; retries > 0; retries--) {
-                                    try {
-                                        setupAgentSecurity(vmHost, sshAccessDetails, vm);
-                                        return;
-                                    } catch (final Exception e) {
-                                        s_logger.error("Retrying after catching exception while trying to secure agent for systemvm id=" + vm.getId(), e);
-                                    }
-                                }
-                                throw new CloudRuntimeException("Failed to setup and secure agent for systemvm id=" + vm.getId());
-                            }
                             return;
                         } else {
                             if (s_logger.isDebugEnabled()) {
diff --git a/scripts/util/keystore-cert-import b/scripts/util/keystore-cert-import
index a2b57bf..8e63e95 100755
--- a/scripts/util/keystore-cert-import
+++ b/scripts/util/keystore-cert-import
@@ -17,19 +17,39 @@
 # under the License.
 
 PROPS_FILE="$1"
-KS_FILE="$2"
-MODE="$3"
-CERT_FILE="$4"
-CERT=$(echo "$5" | tr '^' '\n' | tr '~' ' ')
-CACERT_FILE="$6"
-CACERT=$(echo "$7" | tr '^' '\n' | tr '~' ' ')
-PRIVKEY_FILE="$8"
-PRIVKEY=$(echo "$9" | tr '^' '\n' | tr '~' ' ')
+KS_PASS="$2"
+KS_VALIDITY="$3"
+KS_FILE="$4"
+MODE="$5"
+CERT_FILE="$6"
+CERT=$(echo "$7" | tr '^' '\n' | tr '~' ' ')
+CACERT_FILE="$8"
+CACERT=$(echo "$9" | tr '^' '\n' | tr '~' ' ')
+PRIVKEY_FILE="${10}"
+PRIVKEY=$(echo "${11}" | tr '^' '\n' | tr '~' ' ')
 
 ALIAS="cloud"
 SYSTEM_FILE="/var/cache/cloud/cmdline"
 LIBVIRTD_FILE="/etc/libvirt/libvirtd.conf"
 
+# Re-use existing password or use the one provided
+if [ -f "$PROPS_FILE" ]; then
+    OLD_PASS=$(sed -n '/keystore.passphrase/p' "$PROPS_FILE" 2>/dev/null  | sed 's/keystore.passphrase=//g' 2>/dev/null)
+    if [ ! -z "${OLD_PASS// }" ]; then
+        KS_PASS="$OLD_PASS"
+    else
+        sed -i "/keystore.passphrase.*/d" $PROPS_FILE 2> /dev/null || true
+        echo "keystore.passphrase=$KS_PASS" >> $PROPS_FILE
+    fi
+fi
+
+if [ -f "$KS_FILE" ]; then
+    keytool -delete -noprompt -alias "$ALIAS" -keystore "$KS_FILE" -storepass "$KS_PASS" > /dev/null 2>&1 || true
+fi
+
+CN=$(hostname --fqdn)
+keytool -genkey -storepass "$KS_PASS" -keypass "$KS_PASS" -alias "$ALIAS" -keyalg RSA -validity "$KS_VALIDITY" -dname cn="$CN",ou="cloudstack",o="cloudstack",c="cloudstack" -keystore "$KS_FILE" > /dev/null 2>&1
+
 # Find keystore password
 KS_PASS=$(sed -n '/keystore.passphrase/p' "$PROPS_FILE" 2>/dev/null  | sed 's/keystore.passphrase=//g' 2>/dev/null)
 
@@ -56,11 +76,6 @@
 done
 rm -f cloudca.*
 
-# Stop cloud service in systemvm
-if [ "$MODE" == "ssh" ] && [ -f $SYSTEM_FILE ]; then
-    systemctl stop cloud > /dev/null 2>&1
-fi
-
 # Import private key if available
 if [ ! -z "${PRIVKEY// }" ]; then
     echo "$PRIVKEY" > "$PRIVKEY_FILE"
@@ -99,10 +114,6 @@
     chmod 644 /usr/local/share/ca-certificates/cloudstack/ca.crt
     update-ca-certificates > /dev/null 2>&1 || true
 
-    # Ensure cloud service is running in systemvm
-    if [ "$MODE" == "ssh" ]; then
-        systemctl start cloud > /dev/null 2>&1
-    fi
 fi
 
 # Fix file permission
diff --git a/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java
index 1dcaf7b..40c6363 100644
--- a/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java
+++ b/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java
@@ -29,11 +29,11 @@
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
-import com.cloud.utils.PasswordGenerator;
 import org.apache.cloudstack.agent.lb.IndirectAgentLB;
 import org.apache.cloudstack.ca.CAManager;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
+import org.apache.cloudstack.framework.ca.Certificate;
 import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.config.Configurable;
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
@@ -1210,8 +1210,11 @@
 
     @Override
     public boolean finalizeVirtualMachineProfile(VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) {
-//        final Certificate certificate = caManager.issueCertificate(null, Arrays.asList(profile.getHostName(), profile.getInstanceName()),
-//                new ArrayList<>(ipAddressDetails.values()), CAManager.CertValidityPeriod.value(), null);
+        final Map<String, String> sshAccessDetails = networkMgr.getSystemVMAccessDetails(profile.getVirtualMachine());
+        final Map<String, String> ipAddressDetails = new HashMap<>(sshAccessDetails);
+        ipAddressDetails.remove("router.name");
+        final Certificate certificate = caManager.issueCertificate(null, Arrays.asList(profile.getHostName(), profile.getInstanceName()),
+                new ArrayList<>(ipAddressDetails.values()), CAManager.CertValidityPeriod.value(), null);
         ConsoleProxyVO vm = consoleProxyDao.findById(profile.getId());
         Map<String, String> details = userVmDetailsDao.listDetailsKeyPairs(vm.getId());
         vm.setDetails(details);
@@ -1281,17 +1284,7 @@
             buf.append(" dns2=").append(dc.getDns2());
         }
 
-//        try {
-//            buf.append(" certificate=").append(CertUtils.x509CertificateToPem(certificate.getClientCertificate()));
-//            buf.append(" cacertificate=").append(CertUtils.x509CertificatesToPem(certificate.getCaCertificates()));
-//            if (certificate.getPrivateKey() != null) {
-//                buf.append(" privatekey=").append(CertUtils.privateKeyToPem(certificate.getPrivateKey()));
-//            }
-//        } catch (IOException e) {
-//            throw new CloudRuntimeException("Failed to transform X509 cert to PEM format", e);
-//        }
-        buf.append(" keystore_password=").append(PasswordGenerator.generateRandomPassword(16));
-        buf.append(" validity=").append(CAManager.CertValidityPeriod.value());
+        VirtualMachineGuru.appendCertificateDetails(buf, certificate);
         String bootArgs = buf.toString();
         if (s_logger.isDebugEnabled()) {
             s_logger.debug("Boot Args for " + profile + ": " + bootArgs);
diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxy.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxy.java
index 702e9a8..53b2b99 100644
--- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxy.java
+++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxy.java
@@ -16,6 +16,15 @@
 // under the License.
 package com.cloud.consoleproxy;
 
+import com.cloud.consoleproxy.util.Logger;
+import com.cloud.utils.PropertiesUtil;
+import com.cloud.utils.StringUtils;
+import com.google.gson.Gson;
+import com.sun.net.httpserver.HttpServer;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.log4j.xml.DOMConfigurator;
+import org.eclipse.jetty.websocket.api.Session;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -31,15 +40,6 @@
 import java.util.Properties;
 import java.util.concurrent.Executor;
 
-import com.cloud.utils.StringUtils;
-import org.apache.log4j.xml.DOMConfigurator;
-import org.eclipse.jetty.websocket.api.Session;
-
-import com.cloud.consoleproxy.util.Logger;
-import com.cloud.utils.PropertiesUtil;
-import com.google.gson.Gson;
-import com.sun.net.httpserver.HttpServer;
-
 /**
  *
  * ConsoleProxy, singleton class that manages overall activities in console proxy process. To make legacy code work, we still
@@ -74,6 +74,7 @@
     static boolean standaloneStart = false;
 
     static String encryptorPassword = "Dummy";
+    static final String[] skipProperties = new String[]{"certificate", "cacertificate", "keystore_password", "privatekey"};
 
     private static void configLog4j() {
         final ClassLoader loader = Thread.currentThread().getContextClassLoader();
@@ -108,7 +109,9 @@
     private static void configProxy(Properties conf) {
         s_logger.info("Configure console proxy...");
         for (Object key : conf.keySet()) {
-            s_logger.info("Property " + (String)key + ": " + conf.getProperty((String)key));
+            if (!ArrayUtils.contains(skipProperties, key)) {
+                s_logger.info("Property " + (String)key + ": " + conf.getProperty((String)key));
+            }
         }
 
         String s = conf.getProperty("consoleproxy.httpListenPort");
@@ -247,7 +250,9 @@
 
         if (conf != null) {
             for (Object key : conf.keySet()) {
-                s_logger.info("Context property " + (String)key + ": " + conf.getProperty((String)key));
+                if (!ArrayUtils.contains(skipProperties, key)) {
+                    s_logger.info("Context property " + (String) key + ": " + conf.getProperty((String) key));
+                }
             }
         }
 
diff --git a/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java b/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java
index 99539a2..ad74838 100644
--- a/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java
+++ b/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java
@@ -31,11 +31,13 @@
 import javax.naming.ConfigurationException;
 
 import org.apache.cloudstack.agent.lb.IndirectAgentLB;
+import org.apache.cloudstack.ca.CAManager;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
 import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
+import org.apache.cloudstack.framework.ca.Certificate;
 import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.config.Configurable;
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
@@ -245,6 +247,8 @@
     private ImageStoreDetailsUtil imageStoreDetailsUtil;
     @Inject
     private IndirectAgentLB indirectAgentLB;
+    @Inject
+    private CAManager caManager;
 
     private long _capacityScanInterval = DEFAULT_CAPACITY_SCAN_INTERVAL_IN_MILLISECONDS;
     private int _secStorageVmMtuSize;
@@ -1072,6 +1076,12 @@
             return false;
         }
 
+        final Map<String, String> sshAccessDetails = _networkMgr.getSystemVMAccessDetails(profile.getVirtualMachine());
+        final Map<String, String> ipAddressDetails = new HashMap<>(sshAccessDetails);
+        ipAddressDetails.remove("router.name");
+        final Certificate certificate = caManager.issueCertificate(null, Arrays.asList(profile.getHostName(), profile.getInstanceName()),
+                new ArrayList<>(ipAddressDetails.values()), CAManager.CertValidityPeriod.value(), null);
+
         StringBuilder buf = profile.getBootArgsBuilder();
         buf.append(" template=domP type=secstorage");
         buf.append(" host=").append(StringUtils.toCSVList(indirectAgentLB.getManagementServerList(dest.getHost().getId(), dest.getDataCenter().getId(), null)));
@@ -1157,6 +1167,7 @@
         String nfsVersion = imageStoreDetailsUtil != null ? imageStoreDetailsUtil.getNfsVersion(secStore.getId()) : null;
         buf.append(" nfsVersion=").append(nfsVersion);
 
+        VirtualMachineGuru.appendCertificateDetails(buf, certificate);
         String bootArgs = buf.toString();
         if (s_logger.isDebugEnabled()) {
             s_logger.debug(String.format("Boot args for machine profile [%s]: [%s].", profile.toString(), bootArgs));
diff --git a/systemvm/debian/opt/cloud/bin/setup/cloud-early-config b/systemvm/debian/opt/cloud/bin/setup/cloud-early-config
index 5a50fe8..15da63e 100755
--- a/systemvm/debian/opt/cloud/bin/setup/cloud-early-config
+++ b/systemvm/debian/opt/cloud/bin/setup/cloud-early-config
@@ -55,7 +55,7 @@
 
   eval $(validate_checksums $md5file $oldpatchfile)
   if [ "$oldmd5" == "$newmd5" ] && [ -d /usr/local/cloud/systemvm ] && [ "$(ls -A /usr/local/cloud/systemvm)" ]; then
-    log_it "Checksum matches, do need to patch"
+    log_it "Checksum matches, no need to patch"
     return 0
   fi
 
diff --git a/systemvm/debian/opt/cloud/bin/setup/common.sh b/systemvm/debian/opt/cloud/bin/setup/common.sh
index 75c8f3c..be0b1c4 100755
--- a/systemvm/debian/opt/cloud/bin/setup/common.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/common.sh
@@ -593,6 +593,17 @@
    fi
 }
 
+setup_certificates() {
+  certificate=$(echo "$CERTIFICATE" | base64 -d)
+  cacertificate=$(echo "$CACERTIFICATE" | base64 -d)
+  privatekey=$(echo "$PRIVATEKEY" | base64 -d)
+  kspass=$(echo "$KEYSTORE_PSSWD"| base64 -d)
+  ksvalidity="$KS_VALIDITY"
+  /opt/cloud/bin/keystore-cert-import /usr/local/cloud/systemvm/conf/agent.properties $kspass $ksvalidity \
+      /usr/local/cloud/systemvm/conf/cloud.jks ssh /usr/local/cloud/systemvm/conf/cloud.crt \
+      $certificate /usr/local/cloud/systemvm/conf/cloud.ca.crt $cacertificate /usr/local/cloud/systemvm/conf/cloud.key $privatekey
+}
+
 parse_cmd_line() {
   CMDLINE=$(cat /var/cache/cloud/cmdline)
   TYPE="unknown"
@@ -766,7 +777,16 @@
             export KEYSTORE_PSSWD=$VALUE
             ;;
         validity)
-          export VALIDITY=$VALUE
+          export KS_VALIDITY=$VALUE
+          ;;
+        certificate)
+          export CERTIFICATE=$VALUE
+          ;;
+        cacertificate)
+          export CACERTIFICATE=$VALUE
+          ;;
+        privatekey)
+          export PRIVATEKEY=$VALUE
           ;;
       esac
   done
diff --git a/systemvm/debian/opt/cloud/bin/setup/consoleproxy.sh b/systemvm/debian/opt/cloud/bin/setup/consoleproxy.sh
index ec45b7f..324f92e 100755
--- a/systemvm/debian/opt/cloud/bin/setup/consoleproxy.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/consoleproxy.sh
@@ -36,6 +36,8 @@
   enable_fwding 0
   enable_irqbalance 0
   rm -f /etc/logrotate.d/cloud
+
+  setup_certificates
 }
 
 setup_console_proxy
diff --git a/systemvm/debian/opt/cloud/bin/setup/secstorage.sh b/systemvm/debian/opt/cloud/bin/setup/secstorage.sh
index 3b21ed5..a34e671 100755
--- a/systemvm/debian/opt/cloud/bin/setup/secstorage.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/secstorage.sh
@@ -66,6 +66,7 @@
   setup_ntp
 
   rm -f /etc/logrotate.d/cloud
+  setup_certificates
 }
 
 setup_secstorage