Browse code

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

Jamie Lennox authored on 2013/09/20 15:26:42
Showing 12 changed files
... ...
@@ -209,6 +209,7 @@ function configure_cinder() {
209 209
     inicomment $CINDER_API_PASTE_INI filter:authtoken auth_host
210 210
     inicomment $CINDER_API_PASTE_INI filter:authtoken auth_port
211 211
     inicomment $CINDER_API_PASTE_INI filter:authtoken auth_protocol
212
+    inicomment $CINDER_API_PASTE_INI filter:authtoken cafile
212 213
     inicomment $CINDER_API_PASTE_INI filter:authtoken admin_tenant_name
213 214
     inicomment $CINDER_API_PASTE_INI filter:authtoken admin_user
214 215
     inicomment $CINDER_API_PASTE_INI filter:authtoken admin_password
... ...
@@ -219,6 +220,7 @@ function configure_cinder() {
219 219
     iniset $CINDER_CONF keystone_authtoken auth_host $KEYSTONE_AUTH_HOST
220 220
     iniset $CINDER_CONF keystone_authtoken auth_port $KEYSTONE_AUTH_PORT
221 221
     iniset $CINDER_CONF keystone_authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL
222
+    iniset $CINDER_CONF keystone_authtoken cafile $KEYSTONE_SSL_CA
222 223
     iniset $CINDER_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
223 224
     iniset $CINDER_CONF keystone_authtoken admin_user cinder
224 225
     iniset $CINDER_CONF keystone_authtoken admin_password $SERVICE_PASSWORD
... ...
@@ -82,6 +82,7 @@ function configure_glance() {
82 82
     iniset $GLANCE_REGISTRY_CONF keystone_authtoken auth_host $KEYSTONE_AUTH_HOST
83 83
     iniset $GLANCE_REGISTRY_CONF keystone_authtoken auth_port $KEYSTONE_AUTH_PORT
84 84
     iniset $GLANCE_REGISTRY_CONF keystone_authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL
85
+    iniset $GLANCE_REGISTRY_CONF keystone_authtoken cafile $KEYSTONE_SSL_CA
85 86
     iniset $GLANCE_REGISTRY_CONF keystone_authtoken auth_uri $KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/
86 87
     iniset $GLANCE_REGISTRY_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
87 88
     iniset $GLANCE_REGISTRY_CONF keystone_authtoken admin_user glance
... ...
@@ -99,6 +100,7 @@ function configure_glance() {
99 99
     iniset $GLANCE_API_CONF keystone_authtoken auth_host $KEYSTONE_AUTH_HOST
100 100
     iniset $GLANCE_API_CONF keystone_authtoken auth_port $KEYSTONE_AUTH_PORT
101 101
     iniset $GLANCE_API_CONF keystone_authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL
102
+    iniset $GLANCE_API_CONF keystone_authtoken cafile $KEYSTONE_SSL_CA
102 103
     iniset $GLANCE_API_CONF keystone_authtoken auth_uri $KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/
103 104
     iniset $GLANCE_API_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
104 105
     iniset $GLANCE_API_CONF keystone_authtoken admin_user glance
... ...
@@ -96,6 +96,7 @@ function configure_heat() {
96 96
     iniset $HEAT_CONF keystone_authtoken auth_port $KEYSTONE_AUTH_PORT
97 97
     iniset $HEAT_CONF keystone_authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL
98 98
     iniset $HEAT_CONF keystone_authtoken auth_uri $KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/v2.0
99
+    iniset $HEAT_CONF keystone_authtoken cafile $KEYSTONE_SSL_CA
99 100
     iniset $HEAT_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
100 101
     iniset $HEAT_CONF keystone_authtoken admin_user heat
101 102
     iniset $HEAT_CONF keystone_authtoken admin_password $SERVICE_PASSWORD
... ...
@@ -98,6 +98,7 @@ function configure_ironic_api() {
98 98
     iniset $IRONIC_CONF_FILE keystone_authtoken auth_host $KEYSTONE_AUTH_HOST
99 99
     iniset $IRONIC_CONF_FILE keystone_authtoken auth_port $KEYSTONE_AUTH_PORT
100 100
     iniset $IRONIC_CONF_FILE keystone_authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL
101
+    iniset $IRONIC_CONF_FILE keystone_authtoken cafile $KEYSTONE_SSL_CA
101 102
     iniset $IRONIC_CONF_FILE keystone_authtoken auth_uri $KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/
102 103
     iniset $IRONIC_CONF_FILE keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
103 104
     iniset $IRONIC_CONF_FILE keystone_authtoken admin_user ironic
... ...
@@ -4,6 +4,7 @@
4 4
 # Dependencies:
5 5
 #
6 6
 # - ``functions`` file
7
+# - ``tls`` file
7 8
 # - ``DEST``, ``STACK_USER``
8 9
 # - ``IDENTITY_API_VERSION``
9 10
 # - ``BASE_SQL_CONN``
... ...
@@ -79,6 +80,13 @@ KEYSTONE_VALID_IDENTITY_BACKENDS=kvs,ldap,pam,sql
79 79
 # valid assignment backends as per dir keystone/identity/backends
80 80
 KEYSTONE_VALID_ASSIGNMENT_BACKENDS=kvs,ldap,sql
81 81
 
82
+# if we are running with SSL use https protocols
83
+if is_ssl_enabled_service "key"; then
84
+    KEYSTONE_AUTH_PROTOCOL="https"
85
+    KEYSTONE_SERVICE_PROTOCOL="https"
86
+fi
87
+
88
+
82 89
 # Functions
83 90
 # ---------
84 91
 # cleanup_keystone() - Remove residual data files, anything left over from previous
... ...
@@ -172,6 +180,15 @@ function configure_keystone() {
172 172
     iniset $KEYSTONE_CONF DEFAULT public_endpoint "$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:%(public_port)s/"
173 173
     iniset $KEYSTONE_CONF DEFAULT admin_endpoint "$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:%(admin_port)s/"
174 174
 
175
+    # Register SSL certificates if provided
176
+    if is_ssl_enabled_service key; then
177
+        ensure_certificates KEYSTONE
178
+
179
+        iniset $KEYSTONE_CONF ssl enable True
180
+        iniset $KEYSTONE_CONF ssl certfile $KEYSTONE_SSL_CERT
181
+        iniset $KEYSTONE_CONF ssl keyfile $KEYSTONE_SSL_KEY
182
+    fi
183
+
175 184
     if is_service_enabled tls-proxy; then
176 185
         # Set the service ports for a proxy to take the originals
177 186
         iniset $KEYSTONE_CONF DEFAULT public_port $KEYSTONE_SERVICE_PORT_INT
... ...
@@ -373,7 +390,7 @@ function start_keystone() {
373 373
     fi
374 374
 
375 375
     echo "Waiting for keystone to start..."
376
-    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
376
+    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
377 377
         die $LINENO "keystone did not start"
378 378
     fi
379 379
 
... ...
@@ -225,6 +225,7 @@ function configure_nova() {
225 225
         inicomment $NOVA_API_PASTE_INI filter:authtoken auth_host
226 226
         inicomment $NOVA_API_PASTE_INI filter:authtoken auth_protocol
227 227
         inicomment $NOVA_API_PASTE_INI filter:authtoken admin_tenant_name
228
+        inicomment $NOVA_API_PASTE_INI filter:authtoken cafile
228 229
         inicomment $NOVA_API_PASTE_INI filter:authtoken admin_user
229 230
         inicomment $NOVA_API_PASTE_INI filter:authtoken admin_password
230 231
     fi
... ...
@@ -399,6 +400,7 @@ function create_nova_conf() {
399 399
         iniset $NOVA_CONF keystone_authtoken auth_host $KEYSTONE_AUTH_HOST
400 400
         iniset $NOVA_CONF keystone_authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL
401 401
         iniset $NOVA_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
402
+        iniset $NOVA_CONF keystone_authtoken cafile $KEYSTONE_SSL_CA
402 403
         iniset $NOVA_CONF keystone_authtoken admin_user nova
403 404
         iniset $NOVA_CONF keystone_authtoken admin_password $SERVICE_PASSWORD
404 405
     fi
... ...
@@ -306,6 +306,7 @@ function configure_swift() {
306 306
     iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken auth_host $KEYSTONE_AUTH_HOST
307 307
     iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken auth_port $KEYSTONE_AUTH_PORT
308 308
     iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken auth_protocol $KEYSTONE_AUTH_PROTOCOL
309
+    iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken cafile $KEYSTONE_SSL_CA
309 310
     iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken auth_uri $KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/
310 311
     iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken admin_tenant_name $SERVICE_TENANT_NAME
311 312
     iniset ${SWIFT_CONFIG_PROXY_SERVER} filter:authtoken admin_user swift
... ...
@@ -325,6 +326,7 @@ paste.filter_factory = keystone.middleware.s3_token:filter_factory
325 325
 auth_port = ${KEYSTONE_AUTH_PORT}
326 326
 auth_host = ${KEYSTONE_AUTH_HOST}
327 327
 auth_protocol = ${KEYSTONE_AUTH_PROTOCOL}
328
+cafile = ${KEYSTONE_SSL_CA}
328 329
 auth_token = ${SERVICE_TOKEN}
329 330
 admin_token = ${SERVICE_TOKEN}
330 331
 
... ...
@@ -22,7 +22,8 @@
22 22
 # - make_int_ca
23 23
 # - new_cert $INT_CA_DIR int-server "abc"
24 24
 # - start_tls_proxy HOST_IP 5000 localhost 5000
25
-
25
+# - ensure_certificates
26
+# - is_ssl_enabled_service
26 27
 
27 28
 # Defaults
28 29
 # --------
... ...
@@ -309,6 +310,53 @@ function make_root_CA() {
309 309
 }
310 310
 
311 311
 
312
+# Certificate Input Configuration
313
+# ===============================
314
+
315
+# check to see if the service(s) specified are to be SSL enabled.
316
+#
317
+# Multiple services specified as arguments are ``OR``'ed together; the test
318
+# is a short-circuit boolean, i.e it returns on the first match.
319
+#
320
+# Uses global ``SSL_ENABLED_SERVICES``
321
+function is_ssl_enabled_service() {
322
+    services=$@
323
+    for service in ${services}; do
324
+        [[ ,${SSL_ENABLED_SERVICES}, =~ ,${service}, ]] && return 0
325
+    done
326
+    return 1
327
+}
328
+
329
+
330
+# Ensure that the certificates for a service are in place. This function does
331
+# not check that a service is SSL enabled, this should already have been
332
+# completed.
333
+#
334
+# The function expects to find a certificate, key and CA certificate in the
335
+# variables {service}_SSL_CERT, {service}_SSL_KEY and {service}_SSL_CA. For
336
+# example for keystone this would be KEYSTONE_SSL_CERT, KEYSTONE_SSL_KEY and
337
+# KEYSTONE_SSL_CA. If it does not find these certificates the program will
338
+# quit.
339
+function ensure_certificates() {
340
+    local service=$1
341
+
342
+    local cert_var="${service}_SSL_CERT"
343
+    local key_var="${service}_SSL_KEY"
344
+    local ca_var="${service}_SSL_CA"
345
+
346
+    local cert=${!cert_var}
347
+    local key=${!key_var}
348
+    local ca=${!ca_var}
349
+
350
+    if [[ !($cert && $key && $ca) ]]; then
351
+        die $LINENO "Missing either the ${cert_var} ${key_var} or ${ca_var}" \
352
+                    "variable to enable SSL for ${service}"
353
+    fi
354
+
355
+    cat $ca >> $SSL_BUNDLE_FILE
356
+}
357
+
358
+
312 359
 # Proxy Functions
313 360
 # ===============
314 361
 
... ...
@@ -29,7 +29,6 @@ TROVE_DIR=$DEST/trove
29 29
 TROVECLIENT_DIR=$DEST/python-troveclient
30 30
 TROVE_CONF_DIR=/etc/trove
31 31
 TROVE_LOCAL_CONF_DIR=$TROVE_DIR/etc/trove
32
-TROVE_AUTH_ENDPOINT=$KEYSTONE_AUTH_PROTOCOL://$KEYSTONE_AUTH_HOST:$KEYSTONE_AUTH_PORT//v$IDENTITY_API_VERSION
33 32
 TROVE_AUTH_CACHE_DIR=${TROVE_AUTH_CACHE_DIR:-/var/cache/trove}
34 33
 TROVE_BIN_DIR=/usr/local/bin
35 34
 
... ...
@@ -102,6 +101,7 @@ function configure_trove() {
102 102
     iniset $TROVE_API_PASTE_INI filter:tokenauth auth_host $KEYSTONE_AUTH_HOST
103 103
     iniset $TROVE_API_PASTE_INI filter:tokenauth auth_port $KEYSTONE_AUTH_PORT
104 104
     iniset $TROVE_API_PASTE_INI filter:tokenauth auth_protocol $KEYSTONE_AUTH_PROTOCOL
105
+    iniset $TROVE_API_PASTE_INI filter:tokenauth cafile $KEYSTONE_SSL_CA
105 106
     iniset $TROVE_API_PASTE_INI filter:tokenauth admin_tenant_name $SERVICE_TENANT_NAME
106 107
     iniset $TROVE_API_PASTE_INI filter:tokenauth admin_user trove
107 108
     iniset $TROVE_API_PASTE_INI filter:tokenauth admin_password $SERVICE_PASSWORD
... ...
@@ -123,6 +123,8 @@ function configure_trove() {
123 123
 
124 124
     # (Re)create trove taskmanager conf file if needed
125 125
     if is_service_enabled tr-tmgr; then
126
+        TROVE_AUTH_ENDPOINT=$KEYSTONE_AUTH_PROTOCOL://$KEYSTONE_AUTH_HOST:$KEYSTONE_AUTH_PORT//v$IDENTITY_API_VERSION
127
+
126 128
         iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT rabbit_password $RABBIT_PASSWORD
127 129
         iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT sql_connection `database_connection_url trove`
128 130
         iniset $TROVE_CONF_DIR/trove-taskmanager.conf DEFAULT taskmanager_manager trove.taskmanager.manager.Manager
... ...
@@ -58,6 +58,7 @@ export OS_NO_CACHE=${OS_NO_CACHE:-1}
58 58
 HOST_IP=${HOST_IP:-127.0.0.1}
59 59
 SERVICE_HOST=${SERVICE_HOST:-$HOST_IP}
60 60
 SERVICE_PROTOCOL=${SERVICE_PROTOCOL:-http}
61
+KEYSTONE_AUTH_PROTOCOL=${KEYSTONE_AUTH_PROTOCOL:-$SERVICE_PROTOCOL}
61 62
 
62 63
 # Some exercises call glance directly.  On a single-node installation, Glance
63 64
 # 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}
71 71
 # the user/tenant has access to - including nova, glance, keystone, swift, ...
72 72
 # We currently recommend using the 2.0 *identity api*.
73 73
 #
74
-export OS_AUTH_URL=$SERVICE_PROTOCOL://$SERVICE_HOST:5000/v${OS_IDENTITY_API_VERSION}
74
+export OS_AUTH_URL=$KEYSTONE_AUTH_PROTOCOL://$SERVICE_HOST:5000/v${OS_IDENTITY_API_VERSION}
75 75
 
76 76
 # Set the pointer to our CA certificate chain.  Harmless if TLS is not used.
77
-export OS_CACERT=$INT_CA_DIR/ca-chain.pem
77
+export OS_CACERT=${OS_CACERT:-$INT_CA_DIR/ca-chain.pem}
78 78
 
79 79
 # Currently novaclient needs you to specify the *compute api* version.  This
80 80
 # needs to match the config of your catalog returned by Keystone.
... ...
@@ -290,6 +290,10 @@ LOG_COLOR=`trueorfalse True $LOG_COLOR`
290 290
 # Service startup timeout
291 291
 SERVICE_TIMEOUT=${SERVICE_TIMEOUT:-60}
292 292
 
293
+# Reset the bundle of CA certificates
294
+SSL_BUNDLE_FILE="$DATA_DIR/ca-bundle.pem"
295
+rm -f $SSL_BUNDLE_FILE
296
+
293 297
 
294 298
 # Configure Projects
295 299
 # ==================
... ...
@@ -798,6 +802,17 @@ fi
798 798
 restart_rpc_backend
799 799
 
800 800
 
801
+# Export Certicate Authority Bundle
802
+# ---------------------------------
803
+
804
+# If certificates were used and written to the SSL bundle file then these
805
+# should be exported so clients can validate their connections.
806
+
807
+if [ -f $SSL_BUNDLE_FILE ]; then
808
+    export OS_CACERT=$SSL_BUNDLE_FILE
809
+fi
810
+
811
+
801 812
 # Configure database
802 813
 # ------------------
803 814
 
... ...
@@ -1145,6 +1160,7 @@ if is_service_enabled trove; then
1145 1145
     start_trove
1146 1146
 fi
1147 1147
 
1148
+
1148 1149
 # Create account rc files
1149 1150
 # =======================
1150 1151
 
... ...
@@ -1153,7 +1169,13 @@ fi
1153 1153
 # which is helpful in image bundle steps.
1154 1154
 
1155 1155
 if is_service_enabled nova && is_service_enabled key; then
1156
-    $TOP_DIR/tools/create_userrc.sh -PA --target-dir $TOP_DIR/accrc
1156
+    USERRC_PARAMS="-PA --target-dir $TOP_DIR/accrc"
1157
+
1158
+    if [ -f $SSL_BUNDLE_FILE ]; then
1159
+        USERRC_PARAMS="$USERRC_PARAMS --os-cacert $SSL_BUNDLE_FILE"
1160
+    fi
1161
+
1162
+    $TOP_DIR/tools/create_userrc.sh $USERRC_PARAMS
1157 1163
 fi
1158 1164
 
1159 1165
 
... ...
@@ -1229,7 +1251,7 @@ fi
1229 1229
 CURRENT_RUN_TIME=$(date "+$TIMESTAMP_FORMAT")
1230 1230
 echo "# $CURRENT_RUN_TIME" >$TOP_DIR/.stackenv
1231 1231
 for i in BASE_SQL_CONN ENABLED_SERVICES HOST_IP LOGFILE \
1232
-    SERVICE_HOST SERVICE_PROTOCOL STACK_USER TLS_IP; do
1232
+    SERVICE_HOST SERVICE_PROTOCOL STACK_USER TLS_IP KEYSTONE_AUTH_PROTOCOL OS_CACERT; do
1233 1233
     echo $i=${!i} >>$TOP_DIR/.stackenv
1234 1234
 done
1235 1235
 
... ...
@@ -43,6 +43,7 @@ Optional Arguments
43 43
 --os-tenant-name <tenant_name>
44 44
 --os-tenant-id <tenant_id>
45 45
 --os-auth-url <auth_url>
46
+--os-cacert <cert file>
46 47
 --target-dir <target_directory>
47 48
 --skip-tenant <tenant-name>
48 49
 --debug
... ...
@@ -53,7 +54,7 @@ $0 -P -C mytenant -u myuser -p mypass
53 53
 EOF
54 54
 }
55 55
 
56
-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 -- "$@")
56
+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 -- "$@")
57 57
 then
58 58
     #parse error
59 59
     display_help
... ...
@@ -80,6 +81,7 @@ do
80 80
     --os-tenant-id) export OS_TENANT_ID=$2; shift ;;
81 81
     --skip-tenant) SKIP_TENANT="$SKIP_TENANT$2,"; shift ;;
82 82
     --os-auth-url) export OS_AUTH_URL=$2; shift ;;
83
+    --os-cacert) export OS_CACERT=$2; shift ;;
83 84
     --target-dir) ACCOUNT_DIR=$2; shift ;;
84 85
     --debug) set -o xtrace ;;
85 86
     -u) MODE=${MODE:-one};  USER_NAME=$2; shift ;;
... ...
@@ -201,6 +203,7 @@ export OS_USERNAME="$user_name"
201 201
 # Openstack Tenant ID = $tenant_id
202 202
 export OS_TENANT_NAME="$tenant_name"
203 203
 export OS_AUTH_URL="$OS_AUTH_URL"
204
+export OS_CACERT="$OS_CACERT"
204 205
 export EC2_CERT="$ec2_cert"
205 206
 export EC2_PRIVATE_KEY="$ec2_private_key"
206 207
 export EC2_USER_ID=42 #not checked by nova (can be a 12-digit id)