cloudstack/scripts/util/keystore-cert-import
Rohit Yadav 8da2462469
CLOUDSTACK-10333: Secure Live VM Migration for KVM (#2505)
This extends securing of KVM hosts to securing of libvirt on KVM
host as well for TLS enabled live VM migration. To simplify implementation
securing of host implies that both host and libvirtd processes are
secured with management server's CA plugin issued certificates.

Based on whether keystore and certificates files are available at
/etc/cloudstack/agent, the KVM agent determines whether to use TLS or
TCP based uris for live VM migration. It is also enforced that a secured
host will allow live VM migration to/from other secured host, and an
unsecured hosts will allow live VM migration to/from other unsecured
host only.

Post upgrade the KVM agent on startup will expose its security state
(secured detail is sent as true or false) to the managements server that
gets saved in host_details for the host. This host detail can be accesed
via the listHosts response, and in the UI unsecured KVM hosts will show
up with the host state of ‘unsecured’. Further, a button has been added
that allows admins to provision/renew certificates to KVM hosts and can
be used to secure any unsecured KVM host.

The `cloudstack-setup-agent` was modified to accept a new flag `-s`
which will reconfigure libvirtd with following settings:

    listen_tcp=0
    listen_tls=1
    tcp_port="16509"
    tls_port="16514"
    auth_tcp="none"
    auth_tls="none"
    key_file = "/etc/pki/libvirt/private/serverkey.pem"
    cert_file = "/etc/pki/libvirt/servercert.pem"
    ca_file = "/etc/pki/CA/cacert.pem"

For a connected KVM host agent, when the certificate are
renewed/provisioned a background task is scheduled that waits until all
of the agent tasks finish after which libvirt process is restarted and
finally the agent is restarted via AgentShell.

There are no API or DB changes.

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
2018-04-20 00:36:18 +05:30

112 lines
4.5 KiB
Bash
Executable File

#!/bin/bash
# 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.
PROPS_FILE="$1"
KS_FILE="$2"
MODE="$3"
CERT_FILE="$4"
CERT=$(echo "$5" | tr '^' '\n' | tr '~' ' ')
CACERT_FILE="$6"
CACERT=$(echo "$7" | tr '^' '\n' | tr '~' ' ')
PRIVKEY_FILE="$8"
PRIVKEY=$(echo "$9" | tr '^' '\n' | tr '~' ' ')
ALIAS="cloud"
SYSTEM_FILE="/var/cache/cloud/cmdline"
LIBVIRTD_FILE="/etc/libvirt/libvirtd.conf"
# Find keystore password
KS_PASS=$(sed -n '/keystore.passphrase/p' "$PROPS_FILE" 2>/dev/null | sed 's/keystore.passphrase=//g' 2>/dev/null)
if [ -z "${KS_PASS// }" ]; then
echo "Failed to find keystore passphrase from file: $PROPS_FILE, quiting!"
exit 1
fi
# Use a new keystore file
NEW_KS_FILE="$KS_FILE.new"
# Import certificate
if [ ! -z "${CERT// }" ]; then
echo "$CERT" > "$CERT_FILE"
fi
# Import ca certs
if [ ! -z "${CACERT// }" ]; then
echo "$CACERT" > "$CACERT_FILE"
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
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
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
# 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
fi
# Commit the new keystore
rm -f "$NEW_KS_FILE.p12"
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
if [ -f "$SYSTEM_FILE" ]; then
mkdir -p /usr/local/share/ca-certificates/cloudstack
cp "$CACERT_FILE" /usr/local/share/ca-certificates/cloudstack/ca.crt
chmod 755 /usr/local/share/ca-certificates/cloudstack
chmod 644 /usr/local/share/ca-certificates/cloudstack/ca.crt
update-ca-certificates > /dev/null 2>&1 || true
fi
# Restart cloud service if we're in systemvm
if [ "$MODE" == "ssh" ] && [ -f $SYSTEM_FILE ]; then
systemctl restart cloud > /dev/null 2>&1
fi
# Fix file permission
chmod 600 $CACERT_FILE
chmod 600 $CERT_FILE
chmod 600 $PRIVKEY_FILE