weizhouapache commented on code in PR #12911:
URL: https://github.com/apache/cloudstack/pull/12911#discussion_r3008017973


##########
server/src/main/java/org/apache/cloudstack/ca/CAManagerImpl.java:
##########
@@ -200,6 +228,138 @@ public boolean provisionCertificate(final Host host, 
final Boolean reconnect, fi
         }
     }
 
+    private boolean provisionCertificateForced(Host host, Boolean reconnect, 
String caProvider) {
+        if (host.getType() == Host.Type.Routing && host.getHypervisorType() == 
com.cloud.hypervisor.Hypervisor.HypervisorType.KVM) {
+            return provisionKvmHostViaSsh(host);
+        } else if (host.getType() == Host.Type.ConsoleProxy || host.getType() 
== Host.Type.SecondaryStorageVM) {
+            return provisionSystemVmViaSsh(host, reconnect);
+        }
+        throw new CloudRuntimeException("Forced certificate provisioning is 
only supported for KVM hosts and SystemVMs.");
+    }
+
+    @Override
+    public void provisionCertificateViaSsh(final Connection sshConnection, 
final String agentIp, final String agentHostname) {
+        Integer validityPeriod = CAManager.CertValidityPeriod.value();
+        if (validityPeriod < 1) {
+            validityPeriod = 1;
+        }
+
+        String keystorePassword = PasswordGenerator.generateRandomPassword(16);
+
+        // 1. Setup Keystore and Generate CSR
+        final SSHCmdHelper.SSHCmdResult keystoreSetupResult = 
SSHCmdHelper.sshExecuteCmdWithResult(sshConnection,
+                String.format("sudo 
/usr/share/cloudstack-common/scripts/util/%s " +
+                              "/etc/cloudstack/agent/agent.properties " +
+                              "/etc/cloudstack/agent/%s " +
+                              "%s %d " +
+                              "/etc/cloudstack/agent/%s",
+                        KeyStoreUtils.KS_SETUP_SCRIPT,
+                        KeyStoreUtils.KS_FILENAME,
+                        keystorePassword,
+                        validityPeriod,
+                        KeyStoreUtils.CSR_FILENAME));
+
+        if (!keystoreSetupResult.isSuccess()) {
+            throw new CloudRuntimeException("Failed to setup keystore and 
generate CSR via SSH on host: " + agentIp);
+        }
+
+        // 2. Issue Certificate based on returned CSR
+        final String csr = keystoreSetupResult.getStdOut();
+        final Certificate certificate = issueCertificate(csr, 
Arrays.asList(agentHostname, agentIp),
+                Collections.singletonList(agentIp), null, null);
+
+        if (certificate == null || certificate.getClientCertificate() == null) 
{
+            throw new CloudRuntimeException("Failed to issue certificates for 
host: " + agentIp);
+        }
+
+        // 3. Import Certificate into agent keystore
+        final SetupCertificateCommand certificateCommand = new 
SetupCertificateCommand(certificate);
+        final SSHCmdHelper.SSHCmdResult setupCertResult = 
SSHCmdHelper.sshExecuteCmdWithResult(sshConnection,
+                String.format("sudo 
/usr/share/cloudstack-common/scripts/util/%s " +
+                              "/etc/cloudstack/agent/agent.properties %s " +
+                              "/etc/cloudstack/agent/%s %s " +
+                              "/etc/cloudstack/agent/%s \"%s\" " +
+                              "/etc/cloudstack/agent/%s \"%s\" " +
+                              "/etc/cloudstack/agent/%s \"%s\"",
+                        KeyStoreUtils.KS_IMPORT_SCRIPT,
+                        keystorePassword,
+                        KeyStoreUtils.KS_FILENAME,
+                        KeyStoreUtils.SSH_MODE,
+                        KeyStoreUtils.CERT_FILENAME,
+                        certificateCommand.getEncodedCertificate(),
+                        KeyStoreUtils.CACERT_FILENAME,
+                        certificateCommand.getEncodedCaCertificates(),
+                        KeyStoreUtils.PKEY_FILENAME,
+                        certificateCommand.getEncodedPrivateKey()));
+
+        if (!setupCertResult.isSuccess()) {
+            throw new CloudRuntimeException("Failed to import certificates 
into agent keystore via SSH on host: " + agentIp);
+        }
+    }
+
+    private boolean provisionKvmHostViaSsh(Host host) {
+        final HostVO hostVO = (HostVO) host;
+        hostDao.loadDetails(hostVO);
+        String username = hostVO.getDetail(ApiConstants.USERNAME);
+        String password = hostVO.getDetail(ApiConstants.PASSWORD);
+        String hostIp = host.getPrivateIpAddress();
+
+        int port = 
AgentManager.KVMHostDiscoverySshPort.valueIn(host.getClusterId());
+        if (hostVO.getDetail(Host.HOST_SSH_PORT) != null) {
+            port = NumberUtils.toInt(hostVO.getDetail(Host.HOST_SSH_PORT), 
port);
+        }
+
+        Connection sshConnection = null;
+        try {
+            sshConnection = new Connection(hostIp, port);
+            sshConnection.connect(null, 60000, 60000);
+
+            String privateKey = configDao.getValue("ssh.privatekey");
+            if 
(!SSHCmdHelper.acquireAuthorizedConnectionWithPublicKey(sshConnection, 
username, privateKey)) {
+                if (StringUtils.isEmpty(password) || 
!sshConnection.authenticateWithPassword(username, password)) {
+                    throw new CloudRuntimeException("Failed to authenticate to 
host via SSH for forced provisioning: " + hostIp);
+                }
+            }
+
+            provisionCertificateViaSsh(sshConnection, hostIp, host.getName());
+
+            SSHCmdHelper.sshExecuteCmd(sshConnection, "sudo service 
cloudstack-agent restart");

Review Comment:
   1. sudo could be a problem, depends on which user is used on the kvm host. 
please check.
   2. may use `systemctl` instead of `service`, please check and keep 
consistent with other commands.



##########
plugins/ca/root-ca/src/main/java/org/apache/cloudstack/ca/provider/RootCAProvider.java:
##########
@@ -106,17 +106,21 @@ public final class RootCAProvider extends AdapterBase 
implements CAProvider, Con
     private static ConfigKey<String> rootCAPrivateKey = new 
ConfigKey<>("Hidden", String.class,
             "ca.plugin.root.private.key",
             null,
-            "The ROOT CA private key.", true);
+            "The ROOT CA private key in PEM format (PKCS#8: must start with 
'-----BEGIN PRIVATE KEY-----'). " +
+            "When set along with the public key and certificate, CloudStack 
uses this custom CA instead of auto-generating one. " +
+            "All three ca.plugin.root.* keys must be set together. Restart 
management server(s) when changed.", true);
 
     private static ConfigKey<String> rootCAPublicKey = new 
ConfigKey<>("Hidden", String.class,
             "ca.plugin.root.public.key",
             null,
-            "The ROOT CA public key.", true);
+            "The ROOT CA public key in PEM format (X.509/SPKI: must start with 
'-----BEGIN PUBLIC KEY-----'). " +
+            "Required when providing a custom CA. Restart management server(s) 
when changed.", true);
 
     private static ConfigKey<String> rootCACertificate = new 
ConfigKey<>("Hidden", String.class,
             "ca.plugin.root.ca.certificate",
             null,
-            "The ROOT CA certificate.", true);
+            "The ROOT CA X.509 certificate in PEM format (must start with 
'-----BEGIN CERTIFICATE-----'). " +
+            "Required when providing a custom CA. Restart management server(s) 
when changed.", true);

Review Comment:
   test and update description if an intermediate certificate exists ?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to