Merge branch '4.11'

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2018-04-20 00:42:30 +05:30
commit 644b0910cd
28 changed files with 777 additions and 111 deletions

View File

@ -26,6 +26,7 @@ from cloudutils.configFileOps import configFileOps
from cloudutils.globalEnv import globalEnv from cloudutils.globalEnv import globalEnv
from cloudutils.networkConfig import networkConfig from cloudutils.networkConfig import networkConfig
from cloudutils.syscfg import sysConfigFactory from cloudutils.syscfg import sysConfigFactory
from cloudutils.serviceConfig import configureLibvirtConfig
from optparse import OptionParser from optparse import OptionParser
@ -100,6 +101,7 @@ if __name__ == '__main__':
parser.add_option("-c", "--cluster", dest="cluster", help="cluster id") parser.add_option("-c", "--cluster", dest="cluster", help="cluster id")
parser.add_option("-t", "--hypervisor", default="kvm", dest="hypervisor", help="hypervisor type") parser.add_option("-t", "--hypervisor", default="kvm", dest="hypervisor", help="hypervisor type")
parser.add_option("-g", "--guid", dest="guid", help="guid") parser.add_option("-g", "--guid", dest="guid", help="guid")
parser.add_option("-s", action="store_true", default=False, dest="secure", help="Secure and enable TLS for libvirtd")
parser.add_option("--pubNic", dest="pubNic", help="Public traffic interface") parser.add_option("--pubNic", dest="pubNic", help="Public traffic interface")
parser.add_option("--prvNic", dest="prvNic", help="Private traffic interface") parser.add_option("--prvNic", dest="prvNic", help="Private traffic interface")
parser.add_option("--guestNic", dest="guestNic", help="Guest traffic interface") parser.add_option("--guestNic", dest="guestNic", help="Guest traffic interface")
@ -110,6 +112,12 @@ if __name__ == '__main__':
glbEnv.bridgeType = bridgeType glbEnv.bridgeType = bridgeType
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
if not options.auto and options.secure:
configureLibvirtConfig(True)
print "Libvirtd with TLS configured"
sys.exit(0)
if options.auto is None: if options.auto is None:
userInputs = getUserInputs() userInputs = getUserInputs()
glbEnv.mgtSvr = userInputs[0] glbEnv.mgtSvr = userInputs[0]
@ -138,7 +146,9 @@ if __name__ == '__main__':
glbEnv.nics.append(options.prvNic) glbEnv.nics.append(options.prvNic)
glbEnv.nics.append(options.pubNic) glbEnv.nics.append(options.pubNic)
glbEnv.nics.append(options.guestNic) glbEnv.nics.append(options.guestNic)
glbEnv.secure = options.secure
print "Starting to configure your system:" print "Starting to configure your system:"
syscfg = sysConfigFactory.getSysConfigFactory(glbEnv) syscfg = sysConfigFactory.getSysConfigFactory(glbEnv)
try: try:

View File

@ -42,6 +42,7 @@ import javax.naming.ConfigurationException;
import org.apache.cloudstack.agent.directdownload.SetupDirectDownloadCertificate; import org.apache.cloudstack.agent.directdownload.SetupDirectDownloadCertificate;
import org.apache.cloudstack.agent.lb.SetupMSListAnswer; import org.apache.cloudstack.agent.lb.SetupMSListAnswer;
import org.apache.cloudstack.agent.lb.SetupMSListCommand; import org.apache.cloudstack.agent.lb.SetupMSListCommand;
import org.apache.cloudstack.ca.PostCertificateRenewalCommand;
import org.apache.cloudstack.ca.SetupCertificateAnswer; import org.apache.cloudstack.ca.SetupCertificateAnswer;
import org.apache.cloudstack.ca.SetupCertificateCommand; import org.apache.cloudstack.ca.SetupCertificateCommand;
import org.apache.cloudstack.ca.SetupKeyStoreCommand; import org.apache.cloudstack.ca.SetupKeyStoreCommand;
@ -68,6 +69,7 @@ import com.cloud.agent.api.StartupCommand;
import com.cloud.agent.transport.Request; import com.cloud.agent.transport.Request;
import com.cloud.agent.transport.Response; import com.cloud.agent.transport.Response;
import com.cloud.exception.AgentControlChannelException; import com.cloud.exception.AgentControlChannelException;
import com.cloud.host.Host;
import com.cloud.resource.ServerResource; import com.cloud.resource.ServerResource;
import com.cloud.utils.PropertiesUtil; import com.cloud.utils.PropertiesUtil;
import com.cloud.utils.StringUtils; import com.cloud.utils.StringUtils;
@ -127,6 +129,7 @@ public class Agent implements HandlerFactory, IAgentControl {
Long _id; Long _id;
Timer _timer = new Timer("Agent Timer"); Timer _timer = new Timer("Agent Timer");
Timer certTimer;
Timer hostLBTimer; Timer hostLBTimer;
List<WatchTask> _watchList = new ArrayList<WatchTask>(); List<WatchTask> _watchList = new ArrayList<WatchTask>();
@ -140,9 +143,11 @@ public class Agent implements HandlerFactory, IAgentControl {
long _startupWait = _startupWaitDefault; long _startupWait = _startupWaitDefault;
boolean _reconnectAllowed = true; boolean _reconnectAllowed = true;
//For time sentitive task, e.g. PingTask //For time sentitive task, e.g. PingTask
private final ThreadPoolExecutor _ugentTaskPool; ThreadPoolExecutor _ugentTaskPool;
ExecutorService _executor; ExecutorService _executor;
Thread _shutdownThread = new ShutdownThread(this);
private String _keystoreSetupPath; private String _keystoreSetupPath;
private String _keystoreCertImportPath; private String _keystoreCertImportPath;
@ -153,7 +158,7 @@ public class Agent implements HandlerFactory, IAgentControl {
_connection = new NioClient("Agent", _shell.getNextHost(), _shell.getPort(), _shell.getWorkers(), this); _connection = new NioClient("Agent", _shell.getNextHost(), _shell.getPort(), _shell.getWorkers(), this);
Runtime.getRuntime().addShutdownHook(new ShutdownThread(this)); Runtime.getRuntime().addShutdownHook(_shutdownThread);
_ugentTaskPool = _ugentTaskPool =
new ThreadPoolExecutor(shell.getPingRetries(), 2 * shell.getPingRetries(), 10, TimeUnit.MINUTES, new SynchronousQueue<Runnable>(), new NamedThreadFactory( new ThreadPoolExecutor(shell.getPingRetries(), 2 * shell.getPingRetries(), 10, TimeUnit.MINUTES, new SynchronousQueue<Runnable>(), new NamedThreadFactory(
@ -192,7 +197,7 @@ public class Agent implements HandlerFactory, IAgentControl {
// ((NioClient)_connection).setBindAddress(_shell.getPrivateIp()); // ((NioClient)_connection).setBindAddress(_shell.getPrivateIp());
s_logger.debug("Adding shutdown hook"); s_logger.debug("Adding shutdown hook");
Runtime.getRuntime().addShutdownHook(new ShutdownThread(this)); Runtime.getRuntime().addShutdownHook(_shutdownThread);
_ugentTaskPool = _ugentTaskPool =
new ThreadPoolExecutor(shell.getPingRetries(), 2 * shell.getPingRetries(), 10, TimeUnit.MINUTES, new SynchronousQueue<Runnable>(), new NamedThreadFactory( new ThreadPoolExecutor(shell.getPingRetries(), 2 * shell.getPingRetries(), 10, TimeUnit.MINUTES, new SynchronousQueue<Runnable>(), new NamedThreadFactory(
@ -239,20 +244,39 @@ public class Agent implements HandlerFactory, IAgentControl {
return _resource.getClass().getSimpleName(); return _resource.getClass().getSimpleName();
} }
/**
* In case of a software based agent restart, this method
* can help to perform explicit garbage collection of any old
* agent instances and its inner objects.
*/
private void scavengeOldAgentObjects() {
_executor.submit(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000L);
} catch (final InterruptedException ignored) {
} finally {
System.gc();
}
}
});
}
public void start() { public void start() {
if (!_resource.start()) { if (!_resource.start()) {
s_logger.error("Unable to start the resource: " + _resource.getName()); s_logger.error("Unable to start the resource: " + _resource.getName());
throw new CloudRuntimeException("Unable to start the resource: " + _resource.getName()); throw new CloudRuntimeException("Unable to start the resource: " + _resource.getName());
} }
_keystoreSetupPath = Script.findScript("scripts/util/", KeyStoreUtils.keyStoreSetupScript); _keystoreSetupPath = Script.findScript("scripts/util/", KeyStoreUtils.KS_SETUP_SCRIPT);
if (_keystoreSetupPath == null) { if (_keystoreSetupPath == null) {
throw new CloudRuntimeException(String.format("Unable to find the '%s' script", KeyStoreUtils.keyStoreSetupScript)); throw new CloudRuntimeException(String.format("Unable to find the '%s' script", KeyStoreUtils.KS_SETUP_SCRIPT));
} }
_keystoreCertImportPath = Script.findScript("scripts/util/", KeyStoreUtils.keyStoreImportScript); _keystoreCertImportPath = Script.findScript("scripts/util/", KeyStoreUtils.KS_IMPORT_SCRIPT);
if (_keystoreCertImportPath == null) { if (_keystoreCertImportPath == null) {
throw new CloudRuntimeException(String.format("Unable to find the '%s' script", KeyStoreUtils.keyStoreImportScript)); throw new CloudRuntimeException(String.format("Unable to find the '%s' script", KeyStoreUtils.KS_IMPORT_SCRIPT));
} }
try { try {
@ -274,6 +298,7 @@ public class Agent implements HandlerFactory, IAgentControl {
} }
} }
_shell.updateConnectedHost(); _shell.updateConnectedHost();
scavengeOldAgentObjects();
} }
public void stop(final String reason, final String detail) { public void stop(final String reason, final String detail) {
@ -298,6 +323,7 @@ public class Agent implements HandlerFactory, IAgentControl {
} }
_connection.stop(); _connection.stop();
_connection = null; _connection = null;
_link = null;
} }
if (_resource != null) { if (_resource != null) {
@ -305,7 +331,34 @@ public class Agent implements HandlerFactory, IAgentControl {
_resource = null; _resource = null;
} }
_ugentTaskPool.shutdownNow(); if (_startup != null) {
_startup = null;
}
if (_ugentTaskPool != null) {
_ugentTaskPool.shutdownNow();
_ugentTaskPool = null;
}
if (_executor != null) {
_executor.shutdown();
_executor = null;
}
if (_timer != null) {
_timer.cancel();
_timer = null;
}
if (hostLBTimer != null) {
hostLBTimer.cancel();
hostLBTimer = null;
}
if (certTimer != null) {
certTimer.cancel();
certTimer = null;
}
} }
public Long getId() { public Long getId() {
@ -318,6 +371,15 @@ public class Agent implements HandlerFactory, IAgentControl {
_shell.setPersistentProperty(getResourceName(), "id", Long.toString(id)); _shell.setPersistentProperty(getResourceName(), "id", Long.toString(id));
} }
private synchronized void scheduleServicesRestartTask() {
if (certTimer != null) {
certTimer.cancel();
certTimer.purge();
}
certTimer = new Timer("Certificate Renewal Timer");
certTimer.schedule(new PostCertificateRenewalTask(this), 5000L);
}
private synchronized void scheduleHostLBCheckerTask(final long checkInterval) { private synchronized void scheduleHostLBCheckerTask(final long checkInterval) {
if (hostLBTimer != null) { if (hostLBTimer != null) {
hostLBTimer.cancel(); hostLBTimer.cancel();
@ -578,6 +640,9 @@ public class Agent implements HandlerFactory, IAgentControl {
answer = setupAgentKeystore((SetupKeyStoreCommand) cmd); answer = setupAgentKeystore((SetupKeyStoreCommand) cmd);
} else if (cmd instanceof SetupCertificateCommand && ((SetupCertificateCommand) cmd).isHandleByAgent()) { } else if (cmd instanceof SetupCertificateCommand && ((SetupCertificateCommand) cmd).isHandleByAgent()) {
answer = setupAgentCertificate((SetupCertificateCommand) cmd); answer = setupAgentCertificate((SetupCertificateCommand) cmd);
if (Host.Type.Routing.equals(_resource.getType())) {
scheduleServicesRestartTask();
}
} else if (cmd instanceof SetupDirectDownloadCertificate) { } else if (cmd instanceof SetupDirectDownloadCertificate) {
answer = setupDirectDownloadCertificate((SetupDirectDownloadCertificate) cmd); answer = setupDirectDownloadCertificate((SetupDirectDownloadCertificate) cmd);
} else if (cmd instanceof SetupMSListCommand) { } else if (cmd instanceof SetupMSListCommand) {
@ -641,7 +706,7 @@ public class Agent implements HandlerFactory, IAgentControl {
return new Answer(cmd, false, "Failed to find agent.properties file"); return new Answer(cmd, false, "Failed to find agent.properties file");
} }
final String keyStoreFile = agentFile.getParent() + "/" + KeyStoreUtils.defaultKeystoreFile; final String keyStoreFile = agentFile.getParent() + "/" + KeyStoreUtils.KS_FILENAME;
String cerFile = agentFile.getParent() + "/" + certificateName + ".cer"; String cerFile = agentFile.getParent() + "/" + certificateName + ".cer";
Script.runSimpleBashScript(String.format("echo '%s' > %s", certificate, cerFile)); Script.runSimpleBashScript(String.format("echo '%s' > %s", certificate, cerFile));
@ -666,13 +731,13 @@ public class Agent implements HandlerFactory, IAgentControl {
if (agentFile == null) { if (agentFile == null) {
return new Answer(cmd, false, "Failed to find agent.properties file"); return new Answer(cmd, false, "Failed to find agent.properties file");
} }
final String keyStoreFile = agentFile.getParent() + "/" + KeyStoreUtils.defaultKeystoreFile; final String keyStoreFile = agentFile.getParent() + "/" + KeyStoreUtils.KS_FILENAME;
final String csrFile = agentFile.getParent() + "/" + KeyStoreUtils.defaultCsrFile; final String csrFile = agentFile.getParent() + "/" + KeyStoreUtils.CSR_FILENAME;
String storedPassword = _shell.getPersistentProperty(null, KeyStoreUtils.passphrasePropertyName); String storedPassword = _shell.getPersistentProperty(null, KeyStoreUtils.KS_PASSPHRASE_PROPERTY);
if (Strings.isNullOrEmpty(storedPassword)) { if (Strings.isNullOrEmpty(storedPassword)) {
storedPassword = keyStorePassword; storedPassword = keyStorePassword;
_shell.setPersistentProperty(null, KeyStoreUtils.passphrasePropertyName, storedPassword); _shell.setPersistentProperty(null, KeyStoreUtils.KS_PASSPHRASE_PROPERTY, storedPassword);
} }
Script script = new Script(true, _keystoreSetupPath, 60000, s_logger); Script script = new Script(true, _keystoreSetupPath, 60000, s_logger);
@ -706,10 +771,10 @@ public class Agent implements HandlerFactory, IAgentControl {
if (agentFile == null) { if (agentFile == null) {
return new Answer(cmd, false, "Failed to find agent.properties file"); return new Answer(cmd, false, "Failed to find agent.properties file");
} }
final String keyStoreFile = agentFile.getParent() + "/" + KeyStoreUtils.defaultKeystoreFile; final String keyStoreFile = agentFile.getParent() + "/" + KeyStoreUtils.KS_FILENAME;
final String certFile = agentFile.getParent() + "/" + KeyStoreUtils.defaultCertFile; final String certFile = agentFile.getParent() + "/" + KeyStoreUtils.CERT_FILENAME;
final String privateKeyFile = agentFile.getParent() + "/" + KeyStoreUtils.defaultPrivateKeyFile; final String privateKeyFile = agentFile.getParent() + "/" + KeyStoreUtils.PKEY_FILENAME;
final String caCertFile = agentFile.getParent() + "/" + KeyStoreUtils.defaultCaCertFile; final String caCertFile = agentFile.getParent() + "/" + KeyStoreUtils.CACERT_FILENAME;
try { try {
FileUtils.writeStringToFile(new File(certFile), certificate, Charset.defaultCharset()); FileUtils.writeStringToFile(new File(certFile), certificate, Charset.defaultCharset());
@ -722,7 +787,7 @@ public class Agent implements HandlerFactory, IAgentControl {
Script script = new Script(true, _keystoreCertImportPath, 60000, s_logger); Script script = new Script(true, _keystoreCertImportPath, 60000, s_logger);
script.add(agentFile.getAbsolutePath()); script.add(agentFile.getAbsolutePath());
script.add(keyStoreFile); script.add(keyStoreFile);
script.add(KeyStoreUtils.agentMode); script.add(KeyStoreUtils.AGENT_MODE);
script.add(certFile); script.add(certFile);
script.add(""); script.add("");
script.add(caCertFile); script.add(caCertFile);
@ -1072,6 +1137,60 @@ public class Agent implements HandlerFactory, IAgentControl {
} }
} }
/**
* Task stops the current agent and launches a new agent
* when there are no outstanding jobs in the agent's task queue
*/
public class PostCertificateRenewalTask extends ManagedContextTimerTask {
private Agent agent;
public PostCertificateRenewalTask(final Agent agent) {
this.agent = agent;
}
@Override
protected void runInContext() {
while (true) {
try {
if (_inProgress.get() == 0) {
s_logger.debug("Running post certificate renewal task to restart services.");
// Let the resource perform any post certificate renewal cleanups
_resource.executeRequest(new PostCertificateRenewalCommand());
IAgentShell shell = agent._shell;
ServerResource resource = agent._resource.getClass().newInstance();
// Stop current agent
agent.cancelTasks();
agent._reconnectAllowed = false;
Runtime.getRuntime().removeShutdownHook(agent._shutdownThread);
agent.stop(ShutdownCommand.Requested, "Restarting due to new X509 certificates");
// Nullify references for GC
agent._shell = null;
agent._watchList = null;
agent._shutdownThread = null;
agent._controlListeners = null;
agent = null;
// Start a new agent instance
shell.launchNewAgent(resource);
return;
}
if (s_logger.isTraceEnabled()) {
s_logger.debug("Other tasks are in progress, will retry post certificate renewal command after few seconds");
}
Thread.sleep(5000);
} catch (final Exception e) {
s_logger.warn("Failed to execute post certificate renewal command:", e);
break;
}
}
}
}
public class PreferredHostCheckerTask extends ManagedContextTimerTask { public class PreferredHostCheckerTask extends ManagedContextTimerTask {
@Override @Override

View File

@ -419,7 +419,7 @@ public class AgentShell implements IAgentShell, Daemon {
final Constructor<?> constructor = impl.getDeclaredConstructor(); final Constructor<?> constructor = impl.getDeclaredConstructor();
constructor.setAccessible(true); constructor.setAccessible(true);
ServerResource resource = (ServerResource)constructor.newInstance(); ServerResource resource = (ServerResource)constructor.newInstance();
launchAgent(getNextAgentId(), resource); launchNewAgent(resource);
} catch (final ClassNotFoundException e) { } catch (final ClassNotFoundException e) {
throw new ConfigurationException("Resource class not found: " + name + " due to: " + e.toString()); throw new ConfigurationException("Resource class not found: " + name + " due to: " + e.toString());
} catch (final SecurityException e) { } catch (final SecurityException e) {
@ -447,9 +447,10 @@ public class AgentShell implements IAgentShell, Daemon {
s_logger.trace("Launching agent based on type=" + typeInfo); s_logger.trace("Launching agent based on type=" + typeInfo);
} }
private void launchAgent(int localAgentId, ServerResource resource) throws ConfigurationException { public void launchNewAgent(ServerResource resource) throws ConfigurationException {
// we don't track agent after it is launched for now // we don't track agent after it is launched for now
Agent agent = new Agent(this, localAgentId, resource); _agents.clear();
Agent agent = new Agent(this, getNextAgentId(), resource);
_agents.add(agent); _agents.add(agent);
agent.start(); agent.start();
} }

View File

@ -19,6 +19,9 @@ package com.cloud.agent;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import javax.naming.ConfigurationException;
import com.cloud.resource.ServerResource;
import com.cloud.utils.backoff.BackoffAlgorithm; import com.cloud.utils.backoff.BackoffAlgorithm;
public interface IAgentShell { public interface IAgentShell {
@ -66,4 +69,6 @@ public interface IAgentShell {
void updateConnectedHost(); void updateConnectedHost();
String getConnectedHost(); String getConnectedHost();
void launchNewAgent(ServerResource resource) throws ConfigurationException;
} }

View File

@ -157,11 +157,11 @@ public class VirtualRoutingResource {
"/usr/local/cloud/systemvm/conf/%s " + "/usr/local/cloud/systemvm/conf/%s " +
"%s %d " + "%s %d " +
"/usr/local/cloud/systemvm/conf/%s", "/usr/local/cloud/systemvm/conf/%s",
KeyStoreUtils.defaultKeystoreFile, KeyStoreUtils.KS_FILENAME,
cmd.getKeystorePassword(), cmd.getKeystorePassword(),
cmd.getValidityDays(), cmd.getValidityDays(),
KeyStoreUtils.defaultCsrFile); KeyStoreUtils.CSR_FILENAME);
ExecutionResult result = _vrDeployer.executeInVR(cmd.getRouterAccessIp(), KeyStoreUtils.keyStoreSetupScript, args); ExecutionResult result = _vrDeployer.executeInVR(cmd.getRouterAccessIp(), KeyStoreUtils.KS_SETUP_SCRIPT, args);
return new SetupKeystoreAnswer(result.getDetails()); return new SetupKeystoreAnswer(result.getDetails());
} }
@ -171,15 +171,15 @@ public class VirtualRoutingResource {
"/usr/local/cloud/systemvm/conf/%s \"%s\" " + "/usr/local/cloud/systemvm/conf/%s \"%s\" " +
"/usr/local/cloud/systemvm/conf/%s \"%s\" " + "/usr/local/cloud/systemvm/conf/%s \"%s\" " +
"/usr/local/cloud/systemvm/conf/%s \"%s\"", "/usr/local/cloud/systemvm/conf/%s \"%s\"",
KeyStoreUtils.defaultKeystoreFile, KeyStoreUtils.KS_FILENAME,
KeyStoreUtils.sshMode, KeyStoreUtils.SSH_MODE,
KeyStoreUtils.defaultCertFile, KeyStoreUtils.CERT_FILENAME,
cmd.getEncodedCertificate(), cmd.getEncodedCertificate(),
KeyStoreUtils.defaultCaCertFile, KeyStoreUtils.CACERT_FILENAME,
cmd.getEncodedCaCertificates(), cmd.getEncodedCaCertificates(),
KeyStoreUtils.defaultPrivateKeyFile, KeyStoreUtils.PKEY_FILENAME,
cmd.getEncodedPrivateKey()); cmd.getEncodedPrivateKey());
ExecutionResult result = _vrDeployer.executeInVR(cmd.getRouterAccessIp(), KeyStoreUtils.keyStoreImportScript, args); ExecutionResult result = _vrDeployer.executeInVR(cmd.getRouterAccessIp(), KeyStoreUtils.KS_IMPORT_SCRIPT, args);
return new SetupCertificateAnswer(result.isSuccess()); return new SetupCertificateAnswer(result.isSuccess());
} }

View File

@ -0,0 +1,34 @@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
package org.apache.cloudstack.ca;
import com.cloud.agent.api.Command;
public class PostCertificateRenewalCommand extends Command {
public PostCertificateRenewalCommand() {
super();
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -82,15 +82,15 @@ public class SetupCertificateCommand extends NetworkElementCommand {
} }
public String getEncodedPrivateKey() { public String getEncodedPrivateKey() {
return privateKey.replace("\n", KeyStoreUtils.certNewlineEncoder).replace(" ", KeyStoreUtils.certSpaceEncoder); return privateKey.replace("\n", KeyStoreUtils.CERT_NEWLINE_ENCODER).replace(" ", KeyStoreUtils.CERT_SPACE_ENCODER);
} }
public String getEncodedCertificate() { public String getEncodedCertificate() {
return certificate.replace("\n", KeyStoreUtils.certNewlineEncoder).replace(" ", KeyStoreUtils.certSpaceEncoder); return certificate.replace("\n", KeyStoreUtils.CERT_NEWLINE_ENCODER).replace(" ", KeyStoreUtils.CERT_SPACE_ENCODER);
} }
public String getEncodedCaCertificates() { public String getEncodedCaCertificates() {
return caCertificates.replace("\n", KeyStoreUtils.certNewlineEncoder).replace(" ", KeyStoreUtils.certSpaceEncoder); return caCertificates.replace("\n", KeyStoreUtils.CERT_NEWLINE_ENCODER).replace(" ", KeyStoreUtils.CERT_SPACE_ENCODER);
} }
public boolean isHandleByAgent() { public boolean isHandleByAgent() {

View File

@ -50,6 +50,7 @@ case "$1" in
mkdir /etc/libvirt/hooks mkdir /etc/libvirt/hooks
fi fi
cp -a /usr/share/cloudstack-agent/lib/libvirtqemuhook /etc/libvirt/hooks/qemu cp -a /usr/share/cloudstack-agent/lib/libvirtqemuhook /etc/libvirt/hooks/qemu
;; ;;
esac esac

View File

@ -241,7 +241,7 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
@Override @Override
public char[] getKeyStorePassphrase() { public char[] getKeyStorePassphrase() {
return KeyStoreUtils.defaultKeystorePassphrase; return KeyStoreUtils.DEFAULT_KS_PASSPHRASE;
} }
///////////////////////////////////////////////// /////////////////////////////////////////////////

View File

@ -0,0 +1,52 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.hypervisor.kvm.resource.wrapper;
import org.apache.cloudstack.ca.PostCertificateRenewalCommand;
import org.apache.cloudstack.ca.SetupCertificateAnswer;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Answer;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper;
import com.cloud.utils.script.Script;
@ResourceWrapper(handles = PostCertificateRenewalCommand.class)
public final class LibvirtPostCertificateRenewalCommandWrapper extends CommandWrapper<PostCertificateRenewalCommand, Answer, LibvirtComputingResource> {
private static final Logger s_logger = Logger.getLogger(LibvirtPostCertificateRenewalCommandWrapper.class);
@Override
public Answer execute(final PostCertificateRenewalCommand command, final LibvirtComputingResource serverResource) {
s_logger.info("Restarting libvirt after certificate provisioning/renewal");
if (command != null) {
final int timeout = 30000;
Script script = new Script(true, "service", timeout, s_logger);
if ("Ubuntu".equals(serverResource.getHostDistro()) || "Debian".equals(serverResource.getHostDistro())) {
script.add("libvirt-bin");
} else {
script.add("libvirtd");
}
script.add("restart");
script.execute();
return new SetupCertificateAnswer(true);
}
return new SetupCertificateAnswer(false);
}
}

View File

@ -41,11 +41,19 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.cloudstack.utils.hypervisor.HypervisorUtils;
import org.apache.cloudstack.utils.linux.CPUStat;
import org.apache.cloudstack.utils.linux.MemStat;
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
import org.apache.cloudstack.utils.security.KeyStoreUtils;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.ArrayUtils;
@ -68,14 +76,6 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import com.google.common.base.Strings;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.cloudstack.utils.hypervisor.HypervisorUtils;
import org.apache.cloudstack.utils.linux.CPUStat;
import org.apache.cloudstack.utils.linux.MemStat;
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
import com.cloud.agent.api.Answer; import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command; import com.cloud.agent.api.Command;
@ -168,6 +168,7 @@ import com.cloud.utils.ssh.SshHelper;
import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.PowerState; import com.cloud.vm.VirtualMachine.PowerState;
import com.cloud.vm.VmDetailConstants; import com.cloud.vm.VmDetailConstants;
import com.google.common.base.Strings;
/** /**
* LibvirtComputingResource execute requests on the computing/routing host using * LibvirtComputingResource execute requests on the computing/routing host using
@ -239,6 +240,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
protected long _hypervisorLibvirtVersion; protected long _hypervisorLibvirtVersion;
protected long _hypervisorQemuVersion; protected long _hypervisorQemuVersion;
protected String _hypervisorPath; protected String _hypervisorPath;
protected String _hostDistro;
protected String _networkDirectSourceMode; protected String _networkDirectSourceMode;
protected String _networkDirectDevice; protected String _networkDirectDevice;
protected String _sysvmISOPath; protected String _sysvmISOPath;
@ -2599,11 +2601,16 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
fillNetworkInformation(cmd); fillNetworkInformation(cmd);
_privateIp = cmd.getPrivateIpAddress(); _privateIp = cmd.getPrivateIpAddress();
cmd.getHostDetails().putAll(getVersionStrings()); cmd.getHostDetails().putAll(getVersionStrings());
cmd.getHostDetails().put(KeyStoreUtils.SECURED, String.valueOf(isHostSecured()).toLowerCase());
cmd.setPool(_pool); cmd.setPool(_pool);
cmd.setCluster(_clusterId); cmd.setCluster(_clusterId);
cmd.setGatewayIpAddress(_localGateway); cmd.setGatewayIpAddress(_localGateway);
cmd.setIqn(getIqn()); cmd.setIqn(getIqn());
if (cmd.getHostDetails().containsKey("Host.OS")) {
_hostDistro = cmd.getHostDetails().get("Host.OS");
}
StartupStorageCommand sscmd = null; StartupStorageCommand sscmd = null;
try { try {
@ -3777,4 +3784,24 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
public long getTotalMemory() { public long getTotalMemory() {
return _totalMemory; return _totalMemory;
} }
public String getHostDistro() {
return _hostDistro;
}
public boolean isHostSecured() {
// Test for host certificates
final File confFile = PropertiesUtil.findConfigFile(KeyStoreUtils.AGENT_PROPSFILE);
if (confFile == null || !confFile.exists() || !new File(confFile.getParent() + "/" + KeyStoreUtils.CERT_FILENAME).exists()) {
return false;
}
// Test for libvirt TLS configuration
try {
new Connect(String.format("qemu+tls://%s/system", _privateIp));
} catch (final LibvirtException ignored) {
return false;
}
return true;
}
} }

View File

@ -20,8 +20,8 @@
package com.cloud.hypervisor.kvm.resource.wrapper; package com.cloud.hypervisor.kvm.resource.wrapper;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -46,12 +46,10 @@ import javax.xml.transform.stream.StreamResult;
import org.apache.commons.collections.MapUtils; import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.libvirt.Connect; import org.libvirt.Connect;
import org.libvirt.Domain; import org.libvirt.Domain;
import org.libvirt.DomainInfo.DomainState; import org.libvirt.DomainInfo.DomainState;
import org.libvirt.LibvirtException; import org.libvirt.LibvirtException;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NamedNodeMap;
@ -71,6 +69,7 @@ import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper; import com.cloud.resource.ResourceWrapper;
import com.cloud.utils.Ternary; import com.cloud.utils.Ternary;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.google.common.base.Strings;
@ResourceWrapper(handles = MigrateCommand.class) @ResourceWrapper(handles = MigrateCommand.class)
public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCommand, Answer, LibvirtComputingResource> { public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCommand, Answer, LibvirtComputingResource> {
@ -80,9 +79,17 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
private static final String CONTENTS_WILDCARD = "(?s).*"; private static final String CONTENTS_WILDCARD = "(?s).*";
private static final Logger s_logger = Logger.getLogger(LibvirtMigrateCommandWrapper.class); private static final Logger s_logger = Logger.getLogger(LibvirtMigrateCommandWrapper.class);
protected String createMigrationURI(final String destinationIp, final LibvirtComputingResource libvirtComputingResource) {
if (Strings.isNullOrEmpty(destinationIp)) {
throw new CloudRuntimeException("Provided libvirt destination ip is invalid");
}
return String.format("%s://%s/system", libvirtComputingResource.isHostSecured() ? "qemu+tls" : "qemu+tcp", destinationIp);
}
@Override @Override
public Answer execute(final MigrateCommand command, final LibvirtComputingResource libvirtComputingResource) { public Answer execute(final MigrateCommand command, final LibvirtComputingResource libvirtComputingResource) {
final String vmName = command.getVmName(); final String vmName = command.getVmName();
final String destinationUri = createMigrationURI(command.getDestinationIp(), libvirtComputingResource);
String result = null; String result = null;
@ -140,10 +147,10 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
xmlDesc = replaceStorage(xmlDesc, mapMigrateStorage); xmlDesc = replaceStorage(xmlDesc, mapMigrateStorage);
} }
dconn = libvirtUtilitiesHelper.retrieveQemuConnection("qemu+tcp://" + command.getDestinationIp() + "/system"); dconn = libvirtUtilitiesHelper.retrieveQemuConnection(destinationUri);
//run migration in thread so we can monitor it //run migration in thread so we can monitor it
s_logger.info("Live migration of instance " + vmName + " initiated"); s_logger.info("Live migration of instance " + vmName + " initiated to destination host: " + dconn.getURI());
final ExecutorService executor = Executors.newFixedThreadPool(1); final ExecutorService executor = Executors.newFixedThreadPool(1);
final Callable<Domain> worker = new MigrateKVMAsync(libvirtComputingResource, dm, dconn, xmlDesc, migrateStorage, final Callable<Domain> worker = new MigrateKVMAsync(libvirtComputingResource, dm, dconn, xmlDesc, migrateStorage,
command.isAutoConvergence(), vmName, command.getDestinationIp()); command.isAutoConvergence(), vmName, command.getDestinationIp());
@ -203,6 +210,9 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
} catch (final LibvirtException e) { } catch (final LibvirtException e) {
s_logger.debug("Can't migrate domain: " + e.getMessage()); s_logger.debug("Can't migrate domain: " + e.getMessage());
result = e.getMessage(); result = e.getMessage();
if (result.startsWith("unable to connect to server") && result.endsWith("refused")) {
result = String.format("Migration was refused connection to destination: %s. Please check libvirt configuration compatibility and firewall rules on the source and destination hosts.", destinationUri);
}
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
s_logger.debug("Interrupted while migrating domain: " + e.getMessage()); s_logger.debug("Interrupted while migrating domain: " + e.getMessage());
result = e.getMessage(); result = e.getMessage();

View File

@ -38,7 +38,7 @@ import com.cloud.utils.exception.CloudRuntimeException;
@ResourceWrapper(handles = ModifyTargetsCommand.class) @ResourceWrapper(handles = ModifyTargetsCommand.class)
public final class LibvirtModifyTargetsCommandWrapper extends CommandWrapper<ModifyTargetsCommand, Answer, LibvirtComputingResource> { public final class LibvirtModifyTargetsCommandWrapper extends CommandWrapper<ModifyTargetsCommand, Answer, LibvirtComputingResource> {
private static final Logger s_logger = Logger.getLogger(LibvirtMigrateCommandWrapper.class); private static final Logger s_logger = Logger.getLogger(LibvirtModifyTargetsCommandWrapper.class);
@Override @Override
public Answer execute(final ModifyTargetsCommand command, final LibvirtComputingResource libvirtComputingResource) { public Answer execute(final ModifyTargetsCommand command, final LibvirtComputingResource libvirtComputingResource) {

View File

@ -18,10 +18,14 @@
// //
package com.cloud.hypervisor.kvm.resource.wrapper; package com.cloud.hypervisor.kvm.resource.wrapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import org.junit.Test; import org.junit.Test;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
import com.cloud.utils.exception.CloudRuntimeException;
public class LibvirtMigrateCommandWrapperTest { public class LibvirtMigrateCommandWrapperTest {
String fullfile = String fullfile =
"<domain type='kvm' id='4'>\n" + "<domain type='kvm' id='4'>\n" +
@ -303,4 +307,22 @@ public class LibvirtMigrateCommandWrapperTest {
final String result = lw.replaceIpForVNCInDescFile(xmlDesc, targetIp); final String result = lw.replaceIpForVNCInDescFile(xmlDesc, targetIp);
assertTrue("transformation does not live up to expectation:\n" + result, expectedXmlDesc.equals(result)); assertTrue("transformation does not live up to expectation:\n" + result, expectedXmlDesc.equals(result));
} }
@Test
public void testMigrationUri() {
final String ip = "10.1.1.1";
LibvirtMigrateCommandWrapper lw = new LibvirtMigrateCommandWrapper();
LibvirtComputingResource lcr = new LibvirtComputingResource();
if (lcr.isHostSecured()) {
assertEquals(lw.createMigrationURI(ip, lcr), String.format("qemu+tls://%s/system", ip));
} else {
assertEquals(lw.createMigrationURI(ip, lcr), String.format("qemu+tcp://%s/system", ip));
}
}
@Test(expected = CloudRuntimeException.class)
public void testMigrationUriException() {
LibvirtMigrateCommandWrapper lw = new LibvirtMigrateCommandWrapper();
lw.createMigrationURI(null, new LibvirtComputingResource());
}
} }

View File

@ -802,9 +802,9 @@ class SetupFirewall(ConfigTask):
rule = "-p tcp -m tcp --dport 16509 -j ACCEPT" rule = "-p tcp -m tcp --dport 16509 -j ACCEPT"
if rule in iptablessave().stdout: return True if rule in iptablessave().stdout: return True
return False return False
def execute(self): def execute(self):
ports = "22 1798 16509".split() ports = "22 1798 16509 16514".split()
if distro in (Fedora , CentOS, RHEL6): if distro in (Fedora , CentOS, RHEL6):
for p in ports: iptables("-I","INPUT","1","-p","tcp","--dport",p,'-j','ACCEPT') for p in ports: iptables("-I","INPUT","1","-p","tcp","--dport",p,'-j','ACCEPT')
o = service.iptables.save() ; print o.stdout + o.stderr o = service.iptables.save() ; print o.stdout + o.stderr

View File

@ -471,6 +471,23 @@ class securityPolicyConfigRedhat(serviceCfgBase):
logging.debug(formatExceptionInfo()) logging.debug(formatExceptionInfo())
return False return False
def configureLibvirtConfig(tls_enabled = True, cfg = None):
cfo = configFileOps("/etc/libvirt/libvirtd.conf", cfg)
if tls_enabled:
cfo.addEntry("listen_tcp", "0")
cfo.addEntry("listen_tls", "1")
cfo.addEntry("key_file", "\"/etc/pki/libvirt/private/serverkey.pem\"")
cfo.addEntry("cert_file", "\"/etc/pki/libvirt/servercert.pem\"")
cfo.addEntry("ca_file", "\"/etc/pki/CA/cacert.pem\"")
else:
cfo.addEntry("listen_tcp", "1")
cfo.addEntry("listen_tls", "0")
cfo.addEntry("tcp_port", "\"16509\"")
cfo.addEntry("tls_port", "\"16514\"")
cfo.addEntry("auth_tcp", "\"none\"")
cfo.addEntry("auth_tls", "\"none\"")
cfo.save()
class libvirtConfigRedhat(serviceCfgBase): class libvirtConfigRedhat(serviceCfgBase):
def __init__(self, syscfg): def __init__(self, syscfg):
super(libvirtConfigRedhat, self).__init__(syscfg) super(libvirtConfigRedhat, self).__init__(syscfg)
@ -478,12 +495,7 @@ class libvirtConfigRedhat(serviceCfgBase):
def config(self): def config(self):
try: try:
cfo = configFileOps("/etc/libvirt/libvirtd.conf", self) configureLibvirtConfig(self.syscfg.env.secure, self)
cfo.addEntry("listen_tcp", "1")
cfo.addEntry("tcp_port", "\"16509\"")
cfo.addEntry("auth_tcp", "\"none\"")
cfo.addEntry("listen_tls", "0")
cfo.save()
cfo = configFileOps("/etc/sysconfig/libvirtd", self) cfo = configFileOps("/etc/sysconfig/libvirtd", self)
cfo.addEntry("export CGROUP_DAEMON", "'cpu:/virt'") cfo.addEntry("export CGROUP_DAEMON", "'cpu:/virt'")
@ -516,12 +528,7 @@ class libvirtConfigUbuntu(serviceCfgBase):
self.serviceName = "Libvirt" self.serviceName = "Libvirt"
def setupLiveMigration(self): def setupLiveMigration(self):
cfo = configFileOps("/etc/libvirt/libvirtd.conf", self) configureLibvirtConfig(self.syscfg.env.secure, self)
cfo.addEntry("listen_tcp", "1")
cfo.addEntry("tcp_port", "\"16509\"");
cfo.addEntry("auth_tcp", "\"none\"");
cfo.addEntry("listen_tls", "0")
cfo.save()
if os.path.exists("/etc/init/libvirt-bin.conf"): if os.path.exists("/etc/init/libvirt-bin.conf"):
cfo = configFileOps("/etc/init/libvirt-bin.conf", self) cfo = configFileOps("/etc/init/libvirt-bin.conf", self)
@ -567,7 +574,7 @@ class firewallConfigUbuntu(serviceCfgBase):
def config(self): def config(self):
try: try:
ports = "22 1798 16509".split() ports = "22 1798 16509 16514".split()
for p in ports: for p in ports:
bash("ufw allow %s"%p) bash("ufw allow %s"%p)
bash("ufw allow proto tcp from any to any port 5900:6100") bash("ufw allow proto tcp from any to any port 5900:6100")
@ -627,7 +634,7 @@ class firewallConfigBase(serviceCfgBase):
class firewallConfigAgent(firewallConfigBase): class firewallConfigAgent(firewallConfigBase):
def __init__(self, syscfg): def __init__(self, syscfg):
super(firewallConfigAgent, self).__init__(syscfg) super(firewallConfigAgent, self).__init__(syscfg)
self.ports = "22 16509 5900:6100 49152:49216".split() self.ports = "22 16509 16514 5900:6100 49152:49216".split()
if syscfg.env.distribution.getVersion() == "CentOS": if syscfg.env.distribution.getVersion() == "CentOS":
self.rules = ["-D FORWARD -j RH-Firewall-1-INPUT"] self.rules = ["-D FORWARD -j RH-Firewall-1-INPUT"]
else: else:

View File

@ -28,6 +28,7 @@ PRIVKEY=$(echo "$9" | tr '^' '\n' | tr '~' ' ')
ALIAS="cloud" ALIAS="cloud"
SYSTEM_FILE="/var/cache/cloud/cmdline" SYSTEM_FILE="/var/cache/cloud/cmdline"
LIBVIRTD_FILE="/etc/libvirt/libvirtd.conf"
# Find keystore password # Find keystore password
KS_PASS=$(sed -n '/keystore.passphrase/p' "$PROPS_FILE" 2>/dev/null | sed 's/keystore.passphrase=//g' 2>/dev/null) KS_PASS=$(sed -n '/keystore.passphrase/p' "$PROPS_FILE" 2>/dev/null | sed 's/keystore.passphrase=//g' 2>/dev/null)
@ -78,6 +79,18 @@ fi
rm -f "$NEW_KS_FILE.p12" rm -f "$NEW_KS_FILE.p12"
mv -f "$NEW_KS_FILE" "$KS_FILE" mv -f "$NEW_KS_FILE" "$KS_FILE"
# Secure libvirtd on cert import
if [ -f "$LIBVIRTD_FILE" ]; then
mkdir -p /etc/pki/CA
mkdir -p /etc/pki/libvirt/private
ln -sf /etc/cloudstack/agent/cloud.ca.crt /etc/pki/CA/cacert.pem
ln -sf /etc/cloudstack/agent/cloud.crt /etc/pki/libvirt/clientcert.pem
ln -sf /etc/cloudstack/agent/cloud.crt /etc/pki/libvirt/servercert.pem
ln -sf /etc/cloudstack/agent/cloud.key /etc/pki/libvirt/private/clientkey.pem
ln -sf /etc/cloudstack/agent/cloud.key /etc/pki/libvirt/private/serverkey.pem
cloudstack-setup-agent -s > /dev/null
fi
# Update ca-certs if we're in systemvm # Update ca-certs if we're in systemvm
if [ -f "$SYSTEM_FILE" ]; then if [ -f "$SYSTEM_FILE" ]; then
mkdir -p /usr/local/share/ca-certificates/cloudstack mkdir -p /usr/local/share/ca-certificates/cloudstack

View File

@ -18,6 +18,7 @@ package com.cloud.hypervisor.kvm.discoverer;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.URI; import java.net.URI;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -141,11 +142,6 @@ public abstract class LibvirtServerDiscoverer extends DiscovererBase implements
} }
private void setupAgentSecurity(final Connection sshConnection, final String agentIp, final String agentHostname) { private void setupAgentSecurity(final Connection sshConnection, final String agentIp, final String agentHostname) {
if (!caManager.canProvisionCertificates()) {
s_logger.warn("Cannot secure agent communication because configure CA plugin cannot provision client certificate");
return;
}
if (sshConnection == null) { if (sshConnection == null) {
throw new CloudRuntimeException("Cannot secure agent communication because ssh connection is invalid for host ip=" + agentIp); throw new CloudRuntimeException("Cannot secure agent communication because ssh connection is invalid for host ip=" + agentIp);
} }
@ -161,17 +157,17 @@ public abstract class LibvirtServerDiscoverer extends DiscovererBase implements
"/etc/cloudstack/agent/%s " + "/etc/cloudstack/agent/%s " +
"%s %d " + "%s %d " +
"/etc/cloudstack/agent/%s", "/etc/cloudstack/agent/%s",
KeyStoreUtils.keyStoreSetupScript, KeyStoreUtils.KS_SETUP_SCRIPT,
KeyStoreUtils.defaultKeystoreFile, KeyStoreUtils.KS_FILENAME,
PasswordGenerator.generateRandomPassword(16), PasswordGenerator.generateRandomPassword(16),
validityPeriod, validityPeriod,
KeyStoreUtils.defaultCsrFile)); KeyStoreUtils.CSR_FILENAME));
if (!keystoreSetupResult.isSuccess()) { if (!keystoreSetupResult.isSuccess()) {
throw new CloudRuntimeException("Failed to setup keystore on the KVM host: " + agentIp); throw new CloudRuntimeException("Failed to setup keystore on the KVM host: " + agentIp);
} }
final Certificate certificate = caManager.issueCertificate(keystoreSetupResult.getStdOut(), Collections.singletonList(agentHostname), Collections.singletonList(agentIp), null, null); final Certificate certificate = caManager.issueCertificate(keystoreSetupResult.getStdOut(), Arrays.asList(agentHostname, agentIp), Collections.singletonList(agentIp), null, null);
if (certificate == null || certificate.getClientCertificate() == null) { if (certificate == null || certificate.getClientCertificate() == null) {
throw new CloudRuntimeException("Failed to issue certificates for KVM host agent: " + agentIp); throw new CloudRuntimeException("Failed to issue certificates for KVM host agent: " + agentIp);
} }
@ -184,14 +180,14 @@ public abstract class LibvirtServerDiscoverer extends DiscovererBase implements
"/etc/cloudstack/agent/%s \"%s\" " + "/etc/cloudstack/agent/%s \"%s\" " +
"/etc/cloudstack/agent/%s \"%s\" " + "/etc/cloudstack/agent/%s \"%s\" " +
"/etc/cloudstack/agent/%s \"%s\"", "/etc/cloudstack/agent/%s \"%s\"",
KeyStoreUtils.keyStoreImportScript, KeyStoreUtils.KS_IMPORT_SCRIPT,
KeyStoreUtils.defaultKeystoreFile, KeyStoreUtils.KS_FILENAME,
KeyStoreUtils.sshMode, KeyStoreUtils.SSH_MODE,
KeyStoreUtils.defaultCertFile, KeyStoreUtils.CERT_FILENAME,
certificateCommand.getEncodedCertificate(), certificateCommand.getEncodedCertificate(),
KeyStoreUtils.defaultCaCertFile, KeyStoreUtils.CACERT_FILENAME,
certificateCommand.getEncodedCaCertificates(), certificateCommand.getEncodedCaCertificates(),
KeyStoreUtils.defaultPrivateKeyFile, KeyStoreUtils.PKEY_FILENAME,
certificateCommand.getEncodedPrivateKey())); certificateCommand.getEncodedPrivateKey()));
if (setupCertResult != null && !setupCertResult.isSuccess()) { if (setupCertResult != null && !setupCertResult.isSuccess()) {
@ -288,9 +284,13 @@ public abstract class LibvirtServerDiscoverer extends DiscovererBase implements
kvmGuestNic = (kvmPublicNic != null) ? kvmPublicNic : kvmPrivateNic; kvmGuestNic = (kvmPublicNic != null) ? kvmPublicNic : kvmPrivateNic;
} }
if (!caManager.canProvisionCertificates()) {
throw new CloudRuntimeException("Configured CA plugin cannot provision X509 certificate(s), failing to add host due to security insufficiency.");
}
setupAgentSecurity(sshConnection, agentIp, hostname); setupAgentSecurity(sshConnection, agentIp, hostname);
String parameters = " -m " + StringUtils.toCSVList(indirectAgentLB.getManagementServerList(null, dcId, null)) + " -z " + dcId + " -p " + podId + " -c " + clusterId + " -g " + guid + " -a"; String parameters = " -m " + StringUtils.toCSVList(indirectAgentLB.getManagementServerList(null, dcId, null)) + " -z " + dcId + " -p " + podId + " -c " + clusterId + " -g " + guid + " -a -s ";
parameters += " --pubNic=" + kvmPublicNic; parameters += " --pubNic=" + kvmPublicNic;
parameters += " --prvNic=" + kvmPrivateNic; parameters += " --prvNic=" + kvmPrivateNic;

View File

@ -27,7 +27,6 @@ import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
@ -191,8 +190,7 @@ public class CAManagerImpl extends ManagerBase implements CAManager {
if (Strings.isNullOrEmpty(csr)) { if (Strings.isNullOrEmpty(csr)) {
return false; return false;
} }
final Certificate certificate = issueCertificate(csr, Collections.singletonList(host.getName()), final Certificate certificate = issueCertificate(csr, Arrays.asList(host.getName(), host.getPrivateIpAddress()), Arrays.asList(host.getPrivateIpAddress(), host.getPublicIpAddress(), host.getStorageIpAddress()), CAManager.CertValidityPeriod.value(), caProvider);
Arrays.asList(host.getPrivateIpAddress(), host.getPublicIpAddress(), host.getStorageIpAddress()), CAManager.CertValidityPeriod.value(), caProvider);
return deployCertificate(host, certificate, reconnect, null); return deployCertificate(host, certificate, reconnect, null);
} catch (final AgentUnavailableException | OperationTimedoutException e) { } catch (final AgentUnavailableException | OperationTimedoutException e) {
LOG.error("Host/agent is not available or operation timed out, failed to setup keystore and generate CSR for host/agent id=" + host.getId() + ", due to: ", e); LOG.error("Host/agent is not available or operation timed out, failed to setup keystore and generate CSR for host/agent id=" + host.getId() + ", due to: ", e);

View File

@ -585,7 +585,7 @@ routing_svcs() {
systemctl enable haproxy systemctl enable haproxy
echo "haproxy apache2" > /var/cache/cloud/enabled_svcs echo "haproxy apache2" > /var/cache/cloud/enabled_svcs
echo "cloud nfs-common portmap" > /var/cache/cloud/disabled_svcs echo "cloud nfs-common portmap" > /var/cache/cloud/disabled_svcs
if [ $RROUTER -eq 1 ] if [ "$RROUTER" -eq "1" ]
then then
systemctl disable --now dnsmasq systemctl disable --now dnsmasq
systemctl enable conntrackd systemctl enable conntrackd

View File

@ -21,9 +21,11 @@ from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.cloudstackAPI import (recoverVirtualMachine, from marvin.cloudstackAPI import (recoverVirtualMachine,
destroyVirtualMachine, destroyVirtualMachine,
attachIso, attachIso,
detachIso) detachIso,
from marvin.lib.utils import (cleanup_resources, provisionCertificate,
validateList) updateConfiguration)
from marvin.lib.utils import *
from marvin.lib.base import (Account, from marvin.lib.base import (Account,
ServiceOffering, ServiceOffering,
VirtualMachine, VirtualMachine,
@ -33,11 +35,13 @@ from marvin.lib.base import (Account,
Configurations) Configurations)
from marvin.lib.common import (get_domain, from marvin.lib.common import (get_domain,
get_zone, get_zone,
get_template) get_template,
list_hosts)
from marvin.codes import FAILED, PASS from marvin.codes import FAILED, PASS
from nose.plugins.attrib import attr from nose.plugins.attrib import attr
#Import System modules #Import System modules
import time import time
import re
_multiprocess_shared_ = True _multiprocess_shared_ = True
class TestDeployVM(cloudstackTestCase): class TestDeployVM(cloudstackTestCase):
@ -781,3 +785,301 @@ class TestVMLifeCycle(cloudstackTestCase):
"Check if ISO is detached from virtual machine" "Check if ISO is detached from virtual machine"
) )
return return
class TestSecuredVmMigration(cloudstackTestCase):
@classmethod
def setUpClass(cls):
testClient = super(TestSecuredVmMigration, cls).getClsTestClient()
cls.apiclient = testClient.getApiClient()
cls.services = testClient.getParsedTestDataConfig()
cls.hypervisor = testClient.getHypervisorInfo()
# Get Zone, Domain and templates
domain = get_domain(cls.apiclient)
cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
cls.services['mode'] = cls.zone.networktype
cls.hostConfig = cls.config.__dict__["zones"][0].__dict__["pods"][0].__dict__["clusters"][0].__dict__["hosts"][0].__dict__
cls.management_ip = cls.config.__dict__["mgtSvr"][0].__dict__["mgtSvrIp"]
template = get_template(
cls.apiclient,
cls.zone.id,
cls.services["ostype"]
)
if template == FAILED:
assert False, "get_template() failed to return template with description %s" % cls.services["ostype"]
# Set Zones and disk offerings
cls.services["small"]["zoneid"] = cls.zone.id
cls.services["small"]["template"] = template.id
cls.services["iso1"]["zoneid"] = cls.zone.id
# Create VMs, NAT Rules etc
cls.account = Account.create(
cls.apiclient,
cls.services["account"],
domainid=domain.id
)
cls.small_offering = ServiceOffering.create(
cls.apiclient,
cls.services["service_offerings"]["small"]
)
cls._cleanup = [
cls.small_offering,
cls.account
]
@classmethod
def tearDownClass(cls):
cls.apiclient = super(TestSecuredVmMigration, cls).getClsTestClient().getApiClient()
try:
cleanup_resources(cls.apiclient, cls._cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
def setUp(self):
self.apiclient = self.testClient.getApiClient()
self.dbclient = self.testClient.getDbConnection()
self.cleanup = []
self.updateConfiguration("ca.plugin.root.auth.strictness", "false")
self.make_all_hosts_secure()
if self.hypervisor.lower() not in ["kvm"]:
self.skipTest("Secured migration is not supported on other than KVM")
def tearDown(self):
self.make_all_hosts_secure()
try:
cleanup_resources(self.apiclient, self.cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
def test_01_secured_vm_migration(self):
"""Test secured VM migration"""
# Validate the following
# 1. Environment has enough hosts for migration
# 2. DeployVM on suitable host (with another host in the cluster)
# 3. Migrate the VM and assert migration successful
hosts = self.get_hosts()
secured_hosts = []
for host in hosts:
if host.details.secured == 'true':
secured_hosts.append(host)
if len(secured_hosts) < 2:
self.skipTest("At least two hosts should be present in the zone for migration")
origin_host = secured_hosts[0]
self.vm_to_migrate = self.deploy_vm(origin_host)
target_host = self.get_target_host(secured='true', virtualmachineid=self.vm_to_migrate.id)
self.migrate_and_check(origin_host, target_host, proto='tls')
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
def test_02_not_secured_vm_migration(self):
"""Test Non-secured VM Migration
"""
#self.skipTest()
# Validate the following
# 1. Prepare 2 hosts to run in non-secured more
# 2. DeployVM on suitable host (with another host in the cluster)
# 3. Migrate the VM and assert migration successful
hosts = self.get_hosts()
for host in hosts:
self.make_unsecure_connection(host)
non_secured_hosts = []
hosts = self.get_hosts()
for host in hosts:
if host.details.secured == 'false':
non_secured_hosts.append(host)
if len(non_secured_hosts) < 2:
self.skipTest("At least two hosts should be present in the zone for migration")
origin_host = non_secured_hosts[0]
self.vm_to_migrate = self.deploy_vm(origin_host)
target_host = self.get_target_host(secured='false', virtualmachineid=self.vm_to_migrate.id)
self.migrate_and_check(origin_host, target_host, proto='tcp')
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
def test_03_secured_to_nonsecured_vm_migration(self):
"""Test destroy Virtual Machine
"""
# Validate the following
# 1. Makes one of the hosts non-secured
# 2. Deploys a VM to a Secured host
# 3. Migrates the VM to the non-secured host and assers the migration is via TCP.
hosts = self.get_hosts()
non_secured_host = self.make_unsecure_connection(hosts[0])
secured_hosts = []
hosts = self.get_hosts()
for host in hosts:
if host.details.secured == 'true':
secured_hosts.append(host)
self.vm_to_migrate = self.deploy_vm(secured_hosts[0])
try:
self.migrate_and_check(origin_host=secured_hosts[0], destination_host=non_secured_host, proto='tcp')
except Exception:
pass
else: self.fail("Migration succeed, instead it should fail")
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
def test_04_nonsecured_to_secured_vm_migration(self):
"""Test Non-secured VM Migration
"""
# Validate the following
# 1. Makes one of the hosts non-secured
# 2. Deploys a VM to the non-secured host
# 3. Migrates the VM to the secured host and assers the migration is via TCP.
hosts = self.get_hosts()
non_secured_host = self.make_unsecure_connection(hosts[0])
secured_hosts = []
hosts = self.get_hosts()
for host in hosts:
if host.details.secured == 'true':
secured_hosts.append(host)
self.vm_to_migrate = self.deploy_vm(non_secured_host)
try:
self.migrate_and_check(origin_host=non_secured_host, destination_host=secured_hosts[0], proto='tcp')
except Exception:
pass
else:
self.fail("Migration succeed, instead it should fail")
def get_target_host(self, secured, virtualmachineid):
target_hosts = Host.listForMigration(self.apiclient,
virtualmachineid=virtualmachineid)
for host in target_hosts:
h = list_hosts(self.apiclient,type='Routing', id=host.id)[0]
if h.details.secured == secured:
return h
cloudstackTestCase.skipTest(self, "No target hosts available, skipping test.")
def check_migration_protocol(self, protocol, host):
resp = SshClient(host.ipaddress, port=22, user=self.hostConfig["username"],passwd=self.hostConfig["password"])\
.execute("grep -a Live /var/log/cloudstack/agent/agent.log | tail -1")
if protocol not in resp[0]:
cloudstackTestCase.fail(self, "Migration protocol was not as expected: '" + protocol + "\n"
"Instead we got: " + resp[0])
def make_unsecure_connection(self, host):
SshClient(host.ipaddress, port=22, user=self.hostConfig["username"],passwd=self.hostConfig["password"])\
.execute("rm -f /etc/cloudstack/agent/cloud*")
SshClient(host.ipaddress, port=22, user=self.hostConfig["username"],passwd=self.hostConfig["password"])\
.execute("sed -i 's/listen_tls.*/listen_tls=0/g' /etc/libvirt/libvirtd.conf")
SshClient(host.ipaddress, port=22, user=self.hostConfig["username"],passwd=self.hostConfig["password"])\
.execute("sed -i 's/listen_tcp.*/listen_tcp=1/g' /etc/libvirt/libvirtd.conf ")
SshClient(host.ipaddress, port=22, user=self.hostConfig["username"],passwd=self.hostConfig["password"])\
.execute("sed -i '/.*_file.*/d' /etc/libvirt/libvirtd.conf")
SshClient(host.ipaddress, port=22, user=self.hostConfig["username"],passwd=self.hostConfig["password"])\
.execute("service libvirtd restart")
SshClient(host.ipaddress, port=22, user=self.hostConfig["username"],passwd=self.hostConfig["password"])\
.execute("service cloudstack-agent restart")
self.check_connection(host=host, secured='false')
time.sleep(10)
return host
def make_all_hosts_secure(self):
hosts = Host.list(
self.apiclient,
zoneid=self.zone.id,
type='Routing'
)
for host in hosts:
cmd = provisionCertificate.provisionCertificateCmd()
cmd.hostid = host.id
self.apiclient.updateConfiguration(cmd)
for host in hosts:
self.check_connection(secured='true', host=host)
def get_hosts(self):
hosts = Host.list(
self.apiclient,
zoneid=self.zone.id,
type='Routing'
)
self.assertEqual(validateList(hosts)[0], PASS, "hosts list validation failed")
return hosts
def deploy_vm(self, origin_host):
return VirtualMachine.create(
self.apiclient,
self.services["small"],
accountid=self.account.name,
domainid=self.account.domainid,
serviceofferingid=self.small_offering.id,
mode=self.services["mode"],
hostid=origin_host.id
)
def check_connection(self, secured, host, retries=5, interval=5):
while retries > -1:
time.sleep(interval)
host = Host.list(
self.apiclient,
zoneid=self.zone.id,
hostid=host.id,
type='Routing'
)[0]
if host.details.secured != secured:
if retries >= 0:
retries = retries - 1
continue
else:
return
raise Exception("Host communication is not as expected: " + secured +
". Instead it's: " + host.details.secured)
def migrate_and_check(self, origin_host, destination_host, proto):
self.vm_to_migrate.migrate(self.apiclient, hostid=destination_host.id)
self.check_migration_protocol(protocol=proto, host=origin_host)
vm_response = VirtualMachine.list(self.apiclient, id=self.vm_to_migrate.id)[0]
self.assertEqual(vm_response.hostid, destination_host.id, "Check destination hostID of migrated VM")
def updateConfiguration(self, name, value):
cmd = updateConfiguration.updateConfigurationCmd()
cmd.name = name
cmd.value = value
self.apiclient.updateConfiguration(cmd)

View File

@ -12670,11 +12670,13 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it
background-position: -101px -647px; background-position: -101px -647px;
} }
.secureKVMHost .icon,
.resetPassword .icon, .resetPassword .icon,
.changePassword .icon { .changePassword .icon {
background-position: -68px -30px; background-position: -68px -30px;
} }
.secureKVMHost:hover .icon,
.resetPassword:hover .icon, .resetPassword:hover .icon,
.changePassword:hover .icon { .changePassword:hover .icon {
background-position: -68px -612px; background-position: -68px -612px;

View File

@ -276,6 +276,7 @@ var dictionary = {
"label.action.restore.instance.processing":"Restoring Instance....", "label.action.restore.instance.processing":"Restoring Instance....",
"label.action.revert.snapshot":"Revert to Snapshot", "label.action.revert.snapshot":"Revert to Snapshot",
"label.action.revert.snapshot.processing":"Reverting to Snapshot...", "label.action.revert.snapshot.processing":"Reverting to Snapshot...",
"label.action.secure.host":"Provision Host Security Keys",
"label.action.start.instance":"Start Instance", "label.action.start.instance":"Start Instance",
"label.action.start.instance.processing":"Starting Instance....", "label.action.start.instance.processing":"Starting Instance....",
"label.action.start.router":"Start Router", "label.action.start.router":"Start Router",
@ -1920,6 +1921,7 @@ var dictionary = {
"message.action.reset.password.warning":"Your instance must be stopped before attempting to change its current password.", "message.action.reset.password.warning":"Your instance must be stopped before attempting to change its current password.",
"message.action.restore.instance":"Please confirm that you want to restore this instance.", "message.action.restore.instance":"Please confirm that you want to restore this instance.",
"message.action.revert.snapshot":"Please confirm that you want to revert the owning volume to this snapshot.", "message.action.revert.snapshot":"Please confirm that you want to revert the owning volume to this snapshot.",
"message.action.secure.host":"This will restart the host agent and libvirtd process after applying new X509 certificates, please confirm?",
"message.action.start.instance":"Please confirm that you want to start this instance.", "message.action.start.instance":"Please confirm that you want to start this instance.",
"message.action.start.router":"Please confirm that you want to start this router.", "message.action.start.router":"Please confirm that you want to start this router.",
"message.action.start.systemvm":"Please confirm that you want to start this system VM.", "message.action.start.systemvm":"Please confirm that you want to start this system VM.",

View File

@ -9198,6 +9198,11 @@
if (host && host.outofbandmanagement) { if (host && host.outofbandmanagement) {
items[idx].powerstate = host.outofbandmanagement.powerstate; items[idx].powerstate = host.outofbandmanagement.powerstate;
} }
if (host && host.hypervisor == "KVM" && host.state == 'Up' && host.details && host.details["secured"] != 'true') {
items[idx].state = 'Unsecure';
}
}); });
} }
@ -15710,7 +15715,8 @@
'Down': 'off', 'Down': 'off',
'Disconnected': 'off', 'Disconnected': 'off',
'Alert': 'off', 'Alert': 'off',
'Error': 'off' 'Error': 'off',
'Unsecure': 'warning'
} }
}, },
powerstate: { powerstate: {
@ -15758,6 +15764,10 @@
if (host && host.outofbandmanagement) { if (host && host.outofbandmanagement) {
items[idx].powerstate = host.outofbandmanagement.powerstate; items[idx].powerstate = host.outofbandmanagement.powerstate;
} }
if (host && host.hypervisor == "KVM" && host.state == 'Up' && host.details && host.details["secured"] != 'true') {
items[idx].state = 'Unsecure';
}
}); });
} }
@ -16527,6 +16537,40 @@
} }
}, },
secureKVMHost: {
label: 'label.action.secure.host',
action: function(args) {
var data = {
hostid: args.context.hosts[0].id
};
$.ajax({
url: createURL('provisionCertificate'),
data: data,
async: true,
success: function(json) {
args.response.success({
_custom: {
jobId: json.provisioncertificateresponse.jobid,
getActionFilter: function () {
return hostActionfilter;
}
}
});
}
});
},
messages: {
confirm: function (args) {
return 'message.action.secure.host';
},
notification: function (args) {
return 'label.action.secure.host';
}
},
notification: {
poll: pollAsyncJobResult
}
},
enableMaintenanceMode: { enableMaintenanceMode: {
label: 'label.action.enable.maintenance.mode', label: 'label.action.enable.maintenance.mode',
@ -21924,6 +21968,11 @@
if (jsonObj.state != "Disconnected") if (jsonObj.state != "Disconnected")
allowedActions.push("forceReconnect"); allowedActions.push("forceReconnect");
if (jsonObj.hypervisor == "KVM") {
allowedActions.push("secureKVMHost");
}
} else if (jsonObj.resourcestate == "ErrorInMaintenance") { } else if (jsonObj.resourcestate == "ErrorInMaintenance") {
allowedActions.push("edit"); allowedActions.push("edit");
allowedActions.push("enableMaintenanceMode"); allowedActions.push("enableMaintenanceMode");

View File

@ -379,7 +379,7 @@ public class Link {
return caService.createSSLEngine(sslContext, clientAddress); return caService.createSSLEngine(sslContext, clientAddress);
} }
s_logger.error("CA service is not configured, by-passing CA manager to create SSL engine"); s_logger.error("CA service is not configured, by-passing CA manager to create SSL engine");
char[] passphrase = KeyStoreUtils.defaultKeystorePassphrase; char[] passphrase = KeyStoreUtils.DEFAULT_KS_PASSPHRASE;
final KeyStore ks = loadKeyStore(NioConnection.class.getResourceAsStream("/cloud.keystore"), passphrase); final KeyStore ks = loadKeyStore(NioConnection.class.getResourceAsStream("/cloud.keystore"), passphrase);
final KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); final KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
final TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); final TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
@ -409,11 +409,11 @@ public class Link {
} }
public static SSLContext initClientSSLContext() throws GeneralSecurityException, IOException { public static SSLContext initClientSSLContext() throws GeneralSecurityException, IOException {
char[] passphrase = KeyStoreUtils.defaultKeystorePassphrase; char[] passphrase = KeyStoreUtils.DEFAULT_KS_PASSPHRASE;
File confFile = PropertiesUtil.findConfigFile("agent.properties"); File confFile = PropertiesUtil.findConfigFile("agent.properties");
if (confFile != null) { if (confFile != null) {
s_logger.info("Conf file found: " + confFile.getAbsolutePath()); s_logger.info("Conf file found: " + confFile.getAbsolutePath());
final String pass = PropertiesUtil.loadFromFile(confFile).getProperty(KeyStoreUtils.passphrasePropertyName); final String pass = PropertiesUtil.loadFromFile(confFile).getProperty(KeyStoreUtils.KS_PASSPHRASE_PROPERTY);
if (pass != null) { if (pass != null) {
passphrase = pass.toCharArray(); passphrase = pass.toCharArray();
} }
@ -421,7 +421,7 @@ public class Link {
InputStream stream = null; InputStream stream = null;
if (confFile != null) { if (confFile != null) {
final String keystorePath = confFile.getParent() + "/" + KeyStoreUtils.defaultKeystoreFile; final String keystorePath = confFile.getParent() + "/" + KeyStoreUtils.KS_FILENAME;
if (new File(keystorePath).exists()) { if (new File(keystorePath).exists()) {
stream = new FileInputStream(keystorePath); stream = new FileInputStream(keystorePath);
} }

View File

@ -37,6 +37,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.apache.cloudstack.utils.security.KeyStoreUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.joda.time.Duration; import org.joda.time.Duration;
@ -202,7 +203,7 @@ public class Script implements Callable<String> {
String[] command = _command.toArray(new String[_command.size()]); String[] command = _command.toArray(new String[_command.size()]);
if (_logger.isDebugEnabled()) { if (_logger.isDebugEnabled()) {
_logger.debug("Executing: " + buildCommandLine(command)); _logger.debug("Executing: " + buildCommandLine(command).split(KeyStoreUtils.KS_FILENAME)[0]);
} }
try { try {

View File

@ -139,7 +139,7 @@ public class SSHCmdHelper {
} }
public static SSHCmdResult sshExecuteCmdOneShot(com.trilead.ssh2.Connection sshConnection, String cmd) throws SshException { public static SSHCmdResult sshExecuteCmdOneShot(com.trilead.ssh2.Connection sshConnection, String cmd) throws SshException {
s_logger.debug("Executing cmd: " + cmd.split(KeyStoreUtils.defaultKeystoreFile)[0]); s_logger.debug("Executing cmd: " + cmd.split(KeyStoreUtils.KS_FILENAME)[0]);
Session sshSession = null; Session sshSession = null;
try { try {
sshSession = sshConnection.openSession(); sshSession = sshConnection.openSession();
@ -202,7 +202,7 @@ public class SSHCmdHelper {
final SSHCmdResult result = new SSHCmdResult(-1, sbStdoutResult.toString(), sbStdErrResult.toString()); final SSHCmdResult result = new SSHCmdResult(-1, sbStdoutResult.toString(), sbStdErrResult.toString());
if (!Strings.isNullOrEmpty(result.getStdOut()) || !Strings.isNullOrEmpty(result.getStdErr())) { if (!Strings.isNullOrEmpty(result.getStdOut()) || !Strings.isNullOrEmpty(result.getStdErr())) {
s_logger.debug("SSH command: " + cmd.split(KeyStoreUtils.defaultKeystoreFile)[0] + "\nSSH command output:" + result.getStdOut().split("-----BEGIN")[0] + "\n" + result.getStdErr()); s_logger.debug("SSH command: " + cmd.split(KeyStoreUtils.KS_FILENAME)[0] + "\nSSH command output:" + result.getStdOut().split("-----BEGIN")[0] + "\n" + result.getStdErr());
} }
// exit status delivery might get delayed // exit status delivery might get delayed

View File

@ -19,23 +19,34 @@
package org.apache.cloudstack.utils.security; package org.apache.cloudstack.utils.security;
import java.io.File;
import com.cloud.utils.PropertiesUtil;
public class KeyStoreUtils { public class KeyStoreUtils {
public static final String KS_SETUP_SCRIPT = "keystore-setup";
public static final String KS_IMPORT_SCRIPT = "keystore-cert-import";
public static String defaultTmpKeyStoreFile = "/tmp/tmp.jks"; public static final String AGENT_PROPSFILE = "agent.properties";
public static String defaultKeystoreFile = "cloud.jks"; public static final String KS_PASSPHRASE_PROPERTY = "keystore.passphrase";
public static String defaultPrivateKeyFile = "cloud.key";
public static String defaultCsrFile = "cloud.csr";
public static String defaultCertFile = "cloud.crt";
public static String defaultCaCertFile = "cloud.ca.crt";
public static char[] defaultKeystorePassphrase = "vmops.com".toCharArray();
public static String certNewlineEncoder = "^"; public static final String KS_FILENAME = "cloud.jks";
public static String certSpaceEncoder = "~"; public static final char[] DEFAULT_KS_PASSPHRASE = "vmops.com".toCharArray();
public static String keyStoreSetupScript = "keystore-setup"; public static final String CACERT_FILENAME = "cloud.ca.crt";
public static String keyStoreImportScript = "keystore-cert-import"; public static final String CERT_FILENAME = "cloud.crt";
public static String passphrasePropertyName = "keystore.passphrase"; public static final String CSR_FILENAME = "cloud.csr";
public static final String PKEY_FILENAME = "cloud.key";
public static String sshMode = "ssh"; public static final String CERT_NEWLINE_ENCODER = "^";
public static String agentMode = "agent"; public static final String CERT_SPACE_ENCODER = "~";
public static final String SSH_MODE = "ssh";
public static final String AGENT_MODE = "agent";
public static final String SECURED = "secured";
public static boolean isHostSecured() {
final File confFile = PropertiesUtil.findConfigFile("agent.properties");
return confFile != null && confFile.exists() && new File(confFile.getParent() + "/" + CERT_FILENAME).exists();
}
} }