HTTPS Support via certs or keystone.

Port pipeline reconfiguration.
This commit is contained in:
James Page 2013-03-11 09:31:48 +00:00
commit e4ca39672c
5 changed files with 291 additions and 33 deletions

View File

@ -86,3 +86,14 @@ options:
description: |
Default multicast port number that will be used to communicate between
HA Cluster nodes.
# Per-service HTTPS configuration.
ssl_cert:
type: string
description: |
SSL certificate to install and use for API ports. Setting this value
and ssl_key will enable reverse proxying, point Glance's entry in the
Keystone catalog to use https, and override any certficiate and key
issued by Keystone (if it is configured to do so).
ssl_key:
type: string
description: SSL key to use with certificate specified as ssl_cert.

View File

@ -228,3 +228,37 @@ prepare_storage() {
error_out "Could not create volume group: $vol_group"
return 0
}
configure_https() {
# request openstack-common setup reverse proxy mapping for API and registry
# servers
service_enabled "api" || return 0
local cfg_api_port=$(config-get api-listening-port)
service_ctl cinder-api stop
if [[ -n "$(peer_units)" ]] || is_clustered ; then
# haproxy may already be configured. need to push it back in the request
# pipeline in preparation for a change from:
# from: haproxy (8776) -> cinder-api (8766)
# to: ssl (8776) -> haproxy (8766) -> cinder-api (8756)
local next_server=$(determine_haproxy_port $cfg_api_port)
local api_port=$(determine_api_port $cfg_api_port)
configure_haproxy "cinder_api:$next_server:$api_port"
else
# if not clustered, the cinder-api is next in the pipeline.
local api_port=$(determine_api_port $cfg_api_port)
local next_server=$api_port
fi
# setup https to point to either haproxy or directly to api server, depending.
setup_https $cfg_api_port:$next_server
# configure servers to listen on new ports accordingly.
set_or_update osapi_volume_listen_port "$api_port"
service_ctl cinder-api start
local r_id=""
# (re)configure ks endpoint accordingly in ks and nova.
for r_id in "$(relation-ids identity-service)" ; do
keystone_joined "$r_id"
done
}

View File

@ -35,6 +35,7 @@ install_hook() {
cinder_ctl cinder-volume restart
fi
fi
configure_https
}
db_joined() {
@ -95,13 +96,13 @@ amqp_changed() {
keystone_joined() {
# Exit hook execution if unit is not leader of cluster/service
eligible_leader 'res_cinder_vip' || return 0
port=$(config-get api-listening-port)
if is_clustered; then
port=$(($port + 10000))
url="http://$(config-get vip):$port/v1/\$(tenant_id)s"
elif ! is_clustered; then
url="http://$(unit-get private-address):$port/v1/\$(tenant_id)s"
fi
# determine correct endpoint URL
https && scheme="https" || scheme="http"
is_clustered && local host=$(config-get vip) ||
local host=$(unit-get private-address)
local url="$scheme://$host:$(config-get api-listening-port)/v1/\$(tenant_id)s"
relation-set service="cinder" \
region="$(config-get region)" public_url="$url" admin_url="$url" internal_url="$url"
}
@ -133,6 +134,7 @@ keystone_changed() {
set_or_update "auth_strategy" "keystone" "$CINDER_CONF"
cinder_ctl all restart
configure_https
}
function ceph_joined {
@ -191,7 +193,16 @@ EOF
}
function cluster_changed() {
configure_haproxy "cinder_api:$(config-get api-listening-port)"
service_enabled "api" || return 0
[[ -z "$(peer_units)" ]] &&
juju-log "cluster_changed() with no peers." && exit 0
local cfg_api_port="$(config-get api-listening-port)"
local haproxy_port="$(determine_haproxy_port $cfg_api_port)"
local backend_port="$(determine_api_port $cfg_api_port)"
service cinder-api stop
configure_haproxy "cinder_api:$haproxy_port:$backend_port"
set_or_update osapi_volume_listen_port "$backend_port"
service cinder-api start
}
function upgrade_charm() {
@ -220,26 +231,26 @@ function ha_relation_joined() {
init_services="{
'res_cinder_haproxy':'haproxy'
}"
groups="{
'grp_cinder_haproxy':'res_cinder_vip res_cinder_haproxy'
clones="{
'cl_cinder_haproxy': 'res_cinder_haproxy'
}"
relation-set corosync_bindiface=$corosync_bindiface \
corosync_mcastport=$corosync_mcastport \
resources="$resources" resource_params="$resource_params" \
init_services="$init_services" groups="$groups"
init_services="$init_services" clones="$clones"
else
juju-log "Insufficient configuration data to configure hacluster"
exit 1
fi
fi
}
function ha_relation_changed() {
local clustered=`relation-get clustered`
if [ -n "$clustered" ] && is_leader 'res_cinder_vip'; then
juju-log "Cluster leader, reconfiguring keystone endpoint"
local port=$(config-get api-listening-port)
port=$(($port + 10000))
local url="http://$(config-get vip):$port/v1/\$(tenant_id)s"
https && local scheme="https" || local scheme="http"
local url="$scheme://$(config-get vip):$(config-get api-listening-port)/v1/\$(tenant_id)s"
local r_id=""
for r_id in `relation-ids identity-service`; do
relation-set -r $r_id service="cinder" \
region="$(config-get region)" \
@ -249,6 +260,7 @@ function ha_relation_changed() {
}
function config_changed() {
configure_https
# Save our scriptrc env variables for health checks
declare -a env_vars=(
"OPENSTACK_PORT_MCASTPORT=$(config-get ha-mcastport)"

View File

@ -321,7 +321,6 @@ function get_block_device() {
HAPROXY_CFG=/etc/haproxy/haproxy.cfg
HAPROXY_DEFAULT=/etc/default/haproxy
##########################################################################
# Description: Configures HAProxy services for Openstack API's
# Parameters:
@ -330,9 +329,8 @@ HAPROXY_DEFAULT=/etc/default/haproxy
# assumes the name of the peer relation is 'cluster' and that every
# service unit in the peer relation is running the same services.
#
# The HAProxy service will listen on port + 10000.
# Example:
# configure_haproxy cinder_api:12345 nova_api:9999
# Example
# configure_haproxy cinder_api:8776:8756i nova_api:8774:8764
##########################################################################
configure_haproxy() {
local address=`unit-get private-address`
@ -368,14 +366,18 @@ listen stats :8888
EOF
for service in $@; do
local service_name=$(echo $service | cut -d : -f 1)
local api_listen_port=$(echo $service | cut -d : -f 2)
local haproxy_listen_port=$(($api_listen_port + 10000))
local haproxy_listen_port=$(echo $service | cut -d : -f 2)
local api_listen_port=$(echo $service | cut -d : -f 3)
juju-log "Adding haproxy configuration entry for $service "\
"($haproxy_listen_port -> $api_listen_port)"
cat >> $HAPROXY_CFG << EOF
listen $service_name 0.0.0.0:$haproxy_listen_port
balance roundrobin
option tcplog
server $name $address:$api_listen_port check
EOF
local r_id=""
local unit=""
for r_id in `relation-ids cluster`; do
for unit in `relation-list -r $r_id`; do
local unit_name=${unit////-}
@ -388,6 +390,7 @@ EOF
done
done
echo "ENABLED=1" > $HAPROXY_DEFAULT
service haproxy restart
}
##########################################################################
@ -395,18 +398,20 @@ EOF
# Returns: 0 if configured, 1 if not configured
##########################################################################
is_clustered() {
local r_id=""
local unit=""
for r_id in $(relation-ids ha); do
if [ -n "$r_id" ]; then
for unit in $(relation-list -r $r_id); do
clustered=$(relation-get -r $r_id clustered $unit)
if [ -n "$clustered" ]; then
echo "Unit is clustered"
juju-log "Unit is haclustered"
return 0
fi
done
fi
done
echo "Unit is not clustered"
juju-log "Unit is not haclustered"
return 1
}
@ -415,6 +420,7 @@ is_clustered() {
##########################################################################
peer_units() {
local peers=""
local r_id=""
for r_id in $(relation-ids cluster); do
peers="$peers $(relation-list -r $r_id)"
done
@ -433,11 +439,11 @@ oldest_peer() {
echo "Comparing $JUJU_UNIT_NAME with peers: $peers"
local r_unit_no=$(echo $peer | cut -d / -f 2)
if (($r_unit_no<$l_unit_no)); then
echo "Not oldest peer; deferring"
juju-log "Not oldest peer; deferring"
return 1
fi
done
echo "Oldest peer; might take charge?"
juju-log "Oldest peer; might take charge?"
return 0
}
@ -451,13 +457,13 @@ oldest_peer() {
eligible_leader() {
if is_clustered; then
if ! is_leader $1; then
echo 'Deferring action to CRM leader'
juju-log 'Deferring action to CRM leader'
return 1
fi
else
peers=$(peer_units)
if [ -n "$peers" ] && ! oldest_peer "$peers"; then
echo 'Deferring action to oldest service unit.'
juju-log 'Deferring action to oldest service unit.'
return 1
fi
fi
@ -469,14 +475,14 @@ eligible_leader() {
# Returns: 0 if peered, 1 if not peered
##########################################################################
is_peered() {
r_id=$(relation-ids cluster)
local r_id=$(relation-ids cluster)
if [ -n "$r_id" ]; then
if [ -n "$(relation-list -r $r_id)" ]; then
echo "Unit peered"
juju-log "Unit peered"
return 0
fi
fi
echo "Unit not peered"
juju-log "Unit not peered"
return 1
}
@ -489,14 +495,209 @@ is_leader() {
hostname=`hostname`
if [ -x /usr/sbin/crm ]; then
if crm resource show $1 | grep -q $hostname; then
echo "$hostname is cluster leader"
juju-log "$hostname is cluster leader."
return 0
fi
fi
echo "$hostname is not cluster leader"
juju-log "$hostname is not cluster leader."
return 1
}
##########################################################################
# Description: Determines whether enough data has been provided in
# configuration or relation data to configure HTTPS.
# Parameters: None
# Returns: 0 if HTTPS can be configured, 1 if not.
##########################################################################
https() {
local r_id=""
if [[ -n "$(config-get ssl_cert)" ]] &&
[[ -n "$(config-get ssl_key)" ]] ; then
return 0
fi
for r_id in $(relation-ids identity-service) ; do
for unit in $(relation-list -r $r_id) ; do
if [[ "$(relation-get -r $r_id https_keystone $unit)" == "True" ]] &&
[[ -n "$(relation-get -r $r_id ssl_cert $unit)" ]] &&
[[ -n "$(relation-get -r $r_id ssl_key $unit)" ]] &&
[[ -n "$(relation-get -r $r_id ca_cert $unit)" ]] ; then
return 0
fi
done
done
return 1
}
##########################################################################
# Description: For a given number of port mappings, configures apache2
# HTTPs local reverse proxying using certficates and keys provided in
# either configuration data (preferred) or relation data. Assumes ports
# are not in use (calling charm should ensure that).
# Parameters: Variable number of proxy port mappings as
# $internal:$external.
# Returns: 0 if reverse proxy(s) have been configured, 0 if not.
##########################################################################
enable_https() {
local port_maps="$@"
local http_restart=""
juju-log "Enabling HTTPS for port mappings: $port_maps."
# allow overriding of keystone provided certs with those set manually
# in config.
local cert=$(config-get ssl_cert)
local key=$(config-get ssl_key)
local ca_cert=""
if [[ -z "$cert" ]] || [[ -z "$key" ]] ; then
juju-log "Inspecting identity-service relations for SSL certificate."
local r_id=""
cert=""
key=""
ca_cert=""
for r_id in $(relation-ids identity-service) ; do
for unit in $(relation-list -r $r_id) ; do
[[ -z "$cert" ]] && cert="$(relation-get -r $r_id ssl_cert $unit)"
[[ -z "$key" ]] && key="$(relation-get -r $r_id ssl_key $unit)"
[[ -z "$ca_cert" ]] && ca_cert="$(relation-get -r $r_id ca_cert $unit)"
done
done
[[ -n "$cert" ]] && cert=$(echo $cert | base64 -di)
[[ -n "$key" ]] && key=$(echo $key | base64 -di)
[[ -n "$ca_cert" ]] && ca_cert=$(echo $ca_cert | base64 -di)
else
juju-log "Using SSL certificate provided in service config."
fi
[[ -z "$cert" ]] || [[ -z "$key" ]] &&
juju-log "Expected but could not find SSL certificate data, not "\
"configuring HTTPS!" && return 1
apt-get -y install apache2
a2enmod ssl proxy proxy_http | grep -v "To activate the new configuration" &&
http_restart=1
mkdir -p /etc/apache2/ssl/$CHARM
echo "$cert" >/etc/apache2/ssl/$CHARM/cert
echo "$key" >/etc/apache2/ssl/$CHARM/key
if [[ -n "$ca_cert" ]] ; then
juju-log "Installing Keystone supplied CA cert."
echo "$ca_cert" >/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt
update-ca-certificates --fresh
# XXX TODO: Find a better way of exporting this?
if [[ "$CHARM" == "nova-cloud-controller" ]] ; then
[[ -e /var/www/keystone_juju_ca_cert.crt ]] &&
rm -rf /var/www/keystone_juju_ca_cert.crt
ln -s /usr/local/share/ca-certificates/keystone_juju_ca_cert.crt \
/var/www/keystone_juju_ca_cert.crt
fi
fi
for port_map in $port_maps ; do
local ext_port=$(echo $port_map | cut -d: -f1)
local int_port=$(echo $port_map | cut -d: -f2)
juju-log "Creating apache2 reverse proxy vhost for $port_map."
cat >/etc/apache2/sites-available/${CHARM}_${ext_port} <<END
Listen $ext_port
NameVirtualHost *:$ext_port
<VirtualHost *:$ext_port>
ServerName $(unit-get private-address)
SSLEngine on
SSLCertificateFile /etc/apache2/ssl/$CHARM/cert
SSLCertificateKeyFile /etc/apache2/ssl/$CHARM/key
ProxyPass / http://localhost:$int_port/
ProxyPassReverse / http://localhost:$int_port/
ProxyPreserveHost on
</VirtualHost>
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
<Location />
Order allow,deny
Allow from all
</Location>
END
a2ensite ${CHARM}_${ext_port} | grep -v "To activate the new configuration" &&
http_restart=1
done
if [[ -n "$http_restart" ]] ; then
service apache2 restart
fi
}
##########################################################################
# Description: Ensure HTTPS reverse proxying is disabled for given port
# mappings.
# Parameters: Variable number of proxy port mappings as
# $internal:$external.
# Returns: 0 if reverse proxy is not active for all portmaps, 1 on error.
##########################################################################
disable_https() {
local port_maps="$@"
local http_restart=""
juju-log "Ensuring HTTPS disabled for $port_maps."
( [[ ! -d /etc/apache2 ]] || [[ ! -d /etc/apache2/ssl/$CHARM ]] ) && return 0
for port_map in $port_maps ; do
local ext_port=$(echo $port_map | cut -d: -f1)
local int_port=$(echo $port_map | cut -d: -f2)
if [[ -e /etc/apache2/sites-available/${CHARM}_${ext_port} ]] ; then
juju-log "Disabling HTTPS reverse proxy for $CHARM $port_map."
a2dissite ${CHARM}_${ext_port} | grep -v "To activate the new configuration" &&
http_restart=1
fi
done
if [[ -n "$http_restart" ]] ; then
service apache2 restart
fi
}
##########################################################################
# Description: Ensures HTTPS is either enabled or disabled for given port
# mapping.
# Parameters: Variable number of proxy port mappings as
# $internal:$external.
# Returns: 0 if HTTPS reverse proxy is in place, 1 if it is not.
##########################################################################
setup_https() {
# configure https via apache reverse proxying either
# using certs provided by config or keystone.
[[ -z "$CHARM" ]] &&
error_out "setup_https(): CHARM not set."
if ! https ; then
disable_https $@
else
enable_https $@
fi
}
##########################################################################
# Description: Determine correct API server listening port based on
# existence of HTTPS reverse proxy and/or haproxy.
# Paremeters: The standard public port for given service.
# Returns: The correct listening port for API service.
##########################################################################
determine_api_port() {
local public_port="$1"
local i=0
( [[ -n "$(peer_units)" ]] || is_clustered >/dev/null 2>&1 ) && i=$[$i + 1]
https >/dev/null 2>&1 && i=$[$i + 1]
echo $[$public_port - $[$i * 10]]
}
##########################################################################
# Description: Determine correct proxy listening port based on public IP +
# existence of HTTPS reverse proxy.
# Paremeters: The standard public port for given service.
# Returns: The correct listening port for haproxy service public address.
##########################################################################
determine_haproxy_port() {
local public_port="$1"
local i=0
https >/dev/null 2>&1 && i=$[$i + 1]
echo $[$public_port - $[$i * 10]]
}
##########################################################################
# Description: Print the value for a given config option in an OpenStack
# .ini style configuration file.

View File

@ -1 +1 @@
15
20