From bd24a8d0f884d27f47834c917c047b54271c1179 Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Fri, 20 Sep 2013 16:26:42 +1000 Subject: [PATCH] Allow deploying keystone with SSL certificates Allow providing certificates through environment variables to be used for keystone, and provide the basis for doing this for other services. It cannot be used in conjunction with tls-proxy as the service provides it's own encrypted endpoint. Impletmenting: blueprint devstack-https Change-Id: I8cf4c9c8c8a6911ae56ebcd14600a9d24cca99a0 --- lib/cinder | 2 ++ lib/glance | 2 ++ lib/heat | 1 + lib/ironic | 1 + lib/keystone | 19 +++++++++++++++- lib/nova | 2 ++ lib/swift | 2 ++ lib/tls | 50 +++++++++++++++++++++++++++++++++++++++++- lib/trove | 4 +++- openrc | 5 +++-- stack.sh | 26 ++++++++++++++++++++-- tools/create_userrc.sh | 5 ++++- 12 files changed, 111 insertions(+), 8 deletions(-) diff --git a/lib/cinder b/lib/cinder index 96d25058ce..9288685365 100644 --- a/lib/cinder +++ b/lib/cinder @@ -209,6 +209,7 @@ function configure_cinder() { inicomment $CINDER_API_PASTE_INI filter:authtoken auth_host inicomment $CINDER_API_PASTE_INI filter:authtoken auth_port inicomment $CINDER_API_PASTE_INI filter:authtoken auth_protocol + inicomment $CINDER_API_PASTE_INI filter:authtoken cafile inicomment $CINDER_API_PASTE_INI filter:authtoken admin_tenant_name inicomment $CINDER_API_PASTE_INI filter:authtoken admin_user inicomment $CINDER_API_PASTE_INI filter:authtoken admin_password @@ -219,6 +220,7 @@ function configure_cinder() { iniset $CINDER_CONF keystone_authtoken auth_host $KEYSTONE_AUTH_HOST iniset $CINDER_CONF keystone_authtoken auth_port $KEYSTONE_AUTH_PORT iniset $CINDER_CONF keystone_authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL + iniset $CINDER_CONF keystone_authtoken cafile $KEYSTONE_SSL_CA iniset $CINDER_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME iniset $CINDER_CONF keystone_authtoken admin_user cinder iniset $CINDER_CONF keystone_authtoken admin_password $SERVICE_PASSWORD diff --git a/lib/glance b/lib/glance index eb727f1e2a..c88f2dc472 100644 --- a/lib/glance +++ b/lib/glance @@ -82,6 +82,7 @@ function configure_glance() { iniset $GLANCE_REGISTRY_CONF keystone_authtoken auth_host $KEYSTONE_AUTH_HOST iniset $GLANCE_REGISTRY_CONF keystone_authtoken auth_port $KEYSTONE_AUTH_PORT iniset $GLANCE_REGISTRY_CONF keystone_authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL + iniset $GLANCE_REGISTRY_CONF keystone_authtoken cafile $KEYSTONE_SSL_CA iniset $GLANCE_REGISTRY_CONF keystone_authtoken auth_uri $KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/ iniset $GLANCE_REGISTRY_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME iniset $GLANCE_REGISTRY_CONF keystone_authtoken admin_user glance @@ -99,6 +100,7 @@ function configure_glance() { iniset $GLANCE_API_CONF keystone_authtoken auth_host $KEYSTONE_AUTH_HOST iniset $GLANCE_API_CONF keystone_authtoken auth_port $KEYSTONE_AUTH_PORT iniset $GLANCE_API_CONF keystone_authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL + iniset $GLANCE_API_CONF keystone_authtoken cafile $KEYSTONE_SSL_CA iniset $GLANCE_API_CONF keystone_authtoken auth_uri $KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/ iniset $GLANCE_API_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME iniset $GLANCE_API_CONF keystone_authtoken admin_user glance diff --git a/lib/heat b/lib/heat index 7a9ef0da26..e44a618162 100644 --- a/lib/heat +++ b/lib/heat @@ -96,6 +96,7 @@ function configure_heat() { iniset $HEAT_CONF keystone_authtoken auth_port $KEYSTONE_AUTH_PORT iniset $HEAT_CONF keystone_authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL iniset $HEAT_CONF keystone_authtoken auth_uri $KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/v2.0 + iniset $HEAT_CONF keystone_authtoken cafile $KEYSTONE_SSL_CA iniset $HEAT_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME iniset $HEAT_CONF keystone_authtoken admin_user heat iniset $HEAT_CONF keystone_authtoken admin_password $SERVICE_PASSWORD diff --git a/lib/ironic b/lib/ironic index 9f86e841d8..099746ae22 100644 --- a/lib/ironic +++ b/lib/ironic @@ -98,6 +98,7 @@ function configure_ironic_api() { iniset $IRONIC_CONF_FILE keystone_authtoken auth_host $KEYSTONE_AUTH_HOST iniset $IRONIC_CONF_FILE keystone_authtoken auth_port $KEYSTONE_AUTH_PORT iniset $IRONIC_CONF_FILE keystone_authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL + iniset $IRONIC_CONF_FILE keystone_authtoken cafile $KEYSTONE_SSL_CA iniset $IRONIC_CONF_FILE keystone_authtoken auth_uri $KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/ iniset $IRONIC_CONF_FILE keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME iniset $IRONIC_CONF_FILE keystone_authtoken admin_user ironic diff --git a/lib/keystone b/lib/keystone index 978577f55e..4a7d7bb717 100644 --- a/lib/keystone +++ b/lib/keystone @@ -4,6 +4,7 @@ # Dependencies: # # - ``functions`` file +# - ``tls`` file # - ``DEST``, ``STACK_USER`` # - ``IDENTITY_API_VERSION`` # - ``BASE_SQL_CONN`` @@ -79,6 +80,13 @@ KEYSTONE_VALID_IDENTITY_BACKENDS=kvs,ldap,pam,sql # valid assignment backends as per dir keystone/identity/backends KEYSTONE_VALID_ASSIGNMENT_BACKENDS=kvs,ldap,sql +# if we are running with SSL use https protocols +if is_ssl_enabled_service "key"; then + KEYSTONE_AUTH_PROTOCOL="https" + KEYSTONE_SERVICE_PROTOCOL="https" +fi + + # Functions # --------- # cleanup_keystone() - Remove residual data files, anything left over from previous @@ -172,6 +180,15 @@ function configure_keystone() { iniset $KEYSTONE_CONF DEFAULT public_endpoint "$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:%(public_port)s/" iniset $KEYSTONE_CONF DEFAULT admin_endpoint "$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:%(admin_port)s/" + # Register SSL certificates if provided + if is_ssl_enabled_service key; then + ensure_certificates KEYSTONE + + iniset $KEYSTONE_CONF ssl enable True + iniset $KEYSTONE_CONF ssl certfile $KEYSTONE_SSL_CERT + iniset $KEYSTONE_CONF ssl keyfile $KEYSTONE_SSL_KEY + fi + if is_service_enabled tls-proxy; then # Set the service ports for a proxy to take the originals iniset $KEYSTONE_CONF DEFAULT public_port $KEYSTONE_SERVICE_PORT_INT @@ -373,7 +390,7 @@ function start_keystone() { fi echo "Waiting for keystone to start..." - if ! timeout $SERVICE_TIMEOUT sh -c "while ! curl --noproxy '*' -s http://$SERVICE_HOST:$service_port/v$IDENTITY_API_VERSION/ >/dev/null; do sleep 1; done"; then + if ! timeout $SERVICE_TIMEOUT sh -c "while ! curl --noproxy '*' -s $KEYSTONE_AUTH_PROTOCOL://$SERVICE_HOST:$service_port/v$IDENTITY_API_VERSION/ >/dev/null; do sleep 1; done"; then die $LINENO "keystone did not start" fi diff --git a/lib/nova b/lib/nova index 6ab2000111..5fd0bebf65 100644 --- a/lib/nova +++ b/lib/nova @@ -225,6 +225,7 @@ function configure_nova() { inicomment $NOVA_API_PASTE_INI filter:authtoken auth_host inicomment $NOVA_API_PASTE_INI filter:authtoken auth_protocol inicomment $NOVA_API_PASTE_INI filter:authtoken admin_tenant_name + inicomment $NOVA_API_PASTE_INI filter:authtoken cafile inicomment $NOVA_API_PASTE_INI filter:authtoken admin_user inicomment $NOVA_API_PASTE_INI filter:authtoken admin_password fi @@ -399,6 +400,7 @@ function create_nova_conf() { iniset $NOVA_CONF keystone_authtoken auth_host $KEYSTONE_AUTH_HOST iniset $NOVA_CONF keystone_authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL iniset $NOVA_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME + iniset $NOVA_CONF keystone_authtoken cafile $KEYSTONE_SSL_CA iniset $NOVA_CONF keystone_authtoken admin_user nova iniset $NOVA_CONF keystone_authtoken admin_password $SERVICE_PASSWORD fi diff --git a/lib/swift b/lib/swift index c103b5ba5f..c0493110b9 100644 --- a/lib/swift +++ b/lib/swift @@ -306,6 +306,7 @@ function configure_swift() { iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken auth_host $KEYSTONE_AUTH_HOST iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken auth_port $KEYSTONE_AUTH_PORT iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL + iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken cafile $KEYSTONE_SSL_CA iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken auth_uri $KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/ iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken admin_tenant_name $SERVICE_TENANT_NAME iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken admin_user swift @@ -325,6 +326,7 @@ paste.filter_factory = keystone.middleware.s3_token:filter_factory auth_port = ${KEYSTONE_AUTH_PORT} auth_host = ${KEYSTONE_AUTH_HOST} auth_protocol = ${KEYSTONE_AUTH_PROTOCOL} +cafile = ${KEYSTONE_SSL_CA} auth_token = ${SERVICE_TOKEN} admin_token = ${SERVICE_TOKEN} diff --git a/lib/tls b/lib/tls index a1a7fddc18..6134fa1bad 100644 --- a/lib/tls +++ b/lib/tls @@ -22,7 +22,8 @@ # - make_int_ca # - new_cert $INT_CA_DIR int-server "abc" # - start_tls_proxy HOST_IP 5000 localhost 5000 - +# - ensure_certificates +# - is_ssl_enabled_service # Defaults # -------- @@ -309,6 +310,53 @@ function make_root_CA() { } +# Certificate Input Configuration +# =============================== + +# check to see if the service(s) specified are to be SSL enabled. +# +# Multiple services specified as arguments are ``OR``'ed together; the test +# is a short-circuit boolean, i.e it returns on the first match. +# +# Uses global ``SSL_ENABLED_SERVICES`` +function is_ssl_enabled_service() { + services=$@ + for service in ${services}; do + [[ ,${SSL_ENABLED_SERVICES}, =~ ,${service}, ]] && return 0 + done + return 1 +} + + +# Ensure that the certificates for a service are in place. This function does +# not check that a service is SSL enabled, this should already have been +# completed. +# +# The function expects to find a certificate, key and CA certificate in the +# variables {service}_SSL_CERT, {service}_SSL_KEY and {service}_SSL_CA. For +# example for keystone this would be KEYSTONE_SSL_CERT, KEYSTONE_SSL_KEY and +# KEYSTONE_SSL_CA. If it does not find these certificates the program will +# quit. +function ensure_certificates() { + local service=$1 + + local cert_var="${service}_SSL_CERT" + local key_var="${service}_SSL_KEY" + local ca_var="${service}_SSL_CA" + + local cert=${!cert_var} + local key=${!key_var} + local ca=${!ca_var} + + if [[ !($cert && $key && $ca) ]]; then + die $LINENO "Missing either the ${cert_var} ${key_var} or ${ca_var}" \ + "variable to enable SSL for ${service}" + fi + + cat $ca >> $SSL_BUNDLE_FILE +} + + # Proxy Functions # =============== diff --git a/lib/trove b/lib/trove index c40006bf5d..5ba4de5a4f 100644 --- a/lib/trove +++ b/lib/trove @@ -29,7 +29,6 @@ TROVE_DIR=$DEST/trove TROVECLIENT_DIR=$DEST/python-troveclient TROVE_CONF_DIR=/etc/trove TROVE_LOCAL_CONF_DIR=$TROVE_DIR/etc/trove -TROVE_AUTH_ENDPOINT=$KEYSTONE_AUTH_PROTOCOL://$KEYSTONE_AUTH_HOST:$KEYSTONE_AUTH_PORT//v$IDENTITY_API_VERSION TROVE_AUTH_CACHE_DIR=${TROVE_AUTH_CACHE_DIR:-/var/cache/trove} TROVE_BIN_DIR=/usr/local/bin @@ -102,6 +101,7 @@ function configure_trove() { iniset $TROVE_API_PASTE_INI filter:tokenauth auth_host $KEYSTONE_AUTH_HOST iniset $TROVE_API_PASTE_INI filter:tokenauth auth_port $KEYSTONE_AUTH_PORT iniset $TROVE_API_PASTE_INI filter:tokenauth auth_protocol $KEYSTONE_AUTH_PROTOCOL + iniset $TROVE_API_PASTE_INI filter:tokenauth cafile $KEYSTONE_SSL_CA iniset $TROVE_API_PASTE_INI filter:tokenauth admin_tenant_name $SERVICE_TENANT_NAME iniset $TROVE_API_PASTE_INI filter:tokenauth admin_user trove iniset $TROVE_API_PASTE_INI filter:tokenauth admin_password $SERVICE_PASSWORD @@ -123,6 +123,8 @@ function configure_trove() { # (Re)create trove taskmanager conf file if needed if is_service_enabled tr-tmgr; then + TROVE_AUTH_ENDPOINT=$KEYSTONE_AUTH_PROTOCOL://$KEYSTONE_AUTH_HOST:$KEYSTONE_AUTH_PORT//v$IDENTITY_API_VERSION + iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT rabbit_password $RABBIT_PASSWORD iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT sql_connection `database_connection_url trove` iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT taskmanager_manager trove.taskmanager.manager.Manager diff --git a/openrc b/openrc index 804bb3f3d7..784b00e51b 100644 --- a/openrc +++ b/openrc @@ -58,6 +58,7 @@ export OS_NO_CACHE=${OS_NO_CACHE:-1} HOST_IP=${HOST_IP:-127.0.0.1} SERVICE_HOST=${SERVICE_HOST:-$HOST_IP} SERVICE_PROTOCOL=${SERVICE_PROTOCOL:-http} +KEYSTONE_AUTH_PROTOCOL=${KEYSTONE_AUTH_PROTOCOL:-$SERVICE_PROTOCOL} # Some exercises call glance directly. On a single-node installation, Glance # should be listening on HOST_IP. If its running elsewhere, it can be set here @@ -71,10 +72,10 @@ export OS_IDENTITY_API_VERSION=${IDENTITY_API_VERSION:-2.0} # the user/tenant has access to - including nova, glance, keystone, swift, ... # We currently recommend using the 2.0 *identity api*. # -export OS_AUTH_URL=$SERVICE_PROTOCOL://$SERVICE_HOST:5000/v${OS_IDENTITY_API_VERSION} +export OS_AUTH_URL=$KEYSTONE_AUTH_PROTOCOL://$SERVICE_HOST:5000/v${OS_IDENTITY_API_VERSION} # Set the pointer to our CA certificate chain. Harmless if TLS is not used. -export OS_CACERT=$INT_CA_DIR/ca-chain.pem +export OS_CACERT=${OS_CACERT:-$INT_CA_DIR/ca-chain.pem} # Currently novaclient needs you to specify the *compute api* version. This # needs to match the config of your catalog returned by Keystone. diff --git a/stack.sh b/stack.sh index 47d93bd642..28032def37 100755 --- a/stack.sh +++ b/stack.sh @@ -290,6 +290,10 @@ LOG_COLOR=`trueorfalse True $LOG_COLOR` # Service startup timeout SERVICE_TIMEOUT=${SERVICE_TIMEOUT:-60} +# Reset the bundle of CA certificates +SSL_BUNDLE_FILE="$DATA_DIR/ca-bundle.pem" +rm -f $SSL_BUNDLE_FILE + # Configure Projects # ================== @@ -798,6 +802,17 @@ fi restart_rpc_backend +# Export Certicate Authority Bundle +# --------------------------------- + +# If certificates were used and written to the SSL bundle file then these +# should be exported so clients can validate their connections. + +if [ -f $SSL_BUNDLE_FILE ]; then + export OS_CACERT=$SSL_BUNDLE_FILE +fi + + # Configure database # ------------------ @@ -1145,6 +1160,7 @@ if is_service_enabled trove; then start_trove fi + # Create account rc files # ======================= @@ -1153,7 +1169,13 @@ fi # which is helpful in image bundle steps. if is_service_enabled nova && is_service_enabled key; then - $TOP_DIR/tools/create_userrc.sh -PA --target-dir $TOP_DIR/accrc + USERRC_PARAMS="-PA --target-dir $TOP_DIR/accrc" + + if [ -f $SSL_BUNDLE_FILE ]; then + USERRC_PARAMS="$USERRC_PARAMS --os-cacert $SSL_BUNDLE_FILE" + fi + + $TOP_DIR/tools/create_userrc.sh $USERRC_PARAMS fi @@ -1229,7 +1251,7 @@ fi CURRENT_RUN_TIME=$(date "+$TIMESTAMP_FORMAT") echo "# $CURRENT_RUN_TIME" >$TOP_DIR/.stackenv for i in BASE_SQL_CONN ENABLED_SERVICES HOST_IP LOGFILE \ - SERVICE_HOST SERVICE_PROTOCOL STACK_USER TLS_IP; do + SERVICE_HOST SERVICE_PROTOCOL STACK_USER TLS_IP KEYSTONE_AUTH_PROTOCOL OS_CACERT; do echo $i=${!i} >>$TOP_DIR/.stackenv done diff --git a/tools/create_userrc.sh b/tools/create_userrc.sh index 8383fe7d77..5f4c48660b 100755 --- a/tools/create_userrc.sh +++ b/tools/create_userrc.sh @@ -43,6 +43,7 @@ Optional Arguments --os-tenant-name --os-tenant-id --os-auth-url +--os-cacert --target-dir --skip-tenant --debug @@ -53,7 +54,7 @@ $0 -P -C mytenant -u myuser -p mypass EOF } -if ! options=$(getopt -o hPAp:u:r:C: -l os-username:,os-password:,os-tenant-name:,os-tenant-id:,os-auth-url:,target-dir:,skip-tenant:,help,debug -- "$@") +if ! options=$(getopt -o hPAp:u:r:C: -l os-username:,os-password:,os-tenant-name:,os-tenant-id:,os-auth-url:,target-dir:,skip-tenant:,os-cacert:,help,debug -- "$@") then #parse error display_help @@ -80,6 +81,7 @@ do --os-tenant-id) export OS_TENANT_ID=$2; shift ;; --skip-tenant) SKIP_TENANT="$SKIP_TENANT$2,"; shift ;; --os-auth-url) export OS_AUTH_URL=$2; shift ;; + --os-cacert) export OS_CACERT=$2; shift ;; --target-dir) ACCOUNT_DIR=$2; shift ;; --debug) set -o xtrace ;; -u) MODE=${MODE:-one}; USER_NAME=$2; shift ;; @@ -201,6 +203,7 @@ export OS_USERNAME="$user_name" # Openstack Tenant ID = $tenant_id export OS_TENANT_NAME="$tenant_name" export OS_AUTH_URL="$OS_AUTH_URL" +export OS_CACERT="$OS_CACERT" export EC2_CERT="$ec2_cert" export EC2_PRIVATE_KEY="$ec2_private_key" export EC2_USER_ID=42 #not checked by nova (can be a 12-digit id)