Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KVM: Prevent regenerating keystore on provisionCertificate API #3075

Merged
merged 4 commits into from Jun 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -16,11 +16,6 @@
// under the License.
package org.apache.cloudstack.api.command.admin.direct.download;

import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.exception.NetworkRuleConflictException;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
Expand Down Expand Up @@ -62,15 +57,15 @@ public class UploadTemplateDirectDownloadCertificateCmd extends BaseCmd {
private String hypervisor;

@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
public void execute() {
if (!hypervisor.equalsIgnoreCase("kvm")) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Currently supporting KVM hosts only");
}

SuccessResponse response = new SuccessResponse(getCommandName());
try {
LOG.debug("Uploading certificate " + name + " to agents for Direct Download");
boolean result = directDownloadManager.uploadCertificateToHosts(certificate, name, hypervisor);
SuccessResponse response = new SuccessResponse(getCommandName());
response.setSuccess(result);
setResponseObject(response);
} catch (Exception e) {
Expand Down
23 changes: 9 additions & 14 deletions scripts/util/keystore-cert-import
Expand Up @@ -38,9 +38,6 @@ if [ -z "${KS_PASS// }" ]; then
exit 1
fi

# Use a new keystore file
NEW_KS_FILE="$KS_FILE.new"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script was create to be idempotent for jks creation and cert import. I'll have to test concurrent cases and see if this script would pass/fail. I'll test and review today and keep you posted @nvazquez thanks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll also need to test for certificate renewal.


# Import certificate
if [ ! -z "${CERT// }" ]; then
echo "$CERT" > "$CERT_FILE"
Expand All @@ -54,30 +51,28 @@ fi
# Import cacerts into the keystore
awk '/-----BEGIN CERTIFICATE-----?/{n++}{print > "cloudca." n }' "$CACERT_FILE"
for caChain in $(ls cloudca.*); do
keytool -delete -noprompt -alias "$caChain" -keystore "$NEW_KS_FILE" -storepass "$KS_PASS" > /dev/null 2>&1 || true
keytool -import -noprompt -storepass "$KS_PASS" -trustcacerts -alias "$caChain" -file "$caChain" -keystore "$NEW_KS_FILE" > /dev/null 2>&1
keytool -delete -noprompt -alias "$caChain" -keystore "$KS_FILE" -storepass "$KS_PASS" > /dev/null 2>&1 || true
keytool -import -noprompt -storepass "$KS_PASS" -trustcacerts -alias "$caChain" -file "$caChain" -keystore "$KS_FILE" > /dev/null 2>&1
done
rm -f cloudca.*

# Import private key if available
if [ ! -z "${PRIVKEY// }" ]; then
echo "$PRIVKEY" > "$PRIVKEY_FILE"
# Re-initialize keystore when private key is provided
keytool -delete -noprompt -alias "$ALIAS" -keystore "$NEW_KS_FILE" -storepass "$KS_PASS" 2>/dev/null || true
openssl pkcs12 -export -name "$ALIAS" -in "$CERT_FILE" -inkey "$PRIVKEY_FILE" -out "$NEW_KS_FILE.p12" -password pass:"$KS_PASS" > /dev/null 2>&1
keytool -importkeystore -srckeystore "$NEW_KS_FILE.p12" -destkeystore "$NEW_KS_FILE" -srcstoretype PKCS12 -alias "$ALIAS" -deststorepass "$KS_PASS" -destkeypass "$KS_PASS" -srcstorepass "$KS_PASS" -srckeypass "$KS_PASS" > /dev/null 2>&1
keytool -delete -noprompt -alias "$ALIAS" -keystore "$KS_FILE" -storepass "$KS_PASS" 2>/dev/null || true
openssl pkcs12 -export -name "$ALIAS" -in "$CERT_FILE" -inkey "$PRIVKEY_FILE" -out "$KS_FILE.p12" -password pass:"$KS_PASS" > /dev/null 2>&1
keytool -importkeystore -srckeystore "$KS_FILE.p12" -destkeystore "$KS_FILE" -srcstoretype PKCS12 -alias "$ALIAS" -deststorepass "$KS_PASS" -destkeypass "$KS_PASS" -srcstorepass "$KS_PASS" -srckeypass "$KS_PASS" > /dev/null 2>&1
else
# Import certificate into the keystore
keytool -import -storepass "$KS_PASS" -alias "$ALIAS" -file "$CERT_FILE" -keystore "$NEW_KS_FILE" > /dev/null 2>&1 || true
keytool -import -storepass "$KS_PASS" -alias "$ALIAS" -file "$CERT_FILE" -keystore "$KS_FILE" > /dev/null 2>&1 || true
# Export private key from keystore
rm -f "$PRIVKEY_FILE"
keytool -importkeystore -srckeystore "$NEW_KS_FILE" -destkeystore "$NEW_KS_FILE.p12" -deststoretype PKCS12 -srcalias "$ALIAS" -deststorepass "$KS_PASS" -destkeypass "$KS_PASS" -srcstorepass "$KS_PASS" -srckeypass "$KS_PASS" > /dev/null 2>&1
openssl pkcs12 -in "$NEW_KS_FILE.p12" -nodes -nocerts -nomac -password pass:"$KS_PASS" 2>/dev/null | openssl rsa -out "$PRIVKEY_FILE" > /dev/null 2>&1
keytool -importkeystore -srckeystore "$KS_FILE" -destkeystore "$KS_FILE.p12" -deststoretype PKCS12 -srcalias "$ALIAS" -deststorepass "$KS_PASS" -destkeypass "$KS_PASS" -srcstorepass "$KS_PASS" -srckeypass "$KS_PASS" > /dev/null 2>&1
openssl pkcs12 -in "$KS_FILE.p12" -nodes -nocerts -nomac -password pass:"$KS_PASS" 2>/dev/null | openssl rsa -out "$PRIVKEY_FILE" > /dev/null 2>&1
fi

# Commit the new keystore
rm -f "$NEW_KS_FILE.p12"
mv -f "$NEW_KS_FILE" "$KS_FILE"
rm -f "$KS_FILE.p12"

# Secure libvirtd on cert import
if [ -f "$LIBVIRTD_FILE" ]; then
Expand Down
8 changes: 5 additions & 3 deletions scripts/util/keystore-setup
Expand Up @@ -17,7 +17,7 @@
# under the License.

PROPS_FILE="$1"
KS_FILE="$2.new"
KS_FILE="$2"
KS_PASS="$3"
KS_VALIDITY="$4"
CSR_FILE="$5"
Expand All @@ -35,8 +35,10 @@ if [ -f "$PROPS_FILE" ]; then
fi
fi

# Generate keystore
rm -f "$KS_FILE"
if [ -f "$KS_FILE" ]; then
keytool -delete -noprompt -alias "$ALIAS" -keystore "$KS_FILE" -storepass "$KS_PASS" > /dev/null 2>&1 || true
fi

CN=$(hostname --fqdn)
keytool -genkey -storepass "$KS_PASS" -keypass "$KS_PASS" -alias "$ALIAS" -keyalg RSA -validity "$KS_VALIDITY" -dname cn="$CN",ou="cloudstack",o="cloudstack",c="cloudstack" -keystore "$KS_FILE" > /dev/null 2>&1

Expand Down
Expand Up @@ -85,17 +85,17 @@ public class DirectDownloadManagerImpl extends ManagerBase implements DirectDown
protected final static String LINE_SEPARATOR = "\n";

@Inject
VMTemplateDao vmTemplateDao;
private VMTemplateDao vmTemplateDao;
@Inject
PrimaryDataStoreDao primaryDataStoreDao;
private PrimaryDataStoreDao primaryDataStoreDao;
@Inject
HostDao hostDao;
private HostDao hostDao;
@Inject
AgentManager agentManager;
private AgentManager agentManager;
@Inject
VMTemplatePoolDao vmTemplatePoolDao;
private VMTemplatePoolDao vmTemplatePoolDao;
@Inject
DataStoreManager dataStoreManager;
private DataStoreManager dataStoreManager;

@Override
public List<Class<?>> getCommands() {
Expand Down Expand Up @@ -366,29 +366,37 @@ protected void certificateSanity(String certificatePem) {

@Override
public boolean uploadCertificateToHosts(String certificateCer, String alias, String hypervisor) {
if (alias != null && (alias.equalsIgnoreCase("cloud") || alias.startsWith("cloudca"))) {
throw new CloudRuntimeException("Please provide a different alias name for the certificate");
}

HypervisorType hypervisorType = HypervisorType.getType(hypervisor);
List<HostVO> hosts = getRunningHostsToUploadCertificate(hypervisorType);

String certificatePem = getPretifiedCertificate(certificateCer);
certificateSanity(certificatePem);

s_logger.info("Attempting to upload certificate: " + alias + " to " + hosts.size() + " hosts");
int hostCount = 0;
if (CollectionUtils.isNotEmpty(hosts)) {
for (HostVO host : hosts) {
if (!uploadCertificate(certificatePem, alias, host.getId())) {
String msg = "Could not upload certificate " + alias + " on host: " + host.getName() + " (" + host.getUuid() + ")";
s_logger.error(msg);
throw new CloudRuntimeException(msg);
}
hostCount++;
}
}
s_logger.info("Certificate was successfully uploaded to " + hostCount + " hosts");
return true;
}

/**
* Upload and import certificate to hostId on keystore
*/
protected boolean uploadCertificate(String certificate, String certificateName, long hostId) {
s_logger.debug("Uploading certificate: " + certificateName + " to host " + hostId);
SetupDirectDownloadCertificateCommand cmd = new SetupDirectDownloadCertificateCommand(certificate, certificateName);
Answer answer = agentManager.easySend(hostId, cmd);
if (answer == null || !answer.getResult()) {
Expand Down