diff --git a/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java b/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java
index 9f645f8f479..68c1bdce57a 100644
--- a/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java
+++ b/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java
@@ -20,7 +20,6 @@ package com.cloud.agent.resource.consoleproxy;
import java.io.BufferedReader;
import java.io.BufferedWriter;
-import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
@@ -52,7 +51,7 @@ import com.cloud.agent.api.StartupCommand;
import com.cloud.agent.api.StartupProxyCommand;
import com.cloud.agent.api.proxy.CheckConsoleProxyLoadCommand;
import com.cloud.agent.api.proxy.ConsoleProxyLoadAnswer;
-import com.cloud.agent.api.proxy.UpdateCertificateCommand;
+import com.cloud.agent.api.proxy.StartConsoleProxyAgentHttpHandlerCommand;
import com.cloud.agent.api.proxy.WatchConsoleProxyLoadCommand;
import com.cloud.exception.AgentControlChannelException;
import com.cloud.host.Host;
@@ -99,68 +98,17 @@ public class ConsoleProxyResource extends ServerResourceBase implements ServerRe
return new ReadyAnswer((ReadyCommand)cmd);
} else if(cmd instanceof CheckHealthCommand) {
return new CheckHealthAnswer((CheckHealthCommand)cmd, true);
- } else if(cmd instanceof UpdateCertificateCommand) {
- return execute((UpdateCertificateCommand)cmd);
- }
- else {
+ } else if(cmd instanceof StartConsoleProxyAgentHttpHandlerCommand) {
+ return execute((StartConsoleProxyAgentHttpHandlerCommand)cmd);
+ } else {
return Answer.createUnsupportedCommandAnswer(cmd);
}
}
- protected Answer execute(final UpdateCertificateCommand cmd) {
- boolean success = false;
- String errorStr = null;
- String successStr = null;
- try
- {
- String certificate = cmd.getCertificate();
- //write the cert to /etc/cloud/consoleproxy/cert/
- boolean dirCreated = false;
- boolean dirExists = false;
- boolean forNewProxy = cmd.isForNewProxy();
- String strDirectory = "/etc/cloud/consoleproxy/cert/";
- String filePath = "/etc/cloud/consoleproxy/cert/customcert";
- if(forNewProxy){
- dirCreated = (new File(strDirectory)).mkdirs();
- if(s_logger.isDebugEnabled())
- s_logger.debug("Directory: " + strDirectory + " created");
- if(dirCreated){
- success = copyCertToDirectory(certificate, filePath);
- successStr = "Successfully created cert at /etc/cloud/consoleproxy/cert/ from the listener flow for new console proxy starting up";
- }
- }
- else{
- File dir = new File(strDirectory);
- dirExists = dir.exists();
- if(!dirExists){
- dirCreated = (new File(strDirectory)).mkdirs();
- if(s_logger.isDebugEnabled())
- s_logger.debug("Directory: " + strDirectory + " created");
- }
- if (dirExists || dirCreated)
- {
- success = copyCertToDirectory(certificate, filePath);
- successStr = "Successfully created cert at /etc/cloud/consoleproxy/cert/ from the UploadCustomCert cmd flow for existing console proxy";
- }
- }
- }catch (SecurityException se){
- errorStr = "Unable to upload cert in console proxy resource due to directory creation failure";
- s_logger.error(errorStr,se);
- success = false;
- }catch (IOException ioe){
- errorStr = "Unable to write cert to the location /etc/cloud/consoleproxy/cert/ ";
- s_logger.error(errorStr,ioe);
- success = false;
- }
- catch (Exception e)
- {
- errorStr = "Unable to upload cert in console proxy resource";
- s_logger.error(errorStr,e);
- success = false;
- }
-
- return new Answer(cmd, success, errorStr!=null?errorStr:successStr);
- }
+ private Answer execute(StartConsoleProxyAgentHttpHandlerCommand cmd) {
+ launchConsoleProxy(cmd.getKeystoreBits(), cmd.getKeystorePassword());
+ return new Answer(cmd);
+ }
private void disableRpFilter() {
try {
@@ -321,7 +269,6 @@ public class ConsoleProxyResource extends ServerResourceBase implements ServerRe
if(s_logger.isInfoEnabled())
s_logger.info("Receive proxyVmId in ConsoleProxyResource configuration as " + _proxyVmId);
- launchConsoleProxy();
return true;
}
@@ -369,7 +316,7 @@ public class ConsoleProxyResource extends ServerResourceBase implements ServerRe
return _name;
}
- private void launchConsoleProxy() {
+ private void launchConsoleProxy(final byte[] ksBits, final String ksPassword) {
final Object resource = this;
_consoleProxyMain = new Thread(new Runnable() {
@@ -377,8 +324,8 @@ public class ConsoleProxyResource extends ServerResourceBase implements ServerRe
try {
Class> consoleProxyClazz = Class.forName("com.cloud.consoleproxy.ConsoleProxy");
try {
- Method method = consoleProxyClazz.getMethod("startWithContext", Properties.class, Object.class);
- method.invoke(null, _properties, resource);
+ Method method = consoleProxyClazz.getMethod("startWithContext", Properties.class, Object.class, byte[].class, String.class);
+ method.invoke(null, _properties, resource, ksBits, ksPassword);
} catch (SecurityException e) {
s_logger.error("Unable to launch console proxy due to SecurityException");
System.exit(ExitStatus.Error.value());
diff --git a/api/src/com/cloud/agent/api/proxy/StartConsoleProxyAgentHttpHandlerCommand.java b/api/src/com/cloud/agent/api/proxy/StartConsoleProxyAgentHttpHandlerCommand.java
new file mode 100644
index 00000000000..f2a7ad14906
--- /dev/null
+++ b/api/src/com/cloud/agent/api/proxy/StartConsoleProxyAgentHttpHandlerCommand.java
@@ -0,0 +1,38 @@
+package com.cloud.agent.api.proxy;
+
+import com.cloud.agent.api.Command;
+
+public class StartConsoleProxyAgentHttpHandlerCommand extends Command {
+ private byte[] keystoreBits;
+ private String keystorePassword;
+
+ public StartConsoleProxyAgentHttpHandlerCommand() {
+ super();
+ }
+
+ public StartConsoleProxyAgentHttpHandlerCommand(byte[] ksBits, String ksPassword) {
+ this.keystoreBits = ksBits;
+ this.keystorePassword = ksPassword;
+ }
+
+ @Override
+ public boolean executeInSequence() {
+ return true;
+ }
+
+ public byte[] getKeystoreBits() {
+ return keystoreBits;
+ }
+
+ public void setKeystoreBits(byte[] keystoreBits) {
+ this.keystoreBits = keystoreBits;
+ }
+
+ public String getKeystorePassword() {
+ return keystorePassword;
+ }
+
+ public void setKeystorePassword(String keystorePassword) {
+ this.keystorePassword = keystorePassword;
+ }
+}
diff --git a/api/src/com/cloud/agent/api/proxy/UpdateCertificateCommand.java b/api/src/com/cloud/agent/api/proxy/UpdateCertificateCommand.java
deleted file mode 100644
index daddef0b3ab..00000000000
--- a/api/src/com/cloud/agent/api/proxy/UpdateCertificateCommand.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
- *
- * This software is licensed under the GNU General Public License v3 or later.
- *
- * It is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- */
-
-package com.cloud.agent.api.proxy;
-
-
-/**
- * UpdateCertificateCommand implements one-shot updation of the certificate to be applied by the user
- */
-public class UpdateCertificateCommand extends ProxyCommand {
-
- private String certificate; //certificate to be applied
- private boolean forNewProxy; //denotes if this is called from the listener flow
-
- public UpdateCertificateCommand() {
- this.forNewProxy = false;
- }
-
- public UpdateCertificateCommand(String certificate, boolean forNewProxy) {
- this.certificate = certificate;
- this.forNewProxy = forNewProxy;
- }
-
- public String getCertificate() {
- return this.certificate;
- }
-
- public boolean isForNewProxy() {
- return forNewProxy;
- }
-
- public void setForNewProxy(boolean forNewProxy) {
- this.forNewProxy = forNewProxy;
- }
-
- @Override
- public boolean executeInSequence() {
- return false;
- }
-}
diff --git a/api/src/com/cloud/api/ApiConstants.java b/api/src/com/cloud/api/ApiConstants.java
index 619500f17b2..464b62e0959 100755
--- a/api/src/com/cloud/api/ApiConstants.java
+++ b/api/src/com/cloud/api/ApiConstants.java
@@ -31,6 +31,8 @@ public class ApiConstants {
public static final String BOOTABLE = "bootable";
public static final String CATEGORY = "category";
public static final String CERTIFICATE = "certificate";
+ public static final String PRIVATE_KEY = "privatekey";
+ public static final String DOMAIN_SUFFIX = "domainsuffix";
public static final String CIDR = "cidr";
public static final String CIDR_LIST = "cidrlist";
public static final String CLEANUP = "cleanup";
diff --git a/api/src/com/cloud/api/commands/UploadCustomCertificateCmd.java b/api/src/com/cloud/api/commands/UploadCustomCertificateCmd.java
index 2bf9da965e7..1db7fcb7901 100644
--- a/api/src/com/cloud/api/commands/UploadCustomCertificateCmd.java
+++ b/api/src/com/cloud/api/commands/UploadCustomCertificateCmd.java
@@ -38,9 +38,23 @@ public class UploadCustomCertificateCmd extends BaseAsyncCmd {
@Parameter(name=ApiConstants.CERTIFICATE,type=CommandType.STRING,required=true,description="the custom cert to be uploaded")
private String certificate;
+ @Parameter(name=ApiConstants.PRIVATE_KEY,type=CommandType.STRING,required=true,description="the private key for the certificate")
+ private String privateKey;
+
+ @Parameter(name=ApiConstants.DOMAIN_SUFFIX,type=CommandType.STRING,required=true,description="DNS domain suffix that the certificate is granted for")
+ private String domainSuffix;
+
public String getCertificate() {
return certificate;
}
+
+ public String getPrivateKey() {
+ return privateKey;
+ }
+
+ public String getDomainSuffix() {
+ return domainSuffix;
+ }
@Override
public String getEventType() {
diff --git a/console-proxy/scripts/_run.sh b/console-proxy/scripts/_run.sh
index c14f29f414c..2a76271fd54 100755
--- a/console-proxy/scripts/_run.sh
+++ b/console-proxy/scripts/_run.sh
@@ -60,10 +60,4 @@ then
maxmem=$eightypcnt
fi
-EXTRA=
-if [ -f certs/realhostip.keystore ]
-then
- EXTRA="-Djavax.net.ssl.trustStore=$(dirname $0)/certs/realhostip.keystore -Djavax.net.ssl.trustStorePassword=vmops.com"
-fi
-
-java -mx${maxmem}m ${EXTRA} -cp $CP com.cloud.agent.AgentShell $keyvalues $@
+java -mx${maxmem}m -cp $CP com.cloud.agent.AgentShell $keyvalues $@
diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java
index de1d1a7a346..21ac9d54c57 100644
--- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java
+++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java
@@ -45,15 +45,17 @@ import com.sun.net.httpserver.HttpServer;
public class ConsoleProxy {
private static final Logger s_logger = Logger.getLogger(ConsoleProxy.class);
-/*
- private static final int MAX_STRONG_TEMPLATE_CACHE_SIZE = 100;
- private static final int MAX_SOFT_TEMPLATE_CACHE_SIZE = 100;
-*/
public static final int KEYBOARD_RAW = 0;
public static final int KEYBOARD_COOKED = 1;
public static Object context;
+
+ // this has become more ugly, to store keystore info passed from management server (we now use management server managed keystore to support
+ // dynamically changing to customer supplied certificate)
+ public static byte[] ksBits;
+ public static String ksPassword;
+
public static Method authMethod;
public static Method reportMethod;
public static Method ensureRouteMethod;
@@ -165,8 +167,9 @@ public class ConsoleProxy {
try {
Class> clz = Class.forName(factoryClzName);
try {
- return (ConsoleProxyServerFactory)clz.newInstance();
-
+ ConsoleProxyServerFactory factory = (ConsoleProxyServerFactory)clz.newInstance();
+ factory.init(ConsoleProxy.ksBits, ConsoleProxy.ksPassword);
+ return factory;
} catch (InstantiationException e) {
s_logger.error(e.getMessage(), e);
return null;
@@ -237,7 +240,7 @@ public class ConsoleProxy {
}
}
- public static void startWithContext(Properties conf, Object context) {
+ public static void startWithContext(Properties conf, Object context, byte[] ksBits, String ksPassword) {
s_logger.info("Start console proxy with context");
if(conf != null) {
for(Object key : conf.keySet()) {
@@ -250,6 +253,8 @@ public class ConsoleProxy {
// Using reflection to setup private/secure communication channel towards management server
ConsoleProxy.context = context;
+ ConsoleProxy.ksBits = ksBits;
+ ConsoleProxy.ksPassword = ksPassword;
try {
Class> contextClazz = Class.forName("com.cloud.agent.resource.consoleproxy.ConsoleProxyResource");
authMethod = contextClazz.getDeclaredMethod("authenticateConsoleAccess", String.class, String.class, String.class, String.class, String.class);
diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java
index 22e1a374de7..abfa896c39d 100644
--- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java
+++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java
@@ -18,23 +18,29 @@
package com.cloud.consoleproxy;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-
-import javax.net.ssl.SSLServerSocket;
-
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import javax.net.ssl.SSLServerSocket;
+
import com.cloud.console.Logger;
-import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.HttpServer;
public class ConsoleProxyBaseServerFactoryImpl implements ConsoleProxyServerFactory {
private static final Logger s_logger = Logger.getLogger(ConsoleProxyBaseServerFactoryImpl.class);
+
+ @Override
+ public void init(byte[] ksBits, String ksPassword) {
+ }
+ @Override
public HttpServer createHttpServerInstance(int port) throws IOException {
if(s_logger.isInfoEnabled())
s_logger.info("create HTTP server instance at port: " + port);
return HttpServer.create(new InetSocketAddress(port), 5);
}
+ @Override
public SSLServerSocket createSSLServerSocket(int port) throws IOException {
if(s_logger.isInfoEnabled())
s_logger.info("SSL server socket is not supported in ConsoleProxyBaseServerFactoryImpl");
diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java
index eee9edfbe69..2d00dab7698 100644
--- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java
+++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java
@@ -18,17 +18,10 @@
package com.cloud.consoleproxy;
-import java.io.BufferedInputStream;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
-import java.security.Key;
import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
@@ -49,74 +42,62 @@ public class ConsoleProxySecureServerFactoryImpl implements ConsoleProxyServerFa
private SSLContext sslContext = null;
- public ConsoleProxySecureServerFactoryImpl() {
- try {
- s_logger.info("Start initializing SSL");
-
- char[] passphrase = "vmops.com".toCharArray();
- KeyStore ks = KeyStore.getInstance("JKS");
- ks.load(ConsoleProxy.class.getResourceAsStream("/realhostip.keystore"), passphrase);
- //custom cert logic begins //
- try {
- //check if there is any custom cert added at /etc/cloud/consoleproxy/cert/
- String certPath = "/etc/cloud/consoleproxy/cert/customcert";
- //now generate a cert
- FileInputStream fis = new FileInputStream(certPath);
- BufferedInputStream bis = new BufferedInputStream(fis);
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
-
- while (bis.available() > 1) {
- Certificate cert = cf.generateCertificate(bis);
- if(s_logger.isDebugEnabled()){
- s_logger.debug("The custom certificate generated is:"+cert.toString());
- }
- //get the existing cert chain
- Certificate[] chain = ks.getCertificateChain("realhostip");
- Certificate[] newChain = new Certificate[chain.length+1];
- newChain[0] = cert;//make custom cert the default
- System.arraycopy(chain, 0, newChain, 1, chain.length);
- Key key = ks.getKey("realhostip", passphrase);
- ks.setKeyEntry("realhostip", key, passphrase, newChain);
- if(s_logger.isDebugEnabled())
- s_logger.debug("Custom SSL cert added successfully to the keystore cert chain");
- }
- } catch (FileNotFoundException fnf) {
- if(s_logger.isDebugEnabled())
- s_logger.debug("Unable to find the custom cert file at /etc/cloud/consoleproxy/cert/customcert",fnf);
- } catch (IOException ioe){
- if(s_logger.isDebugEnabled())
- s_logger.debug("Unable to read the custom cert file at /etc/cloud/consoleproxy/cert/customcert",ioe);
- }catch (KeyStoreException kse){
- if(s_logger.isDebugEnabled())
- s_logger.debug("Unable to add custom cert file at /etc/cloud/consoleproxy/cert/customcert to the keystore",kse);
- }catch (CertificateException ce){
- if(s_logger.isDebugEnabled())
- s_logger.debug("Unable to generate certificate from the file /etc/cloud/consoleproxy/cert/customcert",ce);
- }catch (Exception e){
- //catch other excpns
- if(s_logger.isDebugEnabled())
- s_logger.debug("Unable to add custom cert file at /etc/cloud/consoleproxy/cert/customcert to the keystore",e);
- }
- //custom cert logic ends //
-
- s_logger.info("SSL certificate loaded");
-
- KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
- kmf.init(ks, passphrase);
- s_logger.info("Key manager factory is initialized");
-
- TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
- tmf.init(ks);
- s_logger.info("Trust manager factory is initialized");
-
- sslContext = SSLContext.getInstance("TLS");
- sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
- s_logger.info("SSL context is initialized");
- } catch (Exception ioe) {
- s_logger.error(ioe.toString(), ioe);
- }
+ public ConsoleProxySecureServerFactoryImpl() {
}
-
+
+ @Override
+ public void init(byte[] ksBits, String ksPassword) {
+ s_logger.info("Start initializing SSL");
+
+ if(ksBits == null) {
+ try {
+ s_logger.info("Initializing SSL from built-in default certificate");
+
+ char[] passphrase = "vmops.com".toCharArray();
+ KeyStore ks = KeyStore.getInstance("JKS");
+ ks.load(ConsoleProxy.class.getResourceAsStream("/realhostip.keystore"), passphrase);
+
+ s_logger.info("SSL certificate loaded");
+
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(ks, passphrase);
+ s_logger.info("Key manager factory is initialized");
+
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+ tmf.init(ks);
+ s_logger.info("Trust manager factory is initialized");
+
+ sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ s_logger.info("SSL context is initialized");
+ } catch (Exception ioe) {
+ s_logger.error(ioe.toString(), ioe);
+ }
+ } else {
+ char[] passphrase = ksPassword != null ? ksPassword.toCharArray() : null;
+ try {
+ s_logger.info("Initializing SSL from passed-in certificate");
+
+ KeyStore ks = KeyStore.getInstance("JKS");
+ ks.load(new ByteArrayInputStream(ksBits), passphrase);
+
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(ks, passphrase);
+ s_logger.info("Key manager factory is initialized");
+
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+ tmf.init(ks);
+ s_logger.info("Trust manager factory is initialized");
+
+ sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ s_logger.info("SSL context is initialized");
+ } catch(Exception e) {
+ s_logger.error("Unable to init factory due to exception ", e);
+ }
+ }
+ }
+
public HttpServer createHttpServerInstance(int port) throws IOException {
try {
HttpsServer server = HttpsServer.create(new InetSocketAddress(port), 5);
diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java
index 29240b49b2f..6ea240f6423 100644
--- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java
+++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java
@@ -18,13 +18,14 @@
package com.cloud.consoleproxy;
-import java.io.IOException;
+import java.io.IOException;
+
+import javax.net.ssl.SSLServerSocket;
+
+import com.sun.net.httpserver.HttpServer;
-import javax.net.ssl.SSLServerSocket;
-
-import com.sun.net.httpserver.HttpServer;
-
-public interface ConsoleProxyServerFactory {
+public interface ConsoleProxyServerFactory {
+ void init(byte[] ksBits, String ksPassword);
HttpServer createHttpServerInstance(int port) throws IOException;
SSLServerSocket createSSLServerSocket(int port) throws IOException;
}
diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java
index 87bb766d830..9eab10a0b70 100755
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -96,6 +96,10 @@ public enum Config {
ConsoleProxySessionTimeout("Console Proxy", AgentManager.class, Integer.class, "consoleproxy.session.timeout", "300000", "Timeout(in milliseconds) that console proxy tries to maintain a viewer session before it times out the session for no activity", null),
ConsoleProxyDisableRpFilter("Console Proxy", AgentManager.class, Integer.class, "consoleproxy.disable.rpfilter", "true", "disable rp_filter on console proxy VM public interface", null),
ConsoleProxyLaunchMax("Console Proxy", AgentManager.class, Integer.class, "consoleproxy.launch.max", "10", "maximum number of console proxy instances per zone can be launched", null),
+ ConsoleProxyManagementState("Console Proxy", AgentManager.class, String.class, "consoleproxy.management.state", com.cloud.consoleproxy.ConsoleProxyManagementState.Auto.toString(),
+ "console proxy service management state", null),
+ ConsoleProxyManagementLastState("Console Proxy", AgentManager.class, String.class, "consoleproxy.management.state.last", com.cloud.consoleproxy.ConsoleProxyManagementState.Auto.toString(),
+ "last console proxy service management state", null),
// Snapshots
SnapshotHourlyMax("Snapshots", SnapshotManager.class, Integer.class, "snapshot.max.hourly", "8", "Maximum hourly snapshots for a volume", null),
diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
index a3f5abf38e1..7f46e48d3c1 100644
--- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java
+++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
@@ -67,6 +67,8 @@ import com.cloud.host.dao.DetailsDaoImpl;
import com.cloud.host.dao.HostDaoImpl;
import com.cloud.host.dao.HostTagsDaoImpl;
import com.cloud.hypervisor.HypervisorGuruManagerImpl;
+import com.cloud.keystore.KeystoreDaoImpl;
+import com.cloud.keystore.KeystoreManagerImpl;
import com.cloud.maint.UpgradeManagerImpl;
import com.cloud.maint.dao.AgentUpgradeDaoImpl;
import com.cloud.network.NetworkManagerImpl;
@@ -263,6 +265,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
addDao("StoragePoolWorkDao", StoragePoolWorkDaoImpl.class);
addDao("HostTagsDao", HostTagsDaoImpl.class);
addDao("NetworkDomainDao", NetworkDomainDaoImpl.class);
+ addDao("KeystoreDao", KeystoreDaoImpl.class);
}
@Override
@@ -281,6 +284,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
addManager("network manager", NetworkManagerImpl.class);
addManager("download manager", DownloadMonitorImpl.class);
addManager("upload manager", UploadMonitorImpl.class);
+ addManager("keystore manager", KeystoreManagerImpl.class);
addManager("console proxy manager", AgentBasedStandaloneConsoleProxyManager.class);
addManager("secondary storage vm manager", SecondaryStorageManagerImpl.class);
addManager("vm manager", UserVmManagerImpl.class);
diff --git a/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java b/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java
index c5d91d309f8..960e85141dc 100644
--- a/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java
+++ b/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java
@@ -187,7 +187,7 @@ public class AgentBasedConsoleProxyManager implements ConsoleProxyManager, Virtu
}
return null;
}
-
+
@Override
public void onLoadReport(ConsoleProxyLoadReportCommand cmd) {
}
@@ -267,6 +267,23 @@ public class AgentBasedConsoleProxyManager implements ConsoleProxyManager, Virtu
return false;
}
+ @Override
+ public void setManagementState(ConsoleProxyManagementState state) {
+ }
+
+ @Override
+ public ConsoleProxyManagementState getManagementState() {
+ return null;
+ }
+
+ @Override
+ public void resumeLastManagementState() {
+ }
+
+ @Override
+ public void startAgentHttpHandlerInVM(StartupProxyCommand startupCmd) {
+ }
+
@Override
public String getName() {
return _name;
@@ -279,13 +296,7 @@ public class AgentBasedConsoleProxyManager implements ConsoleProxyManager, Virtu
}
return VirtualMachineName.getConsoleProxyId(vmName);
}
-
- @Override
- public boolean applyCustomCertToNewProxy(StartupProxyCommand cmd) {
- // TODO Auto-generated method stub
- return false;
- }
-
+
@Override
public ConsoleProxyVO findByName(String name) {
// TODO Auto-generated method stub
diff --git a/server/src/com/cloud/consoleproxy/AgentHook.java b/server/src/com/cloud/consoleproxy/AgentHook.java
index 2550df69c2f..48369e8057b 100644
--- a/server/src/com/cloud/consoleproxy/AgentHook.java
+++ b/server/src/com/cloud/consoleproxy/AgentHook.java
@@ -35,5 +35,5 @@ public interface AgentHook {
void onAgentConnect(HostVO host, StartupCommand cmd);
public void onAgentDisconnect(long agentId, Status state);
- boolean applyCustomCertToNewProxy(StartupProxyCommand cmd);
+ public void startAgentHttpHandlerInVM(StartupProxyCommand startupCmd);
}
diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyListener.java b/server/src/com/cloud/consoleproxy/ConsoleProxyListener.java
index 61bdc14147b..2e9e930e99d 100644
--- a/server/src/com/cloud/consoleproxy/ConsoleProxyListener.java
+++ b/server/src/com/cloud/consoleproxy/ConsoleProxyListener.java
@@ -69,7 +69,7 @@ public class ConsoleProxyListener implements Listener {
_proxyMgr.onAgentConnect(host, cmd);
if (cmd instanceof StartupProxyCommand) {
- _proxyMgr.applyCustomCertToNewProxy((StartupProxyCommand)cmd);
+ _proxyMgr.startAgentHttpHandlerInVM((StartupProxyCommand)cmd);
}
}
diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagementState.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagementState.java
new file mode 100644
index 00000000000..92a6bfd90ff
--- /dev/null
+++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagementState.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package com.cloud.consoleproxy;
+
+public enum ConsoleProxyManagementState {
+ Auto,
+ Manual,
+ Suspending,
+ ResetSuspending
+}
+
diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManager.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManager.java
index cbb44de5661..137d6b0fe0d 100644
--- a/server/src/com/cloud/consoleproxy/ConsoleProxyManager.java
+++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManager.java
@@ -40,6 +40,11 @@ public interface ConsoleProxyManager extends Manager {
public static final int DEFAULT_PROXY_SESSION_TIMEOUT = 300000; // 5 minutes
public static final String ALERT_SUBJECT = "proxy-alert";
+ public static final String CERTIFICATE_NAME = "CPVMCertificate";
+
+ public void setManagementState(ConsoleProxyManagementState state);
+ public ConsoleProxyManagementState getManagementState();
+ public void resumeLastManagementState();
public ConsoleProxyInfo assignProxy(long dataCenterId, long userVmId);
@@ -47,7 +52,7 @@ public interface ConsoleProxyManager extends Manager {
public boolean stopProxy(long proxyVmId);
public boolean rebootProxy(long proxyVmId);
public boolean destroyProxy(long proxyVmId);
-
+
public void onLoadReport(ConsoleProxyLoadReportCommand cmd);
public AgentControlAnswer onConsoleAccessAuthentication(ConsoleAccessAuthenticationCommand cmd);
diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java
index 5acf93097a1..769bc7c9790 100644
--- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java
+++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java
@@ -25,6 +25,7 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -46,11 +47,10 @@ import com.cloud.agent.api.StopAnswer;
import com.cloud.agent.api.check.CheckSshAnswer;
import com.cloud.agent.api.check.CheckSshCommand;
import com.cloud.agent.api.proxy.ConsoleProxyLoadAnswer;
-import com.cloud.agent.api.proxy.UpdateCertificateCommand;
+import com.cloud.agent.api.proxy.StartConsoleProxyAgentHttpHandlerCommand;
import com.cloud.agent.manager.Commands;
import com.cloud.api.ServerApiException;
import com.cloud.api.commands.DestroyConsoleProxyCmd;
-import com.cloud.certificate.CertificateVO;
import com.cloud.certificate.dao.CertificateDao;
import com.cloud.cluster.ClusterManager;
import com.cloud.cluster.StackMaid;
@@ -82,6 +82,9 @@ import com.cloud.info.ConsoleProxyStatus;
import com.cloud.info.RunningHostCountInfo;
import com.cloud.info.RunningHostInfoAgregator;
import com.cloud.info.RunningHostInfoAgregator.ZoneHostInfo;
+import com.cloud.keystore.KeystoreDao;
+import com.cloud.keystore.KeystoreManager;
+import com.cloud.keystore.KeystoreVO;
import com.cloud.network.NetworkManager;
import com.cloud.network.NetworkVO;
import com.cloud.network.Networks.TrafficType;
@@ -107,6 +110,7 @@ import com.cloud.utils.component.ComponentLocator;
import com.cloud.utils.component.Inject;
import com.cloud.utils.component.Manager;
import com.cloud.utils.concurrency.NamedThreadFactory;
+import com.cloud.utils.db.DB;
import com.cloud.utils.db.GlobalLock;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.events.SubscriptionMgr;
@@ -225,6 +229,55 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx
private Map _zoneVmCountMap; // map
private final GlobalLock _allocProxyLock = GlobalLock.getInternLock(getAllocProxyLockName());
+
+ private String keyContent =
+ "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALV5vGlkiWwoZX4hTRplPXP8qtST\n" +
+ "hwZhko8noeY5vf8ECwmd+vrCTw/JvnOtkx/8oYNbg/SeUt1EfOsk6gqJdBblGFBZRMcUJlIpqE9z\n" +
+ "uv68U9G8Gfi/qvRSY336hibw0J5bZ4vn1QqmyHDB+Czea9AjFUV7AEVG15+vED7why+/AgMBAAEC\n" +
+ "gYBmFBPnNKYYMKDmUdUNA+WNWJK/ADzzWe8WlzR6TACTcbLDthl289WFC/YVG42mcHRpbxDKiEQU\n" +
+ "MnIR0rHTO34Qb/2HcuyweStU2gqR6omxBvMnFpJr90nD1HcOMJzeLHsphau0/EmKKey+gk4PyieD\n" +
+ "KqTM7LTjjHv8xPM4n+WAAQJBAOMNCeFKlJ4kMokWhU74B5/w/NGyT1BHUN0VmilHSiJC8JqS4BiI\n" +
+ "ZpAeET3VmilO6QTGh2XVhEDGteu3uZR6ipUCQQDMnRzMgQ/50LFeIQo4IBtwlEouczMlPQF4c21R\n" +
+ "1d720moxILVPT0NJZTQUDDmmgbL+B7CgtcCR2NlP5sKPZVADAkEAh4Xq1cy8dMBKYcVNgNtPQcqI\n" +
+ "PWpfKR3ISI5yXB0vRNAL6Vet5zbTcUZhKDVtNSbis3UEsGYH8NorEC2z2cpjGQJANhJi9Ow6c5Mh\n" +
+ "/DURBUn+1l5pyCKrZnDbvaALSLATLvjmFTuGjoHszy2OeKnOZmEqExWnKKE/VYuPyhy6V7i3TwJA\n" +
+ "f8skDgtPK0OsBCa6IljPaHoWBjPc4kFkSTSS1d56hUcWSikTmiuKdLyBb85AADSZYsvHWrte4opN\n" +
+ "dhNukMJuRA==\n";
+
+ private String certContent =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIIE3jCCA8agAwIBAgIFAqv56tIwDQYJKoZIhvcNAQEFBQAwgcoxCzAJBgNVBAYT\n" +
+ "AlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYD\n" +
+ "VQQKExFHb0RhZGR5LmNvbSwgSW5jLjEzMDEGA1UECxMqaHR0cDovL2NlcnRpZmlj\n" +
+ "YXRlcy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5MTAwLgYDVQQDEydHbyBEYWRkeSBT\n" +
+ "ZWN1cmUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxETAPBgNVBAUTCDA3OTY5Mjg3\n" +
+ "MB4XDTA5MDIxMTA0NTc1NloXDTEyMDIwNzA1MTEyM1owWTEZMBcGA1UECgwQKi5y\n" +
+ "ZWFsaG9zdGlwLmNvbTEhMB8GA1UECwwYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVk\n" +
+ "MRkwFwYDVQQDDBAqLnJlYWxob3N0aXAuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GN\n" +
+ "ADCBiQKBgQC1ebxpZIlsKGV+IU0aZT1z/KrUk4cGYZKPJ6HmOb3/BAsJnfr6wk8P\n" +
+ "yb5zrZMf/KGDW4P0nlLdRHzrJOoKiXQW5RhQWUTHFCZSKahPc7r+vFPRvBn4v6r0\n" +
+ "UmN9+oYm8NCeW2eL59UKpshwwfgs3mvQIxVFewBFRtefrxA+8IcvvwIDAQABo4IB\n" +
+ "vTCCAbkwDwYDVR0TAQH/BAUwAwEBADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB\n" +
+ "BQUHAwIwDgYDVR0PAQH/BAQDAgWgMDIGA1UdHwQrMCkwJ6AloCOGIWh0dHA6Ly9j\n" +
+ "cmwuZ29kYWRkeS5jb20vZ2RzMS0yLmNybDBTBgNVHSAETDBKMEgGC2CGSAGG/W0B\n" +
+ "BxcBMDkwNwYIKwYBBQUHAgEWK2h0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5j\n" +
+ "b20vcmVwb3NpdG9yeS8wgYAGCCsGAQUFBwEBBHQwcjAkBggrBgEFBQcwAYYYaHR0\n" +
+ "cDovL29jc3AuZ29kYWRkeS5jb20vMEoGCCsGAQUFBzAChj5odHRwOi8vY2VydGlm\n" +
+ "aWNhdGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvZ2RfaW50ZXJtZWRpYXRlLmNy\n" +
+ "dDAfBgNVHSMEGDAWgBT9rGEyk2xF1uLuhV+auud2mWjM5zArBgNVHREEJDAighAq\n" +
+ "LnJlYWxob3N0aXAuY29tgg5yZWFsaG9zdGlwLmNvbTAdBgNVHQ4EFgQUHxwmdK5w\n" +
+ "9/YVeZ/3fHyi6nQfzoYwDQYJKoZIhvcNAQEFBQADggEBABv/XinvId6oWXJtmku+\n" +
+ "7m90JhSVH0ycoIGjgdaIkcExQGP08MCilbUsPcbhLheSFdgn/cR4e1MP083lacoj\n" +
+ "OGauY7b8f/cuquGkT49Ns14awPlEzRjjycQEjjLxFEuL5CFWa2t2gKRE1dSfhDQ+\n" +
+ "fJ6GBCs1XgZLuhkKS8fPf+YmG2ZjHzYDjYoSx7paDXgEm+kbYIZdCK51lA0BUAjP\n" +
+ "9ZMGhsu/PpAbh5U/DtcIqxY0xeqD4TeGsBzXg6uLhv+jKHDtXg5fYPe+z0n5DCEL\n" +
+ "k0fLF4+i/pt9hVCz0QrZ28RUhXf825+EOL0Gw+Uzt+7RV2cCaJrlu4cDrDom2FRy\n" +
+ "E8I=\n" +
+ "-----END CERTIFICATE-----\n";
+
+ @Inject private KeystoreDao _ksDao;
+ @Inject private KeystoreManager _ksMgr;
+ private Random _random = new Random(System.currentTimeMillis());
@Override
public ConsoleProxyInfo assignProxy(final long dataCenterId, final long vmId) {
@@ -267,8 +320,12 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx
s_logger.warn("Assigned console proxy does not have a valid public IP address");
return null;
}
+
+ KeystoreVO ksVo = _ksDao.findByName(ConsoleProxyManager.CERTIFICATE_NAME);
+ assert(ksVo != null);
- return new ConsoleProxyInfo(proxy.isSslEnabled(), proxy.getPublicIpAddress(), _consoleProxyPort, proxy.getPort(), _configDao.getValue("consoleproxy.url.domain"));
+ return new ConsoleProxyInfo(proxy.isSslEnabled(), proxy.getPublicIpAddress(), _consoleProxyPort, proxy.getPort(),
+ ksVo.getDomainSuffix());
}
public ConsoleProxyVO doAssignProxy(long dataCenterId, long vmId) {
@@ -864,12 +921,11 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx
}
private boolean reserveStandbyCapacity() {
- String value = _configDao.getValue(Config.SystemVMAutoReserveCapacity.key());
- if (value != null && value.equalsIgnoreCase("true")) {
- return true;
- }
-
- return false;
+ ConsoleProxyManagementState state = getManagementState();
+ if(state == null || state != ConsoleProxyManagementState.Auto)
+ return false;
+
+ return true;
}
private boolean allowToLaunchNew(long dcId) {
@@ -1047,7 +1103,83 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx
return false;
}
}
+
+ @Override @DB
+ public void setManagementState(ConsoleProxyManagementState state) {
+ Transaction txn = Transaction.currentTxn();
+ try {
+ txn.start();
+
+ ConsoleProxyManagementState lastState = getManagementState();
+ if(lastState == null) {
+ txn.commit();
+ return;
+ }
+
+ if(lastState != state) {
+ _configDao.update(Config.ConsoleProxyManagementLastState.key(), lastState.toString());
+ _configDao.update(Config.ConsoleProxyManagementState.key(), state.toString());
+ }
+
+ txn.commit();
+ } catch (Throwable e) {
+ txn.rollback();
+ }
+ }
+
+ @Override
+ public ConsoleProxyManagementState getManagementState() {
+ String value = _configDao.getValue(Config.ConsoleProxyManagementState.key());
+ if(value != null) {
+ ConsoleProxyManagementState state = ConsoleProxyManagementState.valueOf(value);
+
+ if(state == null) {
+ s_logger.error("Invalid console proxy management state: " + value);
+ }
+ return state;
+ }
+
+ s_logger.error("Invalid console proxy management state: " + value);
+ return null;
+ }
+
+ @Override @DB
+ public void resumeLastManagementState() {
+ Transaction txn = Transaction.currentTxn();
+ try {
+ txn.start();
+ ConsoleProxyManagementState state = getManagementState();
+ ConsoleProxyManagementState lastState = getLastManagementState();
+ if(lastState == null) {
+ txn.commit();
+ return;
+ }
+
+ if(lastState != state) {
+ _configDao.update(Config.ConsoleProxyManagementState.key(), lastState.toString());
+ }
+
+ txn.commit();
+ } catch (Throwable e) {
+ txn.rollback();
+ }
+ }
+ private ConsoleProxyManagementState getLastManagementState() {
+ String value = _configDao.getValue(Config.ConsoleProxyManagementLastState.key());
+ if(value != null) {
+ ConsoleProxyManagementState state = ConsoleProxyManagementState.valueOf(value);
+
+ if(state == null) {
+ s_logger.error("Invalid console proxy management state: " + value);
+ }
+ return state;
+ }
+
+ s_logger.error("Invalid console proxy management state: " + value);
+ return null;
+ }
+
@Override
public boolean rebootProxy(long proxyVmId) {
final ConsoleProxyVO proxy = _consoleProxyDao.findById(proxyVmId);
@@ -1095,7 +1227,22 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx
private String getAllocProxyLockName() {
return "consoleproxy.alloc";
}
-
+
+ private void prepareDefaultCertificate() {
+ GlobalLock lock = GlobalLock.getInternLock("consoleproxy.cert.setup");
+ try {
+ if(lock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_SYNC)) {
+ KeystoreVO ksVo = _ksDao.findByName(CERTIFICATE_NAME);
+ if(ksVo == null) {
+ _ksDao.save(CERTIFICATE_NAME, certContent, keyContent, "realhostip.com");
+ }
+ lock.unlock();
+ }
+ } finally {
+ lock.releaseRef();
+ }
+ }
+
@Override
public boolean configure(String name, Map params) throws ConfigurationException {
if (s_logger.isInfoEnabled()) {
@@ -1157,6 +1304,8 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx
if (_instance == null) {
_instance = "DEFAULT";
}
+
+ prepareDefaultCertificate();
Map agentMgrConfigs = configDao.getConfiguration("AgentManager", params);
_mgmt_host = agentMgrConfigs.get("host");
@@ -1346,42 +1495,41 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx
_consoleProxyDao.update(proxy.getId(), proxy);
}
- @Override
- public boolean applyCustomCertToNewProxy(StartupProxyCommand cmd) {
- // this is the case for updating cust cert on each new starting proxy, if such cert exists
- // get cert from db
- CertificateVO cert = _certDao.listAll().get(0);
-
- if (cert.getUpdated().equalsIgnoreCase("Y")) {
- String certStr = cert.getCertificate();
- long proxyVmId = (cmd).getProxyVmId();
- ConsoleProxyVO consoleProxy = _consoleProxyDao.findById(proxyVmId);
- // find corresponding host
- if (consoleProxy != null) {
- HostVO consoleProxyHost = _hostDao.findConsoleProxyHost(consoleProxy.getName(), Type.ConsoleProxy);
- // now send a command to console proxy host
- UpdateCertificateCommand certCmd = new UpdateCertificateCommand(certStr, true);
- try {
- Answer updateCertAns = _agentMgr.send(consoleProxyHost.getId(), certCmd);
- if (updateCertAns.getResult() == true) {
- // we have the cert copied over on cpvm
- rebootProxy(consoleProxy.getId());
- // when cp reboots, the context will be reinit with the new cert
- s_logger.info("Successfully rebooted console proxy resource after custom certificate application for proxy:" + cmd.getProxyVmId());
- return true;
- }
- } catch (AgentUnavailableException e) {
- s_logger.warn("Unable to send update certificate command to the console proxy resource for proxy:" + cmd.getProxyVmId(), e);
- return false;
- } catch (OperationTimedoutException e) {
- s_logger.warn("Unable to send update certificate command to the console proxy resource for proxy:" + cmd.getProxyVmId(), e);
- return false;
- }
- }
- } else {
- return false;// no cert entry in the db record
- }
- return false;// cert already applied in previous cycles
+ public void startAgentHttpHandlerInVM(StartupProxyCommand startupCmd) {
+ StartConsoleProxyAgentHttpHandlerCommand cmd = null;
+ if(_configDao.isPremium()) {
+ String storePassword = String.valueOf(_random.nextLong());
+ byte[] ksBits = _ksMgr.getKeystoreBits(ConsoleProxyManager.CERTIFICATE_NAME, ConsoleProxyManager.CERTIFICATE_NAME,
+ storePassword);
+
+ assert(ksBits != null);
+ if(ksBits == null) {
+ s_logger.error("Could not find and construct a valid SSL certificate");
+ }
+ cmd = new StartConsoleProxyAgentHttpHandlerCommand(ksBits, storePassword);
+ } else {
+ cmd = new StartConsoleProxyAgentHttpHandlerCommand();
+ }
+
+ try {
+ long proxyVmId = startupCmd.getProxyVmId();
+ ConsoleProxyVO consoleProxy = _consoleProxyDao.findById(proxyVmId);
+ assert(consoleProxy != null);
+ HostVO consoleProxyHost = _hostDao.findConsoleProxyHost(consoleProxy.getName(), Type.ConsoleProxy);
+
+ Answer answer = _agentMgr.send(consoleProxyHost.getId(), cmd);
+ if(answer == null || !answer.getResult()) {
+ s_logger.error("Console proxy agent reported that it failed to execute http handling startup command");
+ } else {
+ s_logger.info("Successfully sent out command to start HTTP handling in console proxy agent");
+ }
+ } catch(AgentUnavailableException e) {
+ s_logger.error("Unable to send http handling startup command to the console proxy resource for proxy:" + startupCmd.getProxyVmId(), e);
+ } catch(OperationTimedoutException e) {
+ s_logger.error("Unable to send http handling startup command(time out) to the console proxy resource for proxy:" + startupCmd.getProxyVmId(), e);
+ } catch(Exception e) {
+ s_logger.error("Unexpected exception when sending http handling startup command(time out) to the console proxy resource for proxy:" + startupCmd.getProxyVmId(), e);
+ }
}
@Override
@@ -1428,11 +1576,48 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx
_zoneVmCountMap.put(info.getId(), info);
}
}
-
+
+ private void scanManagementState() {
+ ConsoleProxyManagementState state = getManagementState();
+ if(state != null) {
+ switch(state) {
+ case Auto:
+ case Manual:
+ case Suspending:
+ break;
+
+ case ResetSuspending:
+ handleResetSuspending();
+ break;
+
+ default:
+ assert(false);
+ }
+ }
+ }
+
+ private void handleResetSuspending() {
+ List runningProxies = _consoleProxyDao.getProxyListInStates(State.Running);
+ for(ConsoleProxyVO proxy : runningProxies) {
+ s_logger.info("Stop console proxy " + proxy.getId() + " because of we are currently in ResetSuspending management mode");
+ this.stopProxy(proxy.getId());
+ }
+
+ // check if it is time to resume
+ List proxiesInTransition = _consoleProxyDao.getProxyListInStates(State.Running, State.Starting, State.Stopping);
+ if(proxiesInTransition.size() == 0) {
+ s_logger.info("All previous console proxy VMs in transition mode ceased the mode, we will now resume to last management state");
+ this.resumeLastManagementState();
+ }
+ }
+
@Override
- public boolean canScan() {
- if (!reserveStandbyCapacity()) {
- if (s_logger.isDebugEnabled()) {
+ public boolean canScan() {
+ // take the chance to do management-state management
+ scanManagementState();
+
+ if(!reserveStandbyCapacity()) {
+ if(s_logger.isDebugEnabled()) {
s_logger.debug("Reserving standby capacity is disable, skip capacity scan");
}
return false;
diff --git a/server/src/com/cloud/keystore/KeystoreDaoImpl.java b/server/src/com/cloud/keystore/KeystoreDaoImpl.java
index d7a15d3de68..22421a9c55c 100644
--- a/server/src/com/cloud/keystore/KeystoreDaoImpl.java
+++ b/server/src/com/cloud/keystore/KeystoreDaoImpl.java
@@ -31,9 +31,8 @@ import com.cloud.utils.exception.CloudRuntimeException;
@Local(value={KeystoreDao.class})
public class KeystoreDaoImpl extends GenericDaoBase implements KeystoreDao {
-
protected final SearchBuilder FindByNameSearch;
-
+
public KeystoreDaoImpl() {
FindByNameSearch = createSearchBuilder();
FindByNameSearch.and("name", FindByNameSearch.entity().getName(), Op.EQ);
@@ -49,7 +48,6 @@ public class KeystoreDaoImpl extends GenericDaoBase implements
return findOneBy(sc);
}
-
@Override
@DB
public void save(String name, String certificate, String key, String domainSuffix) {
diff --git a/server/src/com/cloud/keystore/KeystoreManager.java b/server/src/com/cloud/keystore/KeystoreManager.java
index 79e4db49a22..44491af07e8 100644
--- a/server/src/com/cloud/keystore/KeystoreManager.java
+++ b/server/src/com/cloud/keystore/KeystoreManager.java
@@ -21,6 +21,7 @@ package com.cloud.keystore;
import com.cloud.utils.component.Manager;
public interface KeystoreManager extends Manager {
+ boolean validateCertificate(String certificate, String key, String domainSuffix);
void saveCertificate(String name, String certificate, String key, String domainSuffix);
byte[] getKeystoreBits(String name, String aliasForCertificateInStore, String storePassword);
}
diff --git a/server/src/com/cloud/keystore/KeystoreManagerImpl.java b/server/src/com/cloud/keystore/KeystoreManagerImpl.java
index cad54c57a4d..4b880e7edeb 100644
--- a/server/src/com/cloud/keystore/KeystoreManagerImpl.java
+++ b/server/src/com/cloud/keystore/KeystoreManagerImpl.java
@@ -18,6 +18,7 @@
package com.cloud.keystore;
import java.io.IOException;
+import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
@@ -64,6 +65,29 @@ public class KeystoreManagerImpl implements KeystoreManager {
return _name;
}
+ @Override
+ public boolean validateCertificate(String certificate, String key, String domainSuffix) {
+ if(certificate == null || certificate.isEmpty() ||
+ key == null || key.isEmpty() ||
+ domainSuffix == null || domainSuffix.isEmpty()) {
+ s_logger.error("Invalid parameter found in (certificate, key, domainSuffix) tuple for domain: " + domainSuffix);
+ return false;
+ }
+
+ try {
+ String ksPassword = "passwordForValidation";
+ byte[] ksBits = CertificateHelper.buildAndSaveKeystore(domainSuffix, certificate, key, ksPassword);
+ KeyStore ks = CertificateHelper.loadKeystore(ksBits, ksPassword);
+ if(ks != null)
+ return true;
+
+ s_logger.error("Unabled to construct keystore for domain: " + domainSuffix);
+ } catch(Exception e) {
+ s_logger.error("Certificate validation failed due to exception for domain: " + domainSuffix, e);
+ }
+ return false;
+ }
+
@Override
public void saveCertificate(String name, String certificate, String key, String domainSuffix) {
if(name == null || name.isEmpty() ||
@@ -72,7 +96,6 @@ public class KeystoreManagerImpl implements KeystoreManager {
domainSuffix == null || domainSuffix.isEmpty())
throw new CloudRuntimeException("invalid parameter in saveCerticate");
-
_ksDao.save(name, certificate, key, domainSuffix);
}
diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java
index 9e797946482..6de46dc3a37 100644
--- a/server/src/com/cloud/server/ConfigurationServerImpl.java
+++ b/server/src/com/cloud/server/ConfigurationServerImpl.java
@@ -122,7 +122,7 @@ public class ConfigurationServerImpl implements ConfigurationServer {
// Get init
String init = _configDao.getValue("init");
- if (init.equals("false")) {
+ if (init == null || init.equals("false")) {
s_logger.debug("ConfigurationServer is saving default values to the database.");
// Save default Configuration Table values
diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java
index cee74092028..2ba0e556e30 100755
--- a/server/src/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/com/cloud/server/ManagementServerImpl.java
@@ -17,12 +17,6 @@
*/
package com.cloud.server;
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -32,9 +26,6 @@ import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
@@ -62,10 +53,8 @@ import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import com.cloud.agent.AgentManager;
-import com.cloud.agent.api.Answer;
import com.cloud.agent.api.GetVncPortAnswer;
import com.cloud.agent.api.GetVncPortCommand;
-import com.cloud.agent.api.proxy.UpdateCertificateCommand;
import com.cloud.agent.api.storage.CopyVolumeAnswer;
import com.cloud.agent.api.storage.CopyVolumeCommand;
import com.cloud.alert.Alert;
@@ -135,7 +124,6 @@ import com.cloud.async.dao.AsyncJobDao;
import com.cloud.capacity.Capacity;
import com.cloud.capacity.CapacityVO;
import com.cloud.capacity.dao.CapacityDao;
-import com.cloud.certificate.CertificateVO;
import com.cloud.certificate.dao.CertificateDao;
import com.cloud.configuration.Config;
import com.cloud.configuration.ConfigurationManager;
@@ -143,6 +131,7 @@ import com.cloud.configuration.ConfigurationVO;
import com.cloud.configuration.ResourceLimitVO;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.configuration.dao.ResourceLimitDao;
+import com.cloud.consoleproxy.ConsoleProxyManagementState;
import com.cloud.consoleproxy.ConsoleProxyManager;
import com.cloud.dc.AccountVlanMapVO;
import com.cloud.dc.ClusterVO;
@@ -168,11 +157,9 @@ import com.cloud.event.EventTypes;
import com.cloud.event.EventUtils;
import com.cloud.event.EventVO;
import com.cloud.event.dao.EventDao;
-import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.CloudAuthenticationException;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InvalidParameterValueException;
-import com.cloud.exception.ManagementServerException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceUnavailableException;
@@ -184,6 +171,7 @@ import com.cloud.host.Status;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.info.ConsoleProxyInfo;
+import com.cloud.keystore.KeystoreManager;
import com.cloud.network.IPAddressVO;
import com.cloud.network.NetworkManager;
import com.cloud.network.NetworkVO;
@@ -256,7 +244,6 @@ import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.exception.CloudRuntimeException;
-import com.cloud.utils.exception.ExecutionException;
import com.cloud.utils.net.MacAddress;
import com.cloud.utils.net.NetUtils;
import com.cloud.utils.ssh.SSHKeysHelper;
@@ -339,6 +326,8 @@ public class ManagementServerImpl implements ManagementServer {
private final CertificateDao _certDao;
private final SSHKeyPairDao _sshKeyPairDao;
+ private final KeystoreManager _ksMgr;
+
private final ScheduledExecutorService _eventExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker"));
private final StatsCollector _statsCollector;
@@ -410,7 +399,8 @@ public class ManagementServerImpl implements ManagementServer {
_sshKeyPairDao = locator.getDao(SSHKeyPairDao.class);
_itMgr = locator.getManager(VirtualMachineManager.class);
_networkMgr = locator.getManager(NetworkManager.class);
-
+ _ksMgr = locator.getManager(KeystoreManager.class);
+
_userAuthenticators = locator.getAdapters(UserAuthenticator.class);
if (_userAuthenticators == null || !_userAuthenticators.isSet()) {
s_logger.error("Unable to find an user authenticator.");
@@ -4566,141 +4556,15 @@ public class ManagementServerImpl implements ManagementServer {
return EventUtils.saveEvent(userId, accountId, level, type, description, startEventId);
}
- @Override
- @DB
+ @Override @DB
public String uploadCertificate(UploadCustomCertificateCmd cmd) {
- CertificateVO cert = null;
- Long certVOId = null;
- try {
- Transaction.currentTxn();
- String certificate = cmd.getCertificate();
- cert = _certDao.listAll().get(0); // always 1 record in db (from the deploydb time)
- cert = _certDao.acquireInLockTable(cert.getId());
- if (cert == null) {
- String msg = "Unable to obtain lock on the cert from uploadCertificate()";
- s_logger.error(msg);
- throw new ConcurrentOperationException(msg);
- } else {
- if (cert.getUpdated().equalsIgnoreCase("Y")) {
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("A custom certificate already exists in the DB, will replace it with the new one being uploaded");
- }
- } else {
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("No custom certificate exists in the DB, will upload a new one");
- }
- }
-
- // validate if the cert follows X509 format, if not, don't persist to db
- InputStream is = new ByteArrayInputStream(certificate.getBytes("UTF-8"));
- BufferedInputStream bis = new BufferedInputStream(is);
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- while (bis.available() > 1) {
- Certificate localCert = cf.generateCertificate(bis);// throws certexception if not valid cert format
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("The custom certificate generated for validation is:" + localCert.toString());
- }
- }
-
- certVOId = _certDao.persistCustomCertToDb(certificate, cert, this.getId());// 0 implies failure
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Custom certificate persisted to the DB");
- }
- }
-
- if (certVOId != 0) {
- // certficate uploaded to db successfully
- // get a list of all Console proxies from the cp table
- List cpList = _consoleProxyDao.listAll();
- if (cpList.size() == 0) {
- String msg = "Unable to find any console proxies in the system for certificate update";
- s_logger.warn(msg);
- throw new ExecutionException(msg);
- }
- // get a list of all hosts in host table for type cp
- List cpHosts = _hostDao.listByType(com.cloud.host.Host.Type.ConsoleProxy);
- if (cpHosts.size() == 0) {
- String msg = "Unable to find any console proxy hosts in the system for certificate update";
- s_logger.warn(msg);
- throw new ExecutionException(msg);
- }
- // create a hashmap for fast lookup
- Map hostNameToHostIdMap = new HashMap();
- // updated console proxies id list
- List updatedCpIdList = new ArrayList();
- for (HostVO cpHost : cpHosts) {
- hostNameToHostIdMap.put(cpHost.getName(), cpHost.getId());
- }
- for (ConsoleProxyVO cp : cpList) {
- Long cpHostId = hostNameToHostIdMap.get(cp.getName());
- // now send a command to each console proxy host
- UpdateCertificateCommand certCmd = new UpdateCertificateCommand(_certDao.findById(certVOId).getCertificate(), false);
- try {
- Answer updateCertAns = _agentMgr.send(cpHostId, certCmd);
- if (updateCertAns.getResult() == true) {
- // we have the cert copied over on cpvm
- _consoleProxyMgr.rebootProxy(cp.getId());
- // when cp reboots, the context will be reinit with the new cert
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Successfully updated custom certificate on console proxy vm id:" + cp.getId() + " ,console proxy host id:" + cpHostId);
- }
- updatedCpIdList.add(cp.getId());
- }
- } catch (AgentUnavailableException e) {
- s_logger.warn("Unable to send update certificate command to the console proxy resource as agent is unavailable for console proxy vm id:" + cp.getId()
- + " ,console proxy host id:" + cpHostId, e);
- } catch (OperationTimedoutException e) {
- s_logger.warn("Unable to send update certificate command to the console proxy resource as there was a timeout for console proxy vm id:" + cp.getId()
- + " ,console proxy host id:" + cpHostId, e);
- }
- }
-
- if (updatedCpIdList.size() == cpList.size()) {
- // success case, all updated
- return ("Updated:" + updatedCpIdList.size() + " out of:" + cpList.size() + " console proxies");
- } else {
- // failure case, if even one update fails
- throw new ManagementServerException("Updated:" + updatedCpIdList.size() + " out of:" + cpList.size() + " console proxies with successfully updated console proxy ids being:"
- + (updatedCpIdList.size() > 0 ? updatedCpIdList.toString() : ""));
- }
- } else {
- throw new ManagementServerException("Unable to persist custom certificate to the cloud db");
- }
- } catch (Exception e) {
- s_logger.warn("Failed to successfully update the cert across console proxies on management server:" + this.getId());
- if (e instanceof ExecutionException) {
- throw new CloudRuntimeException(e.getMessage());
- } else if (e instanceof ManagementServerException) {
- throw new CloudRuntimeException(e.getMessage());
- } else if (e instanceof IndexOutOfBoundsException) {
- String msg = "Custom certificate record in the db deleted; this should never happen. Please create a new record in the certificate table";
- s_logger.error(msg, e);
- throw new CloudRuntimeException(msg);
- } else if (e instanceof FileNotFoundException) {
- String msg = "Invalid file path for custom cert found during cert validation";
- s_logger.error(msg, e);
- throw new InvalidParameterValueException(msg);
- } else if (e instanceof CertificateException) {
- String msg = "The file format for custom cert does not conform to the X.509 specification";
- s_logger.error(msg, e);
- throw new CloudRuntimeException(msg);
- } else if (e instanceof UnsupportedEncodingException) {
- String msg = "Unable to encode the certificate into UTF-8 input stream for validation";
- s_logger.error(msg, e);
- throw new CloudRuntimeException(msg);
- } else if (e instanceof IOException) {
- String msg = "Cannot generate input stream during custom cert validation";
- s_logger.error(msg, e);
- throw new CloudRuntimeException(msg);
- } else {
- String msg = "Cannot upload custom certificate, internal error.";
- s_logger.error(msg, e);
- throw new CloudRuntimeException(msg);
- }
-
- } finally {
- _certDao.releaseFromLockTable(cert.getId());
- }
+ if(!_ksMgr.validateCertificate(cmd.getCertificate(), cmd.getPrivateKey(), cmd.getDomainSuffix()))
+ throw new InvalidParameterValueException("Failed to pass certificate validation check");
+
+ _ksMgr.saveCertificate(ConsoleProxyManager.CERTIFICATE_NAME, cmd.getCertificate(), cmd.getPrivateKey(), cmd.getDomainSuffix());
+
+ _consoleProxyMgr.setManagementState(ConsoleProxyManagementState.ResetSuspending);
+ return "Certificate has been updated, we will stop all running console proxy VMs for certificate propagation";
}
@Override