Merge lp:~james-page/charms/precise/openstack-dashboard/ha-support into lp:~openstack-charmers/charms/precise/openstack-dashboard/ha-support
- Precise Pangolin (12.04)
- ha-support
- Merge into ha-support
Status: | Merged |
---|---|
Merged at revision: | 28 |
Proposed branch: | lp:~james-page/charms/precise/openstack-dashboard/ha-support |
Merge into: | lp:~openstack-charmers/charms/precise/openstack-dashboard/ha-support |
Diff against target: |
596 lines (+331/-68) 4 files modified
config.yaml (+25/-0) hooks/horizon-common (+45/-29) hooks/horizon-relations (+36/-17) hooks/lib/openstack-common (+225/-22) |
To merge this branch: | bzr merge lp:~james-page/charms/precise/openstack-dashboard/ha-support |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Adam Gandelman (community) | Needs Fixing | ||
Review via email: mp+152712@code.launchpad.net |
Commit message
Description of the change
James Page (james-page) wrote : | # |
Adam Gandelman (gandelman-a) wrote : | # |
Hey James, some comments:
- Please add a note to config.yaml or docs stating ssl_cert + ssl_key are expected to be input as base64 encoded.
- Once I realized that, I did a juju set openstack-dashboard ssl_key="$(cat /tmp/key | base64)" ssl_cert="$(cat tmp/cert | base64)" but was getting base64 decode errors in config-changed, needed to update the horizon-common cert config function:
key=$2
- echo $cert | base64 -d > /etc/ssl/
- echo $key | base64 -d > /etc/ssl/
+ echo $cert | base64 -di > /etc/ssl/
+ echo $key | base64 -di > /etc/ssl/
chmod 0600 /etc/ssl/
- I don't think its a blocker, but one issue with putting SSL termination behind haproxy is that all service units are sharing the same certificate. When deploying with custom certificates, its the deployers responsibility to provide a certificate that will work for all dashboard hosts. When using hacluster/VIP, a cert signed for a single common name (the VIP) would work. However, if we want to use keystone-provided certificates (I hope to), when there is no VIP/hacluster, the generated certificate will only be signed for the eligible leader at the time the keystone <-> dashboard relation is made. This will work okay initially, but if that initial leader host goes away and requests start hitting another node in the service group, a certificate for an invalid host will be used and a browser warning. This is less of an issue for the dashboard since it ends with a browser warning, but more of an issue if we plan on using this configuration for the internal openstack API traffic.
- Also, can you please include the latest openstack-common helper from lp:~openstack-charmers/openstack-charm-helpers/ha-helpers and rebase your changes to that code against the latest? It'll make syncing these changes easier once this merges, and I expect we'll be using the haproxy changes elsewhere.
- 42. By James Page
-
Fixup base64 encoding features
- 43. By James Page
-
Rebase from target
Preview Diff
1 | === modified file 'config.yaml' |
2 | --- config.yaml 2013-02-13 16:00:50 +0000 |
3 | +++ config.yaml 2013-03-13 11:10:27 +0000 |
4 | @@ -49,3 +49,28 @@ |
5 | description: | |
6 | Default multicast port number that will be used to communicate between |
7 | HA Cluster nodes. |
8 | + # User provided SSL cert and key |
9 | + ssl_cert: |
10 | + type: string |
11 | + description: | |
12 | + Base64 encoded SSL certificate to install and use for API ports. |
13 | + . |
14 | + juju set swift-proxy ssl_cert="$(cat cert | base64)" \ |
15 | + ssl_key="$(cat key | base64)" |
16 | + . |
17 | + Setting this value (and ssl_key) will enable reverse proxying, point |
18 | + Swifts's entry in the Keystone catalog to use https, and override |
19 | + any certficiate and key issued by Keystone (if it is configured to |
20 | + do so). |
21 | + ssl_key: |
22 | + type: string |
23 | + description: | |
24 | + Base64 encoded SSL key to use with certificate specified as ssl_cert. |
25 | + offline-compression: |
26 | + type: string |
27 | + default: "yes" |
28 | + description: Use pre-generated Less compiled JS and CSS. |
29 | + debug: |
30 | + type: string |
31 | + default: "no" |
32 | + description: Show Django debug messages. |
33 | |
34 | === modified file 'hooks/horizon-common' |
35 | --- hooks/horizon-common 2013-02-14 14:22:41 +0000 |
36 | +++ hooks/horizon-common 2013-03-13 11:10:27 +0000 |
37 | @@ -1,4 +1,5 @@ |
38 | #!/bin/bash |
39 | +# vim: set ts=2:et |
40 | |
41 | CHARM="openstack-dashboard" |
42 | |
43 | @@ -16,15 +17,28 @@ |
44 | local key=$1 value=$2 |
45 | [[ -z "$key" ]] || [[ -z "$value" ]] && |
46 | juju-log "$CHARM set_or_update: ERROR - missing parameters" && return 1 |
47 | - grep -q "^$key = \"$value\"" "$LOCAL_SETTINGS" && |
48 | - juju-log "$CHARM set_or_update: $key = $value already set" && return 0 |
49 | + if [ "$value" == "True" ] || [ "$value" == "False" ]; then |
50 | + grep -q "^$key = $value" "$LOCAL_SETTINGS" && |
51 | + juju-log "$CHARM set_or_update: $key = $value already set" && return 0 |
52 | + else |
53 | + grep -q "^$key = \"$value\"" "$LOCAL_SETTINGS" && |
54 | + juju-log "$CHARM set_or_update: $key = $value already set" && return 0 |
55 | + fi |
56 | if grep -q "^$key = " "$LOCAL_SETTINGS" ; then |
57 | juju-log "$CHARM set_or_update: Setting $key = $value" |
58 | cp "$LOCAL_SETTINGS" /etc/openstack-dashboard/local_settings.last |
59 | - sed -i "s|\(^$key = \).*|\1\"$value\"|g" "$LOCAL_SETTINGS" || return 1 |
60 | + if [ "$value" == "True" ] || [ "$value" == "False" ]; then |
61 | + sed -i "s|\(^$key = \).*|\1$value|g" "$LOCAL_SETTINGS" || return 1 |
62 | + else |
63 | + sed -i "s|\(^$key = \).*|\1\"$value\"|g" "$LOCAL_SETTINGS" || return 1 |
64 | + fi |
65 | else |
66 | juju-log "$CHARM set_or_update: Adding $key = $value" |
67 | - echo "$key = \"$value\"" >>$LOCAL_SETTINGS || return 1 |
68 | + if [ "$value" == "True" ] || [ "$value" == "False" ]; then |
69 | + echo "$key = $value" >>$LOCAL_SETTINGS || return 1 |
70 | + else |
71 | + echo "$key = \"$value\"" >>$LOCAL_SETTINGS || return 1 |
72 | + fi |
73 | fi |
74 | return 0 |
75 | } |
76 | @@ -46,35 +60,37 @@ |
77 | export JUJU_REMOTE_UNIT=$(relation-list -r $r_id | head -n1) |
78 | export JUJU_RELATION="identity-service" |
79 | export JUJU_RELATION_ID="$r_id" |
80 | - local ks_host=$(relation-get -r $r_id private-address) |
81 | - if [[ -n "$ks_host" ]] ; then |
82 | - service_url="http://$ks_host:5000/v2.0" |
83 | + local service_host=$(relation-get -r $r_id service_host) |
84 | + local service_port=$(relation-get -r $r_id service_port) |
85 | + if [[ -n "$service_host" ]] && [[ -n "$service_port" ]] ; then |
86 | + service_url="http://$service_host:$service_port/v2.0" |
87 | set_or_update OPENSTACK_KEYSTONE_URL "$service_url" |
88 | fi |
89 | fi |
90 | } |
91 | |
92 | -configure_memcached() { |
93 | - tfile=`mktemp /etc/.memcached.conf.XXXXXXX` |
94 | - cat > $tfile << EOF |
95 | -# memcache config file - juju openstack-dashboard |
96 | --d |
97 | -logfile /var/log/memcached.log |
98 | --m 64 |
99 | --p 11211 |
100 | --u memcache |
101 | --l 0.0.0.0 |
102 | -# TODO: Enable SASL auth |
103 | -# -S |
104 | -EOF |
105 | - new_hash=`md5sum $tfile|cut -d' ' -f1` |
106 | - old_hash=`md5sum /etc/memcached.conf|cut -d' ' -f1` |
107 | - if [ "$new_hash" != "$old_hash" ]; then |
108 | - juju-log "Reconfiguring memcache for clustering" |
109 | - # TODO: setup SASL authentication |
110 | - mv -f /etc/memcached.conf /etc/memcached.conf.$old_hash |
111 | - mv -f $tfile /etc/memcached.conf |
112 | - service memcached restart |
113 | - fi |
114 | +configure_apache() { |
115 | + # Reconfigure to listen on provided port |
116 | + a2ensite default-ssl || : |
117 | + a2enmod ssl || : |
118 | + for ports in $@; do |
119 | + from_port=$(echo $ports | cut -d : -f 1) |
120 | + to_port=$(echo $ports | cut -d : -f 2) |
121 | + sed -i -e "s/$from_port/$to_port/g" /etc/apache2/ports.conf |
122 | + for site in $(ls -1 /etc/apache2/sites-available); do |
123 | + sed -i -e "s/$from_port/$to_port/g" \ |
124 | + /etc/apache2/sites-available/$site |
125 | + done |
126 | + done |
127 | } |
128 | |
129 | +configure_apache_cert() { |
130 | + cert=$1 |
131 | + key=$2 |
132 | + echo $cert | base64 -di > /etc/ssl/certs/dashboard.cert |
133 | + echo $key | base64 -di > /etc/ssl/private/dashboard.key |
134 | + chmod 0600 /etc/ssl/private/dashboard.key |
135 | + sed -i -e "s|\(.*SSLCertificateFile\).*|\1 /etc/ssl/certs/dashboard.cert|g" \ |
136 | + -e "s|\(.*SSLCertificateKeyFile\).*|\1 /etc/ssl/private/dashboard.key|g" \ |
137 | + /etc/apache2/sites-available/default-ssl |
138 | +} |
139 | |
140 | === modified file 'hooks/horizon-relations' |
141 | --- hooks/horizon-relations 2013-02-27 05:27:51 +0000 |
142 | +++ hooks/horizon-relations 2013-03-13 11:10:27 +0000 |
143 | @@ -50,6 +50,12 @@ |
144 | juju-log "Insufficient information to configure keystone url" |
145 | exit 0 |
146 | fi |
147 | + local ca_cert=$(relation-get ca_cert) |
148 | + if [ -n "$ca_cert" ]; then |
149 | + juju-log "Installing Keystone supplied CA cert." |
150 | + echo $ca_cert | base64 -di > /usr/local/share/ca-certificates/keystone_juju_ca_cert.crt |
151 | + update-ca-certificates --fresh |
152 | + fi |
153 | service_url="http://${service_host}:${service_port}/v2.0" |
154 | juju-log "$CHARM: Configuring Horizon to access keystone @ $service_url." |
155 | set_or_update OPENSTACK_KEYSTONE_URL "$service_url" |
156 | @@ -95,16 +101,39 @@ |
157 | keystone_joined "$relid" |
158 | done |
159 | |
160 | - service apache2 reload |
161 | - |
162 | + if [ "$(config-get offline-compression)" != "yes" ]; then |
163 | + set_or_update COMPRESS_OFFLINE False |
164 | + apt-get install -y nodejs node-less |
165 | + else |
166 | + set_or_update COMPRESS_OFFLINE True |
167 | + fi |
168 | + |
169 | + # Configure default HAProxy + Apache config |
170 | + if [ -n "$(config-get ssl_cert)" ] && \ |
171 | + [ -n "$(config-get ssl_key)" ]; then |
172 | + configure_apache_cert "$(config-get ssl_cert)" "$(config-get ssl_key)" |
173 | + fi |
174 | + |
175 | + if [ "$(config-get debug)" != "yes" ]; then |
176 | + set_or_update DEBUG False |
177 | + else |
178 | + set_or_update DEBUG True |
179 | + fi |
180 | + |
181 | + # Reconfigure Apache Ports |
182 | + configure_apache "80:70" "443:433" |
183 | + service apache2 restart |
184 | + configure_haproxy "dash_insecure:80:70:http dash_secure:443:433:tcp" |
185 | + service haproxy restart |
186 | } |
187 | |
188 | function cluster_changed() { |
189 | - configure_haproxy "openstack_dashboard:80" |
190 | - configure_memcached |
191 | + configure_haproxy "dash_insecure:80:70:http dash_secure:443:433:tcp" |
192 | + service haproxy reload |
193 | } |
194 | |
195 | function ha_relation_joined() { |
196 | + # Configure HA Cluster |
197 | local corosync_bindiface=`config-get ha-bindiface` |
198 | local corosync_mcastport=`config-get ha-mcastport` |
199 | local vip=`config-get vip` |
200 | @@ -126,28 +155,19 @@ |
201 | init_services="{ |
202 | 'res_horizon_haproxy':'haproxy' |
203 | }" |
204 | - groups="{ |
205 | -'grp_horizon_haproxy':'res_horizon_vip res_horizon_haproxy' |
206 | + clones="{ |
207 | +'cl_horizon_haproxy':'res_horizon_haproxy' |
208 | }" |
209 | relation-set corosync_bindiface=$corosync_bindiface \ |
210 | corosync_mcastport=$corosync_mcastport \ |
211 | resources="$resources" resource_params="$resource_params" \ |
212 | - init_services="$init_services" groups="$groups" |
213 | + init_services="$init_services" clones="$clones" |
214 | else |
215 | juju-log "Insufficient configuration data to configure hacluster" |
216 | exit 1 |
217 | fi |
218 | } |
219 | |
220 | - |
221 | -function ha_relation_changed() { |
222 | - local clustered=`relation-get clustered` |
223 | - if [ -n "$clustered" ]; then |
224 | - open-port 10080 |
225 | - fi |
226 | -} |
227 | - |
228 | - |
229 | juju-log "$CHARM: Running hook $ARG0." |
230 | case $ARG0 in |
231 | "install") install_hook ;; |
232 | @@ -161,5 +181,4 @@ |
233 | "cluster-relation-changed") cluster_changed ;; |
234 | "cluster-relation-departed") cluster_changed ;; |
235 | "ha-relation-joined") ha_relation_joined ;; |
236 | - "ha-relation-changed") ha_relation_changed ;; |
237 | esac |
238 | |
239 | === modified file 'hooks/lib/openstack-common' |
240 | --- hooks/lib/openstack-common 2013-03-08 21:15:45 +0000 |
241 | +++ hooks/lib/openstack-common 2013-03-13 11:10:27 +0000 |
242 | @@ -321,18 +321,16 @@ |
243 | |
244 | HAPROXY_CFG=/etc/haproxy/haproxy.cfg |
245 | HAPROXY_DEFAULT=/etc/default/haproxy |
246 | - |
247 | ########################################################################## |
248 | # Description: Configures HAProxy services for Openstack API's |
249 | # Parameters: |
250 | -# Space delimited list of service:port combinations for which |
251 | +# Space delimited list of service:port:mode combinations for which |
252 | # haproxy service configuration should be generated for. The function |
253 | # assumes the name of the peer relation is 'cluster' and that every |
254 | # service unit in the peer relation is running the same services. |
255 | # |
256 | -# The HAProxy service will listen on port + 10000. |
257 | -# Example: |
258 | -# configure_haproxy cinder_api:12345 nova_api:9999 |
259 | +# Example |
260 | +# configure_haproxy cinder_api:8776:8756:tcp nova_api:8774:8764:http |
261 | ########################################################################## |
262 | configure_haproxy() { |
263 | local address=`unit-get private-address` |
264 | @@ -354,8 +352,8 @@ |
265 | retries 3 |
266 | timeout queue 1000 |
267 | timeout connect 1000 |
268 | - timeout client 10000 |
269 | - timeout server 10000 |
270 | + timeout client 30000 |
271 | + timeout server 30000 |
272 | |
273 | listen stats :8888 |
274 | mode http |
275 | @@ -368,14 +366,20 @@ |
276 | EOF |
277 | for service in $@; do |
278 | local service_name=$(echo $service | cut -d : -f 1) |
279 | - local api_listen_port=$(echo $service | cut -d : -f 2) |
280 | - local haproxy_listen_port=$(($api_listen_port + 10000)) |
281 | + local haproxy_listen_port=$(echo $service | cut -d : -f 2) |
282 | + local api_listen_port=$(echo $service | cut -d : -f 3) |
283 | + local mode=$(echo $service | cut -d : -f 4) |
284 | + juju-log "Adding haproxy configuration entry for $service "\ |
285 | + "($haproxy_listen_port -> $api_listen_port)" |
286 | cat >> $HAPROXY_CFG << EOF |
287 | listen $service_name 0.0.0.0:$haproxy_listen_port |
288 | balance roundrobin |
289 | - option tcplog |
290 | + mode $mode |
291 | + option ${mode}log |
292 | server $name $address:$api_listen_port check |
293 | EOF |
294 | + local r_id="" |
295 | + local unit="" |
296 | for r_id in `relation-ids cluster`; do |
297 | for unit in `relation-list -r $r_id`; do |
298 | local unit_name=${unit////-} |
299 | @@ -388,6 +392,7 @@ |
300 | done |
301 | done |
302 | echo "ENABLED=1" > $HAPROXY_DEFAULT |
303 | + service haproxy restart |
304 | } |
305 | |
306 | ########################################################################## |
307 | @@ -395,18 +400,20 @@ |
308 | # Returns: 0 if configured, 1 if not configured |
309 | ########################################################################## |
310 | is_clustered() { |
311 | + local r_id="" |
312 | + local unit="" |
313 | for r_id in $(relation-ids ha); do |
314 | if [ -n "$r_id" ]; then |
315 | for unit in $(relation-list -r $r_id); do |
316 | clustered=$(relation-get -r $r_id clustered $unit) |
317 | if [ -n "$clustered" ]; then |
318 | - echo "Unit is clustered" |
319 | + juju-log "Unit is haclustered" |
320 | return 0 |
321 | fi |
322 | done |
323 | fi |
324 | done |
325 | - echo "Unit is not clustered" |
326 | + juju-log "Unit is not haclustered" |
327 | return 1 |
328 | } |
329 | |
330 | @@ -415,6 +422,7 @@ |
331 | ########################################################################## |
332 | peer_units() { |
333 | local peers="" |
334 | + local r_id="" |
335 | for r_id in $(relation-ids cluster); do |
336 | peers="$peers $(relation-list -r $r_id)" |
337 | done |
338 | @@ -433,11 +441,11 @@ |
339 | echo "Comparing $JUJU_UNIT_NAME with peers: $peers" |
340 | local r_unit_no=$(echo $peer | cut -d / -f 2) |
341 | if (($r_unit_no<$l_unit_no)); then |
342 | - echo "Not oldest peer; deferring" |
343 | + juju-log "Not oldest peer; deferring" |
344 | return 1 |
345 | fi |
346 | done |
347 | - echo "Oldest peer; might take charge?" |
348 | + juju-log "Oldest peer; might take charge?" |
349 | return 0 |
350 | } |
351 | |
352 | @@ -451,13 +459,13 @@ |
353 | eligible_leader() { |
354 | if is_clustered; then |
355 | if ! is_leader $1; then |
356 | - echo 'Deferring action to CRM leader' |
357 | + juju-log 'Deferring action to CRM leader' |
358 | return 1 |
359 | fi |
360 | else |
361 | peers=$(peer_units) |
362 | if [ -n "$peers" ] && ! oldest_peer "$peers"; then |
363 | - echo 'Deferring action to oldest service unit.' |
364 | + juju-log 'Deferring action to oldest service unit.' |
365 | return 1 |
366 | fi |
367 | fi |
368 | @@ -469,14 +477,14 @@ |
369 | # Returns: 0 if peered, 1 if not peered |
370 | ########################################################################## |
371 | is_peered() { |
372 | - r_id=$(relation-ids cluster) |
373 | + local r_id=$(relation-ids cluster) |
374 | if [ -n "$r_id" ]; then |
375 | if [ -n "$(relation-list -r $r_id)" ]; then |
376 | - echo "Unit peered" |
377 | + juju-log "Unit peered" |
378 | return 0 |
379 | fi |
380 | fi |
381 | - echo "Unit not peered" |
382 | + juju-log "Unit not peered" |
383 | return 1 |
384 | } |
385 | |
386 | @@ -489,12 +497,207 @@ |
387 | hostname=`hostname` |
388 | if [ -x /usr/sbin/crm ]; then |
389 | if crm resource show $1 | grep -q $hostname; then |
390 | - echo "$hostname is cluster leader" |
391 | + juju-log "$hostname is cluster leader." |
392 | return 0 |
393 | fi |
394 | fi |
395 | - echo "$hostname is not cluster leader" |
396 | - return 1 |
397 | + juju-log "$hostname is not cluster leader." |
398 | + return 1 |
399 | +} |
400 | + |
401 | +########################################################################## |
402 | +# Description: Determines whether enough data has been provided in |
403 | +# configuration or relation data to configure HTTPS. |
404 | +# Parameters: None |
405 | +# Returns: 0 if HTTPS can be configured, 1 if not. |
406 | +########################################################################## |
407 | +https() { |
408 | + local r_id="" |
409 | + if [[ -n "$(config-get ssl_cert)" ]] && |
410 | + [[ -n "$(config-get ssl_key)" ]] ; then |
411 | + return 0 |
412 | + fi |
413 | + for r_id in $(relation-ids identity-service) ; do |
414 | + for unit in $(relation-list -r $r_id) ; do |
415 | + if [[ "$(relation-get -r $r_id https_keystone $unit)" == "True" ]] && |
416 | + [[ -n "$(relation-get -r $r_id ssl_cert $unit)" ]] && |
417 | + [[ -n "$(relation-get -r $r_id ssl_key $unit)" ]] && |
418 | + [[ -n "$(relation-get -r $r_id ca_cert $unit)" ]] ; then |
419 | + return 0 |
420 | + fi |
421 | + done |
422 | + done |
423 | + return 1 |
424 | +} |
425 | + |
426 | +########################################################################## |
427 | +# Description: For a given number of port mappings, configures apache2 |
428 | +# HTTPs local reverse proxying using certficates and keys provided in |
429 | +# either configuration data (preferred) or relation data. Assumes ports |
430 | +# are not in use (calling charm should ensure that). |
431 | +# Parameters: Variable number of proxy port mappings as |
432 | +# $internal:$external. |
433 | +# Returns: 0 if reverse proxy(s) have been configured, 0 if not. |
434 | +########################################################################## |
435 | +enable_https() { |
436 | + local port_maps="$@" |
437 | + local http_restart="" |
438 | + juju-log "Enabling HTTPS for port mappings: $port_maps." |
439 | + |
440 | + # allow overriding of keystone provided certs with those set manually |
441 | + # in config. |
442 | + local cert=$(config-get ssl_cert) |
443 | + local key=$(config-get ssl_key) |
444 | + local ca_cert="" |
445 | + if [[ -z "$cert" ]] || [[ -z "$key" ]] ; then |
446 | + juju-log "Inspecting identity-service relations for SSL certificate." |
447 | + local r_id="" |
448 | + cert="" |
449 | + key="" |
450 | + ca_cert="" |
451 | + for r_id in $(relation-ids identity-service) ; do |
452 | + for unit in $(relation-list -r $r_id) ; do |
453 | + [[ -z "$cert" ]] && cert="$(relation-get -r $r_id ssl_cert $unit)" |
454 | + [[ -z "$key" ]] && key="$(relation-get -r $r_id ssl_key $unit)" |
455 | + [[ -z "$ca_cert" ]] && ca_cert="$(relation-get -r $r_id ca_cert $unit)" |
456 | + done |
457 | + done |
458 | + [[ -n "$cert" ]] && cert=$(echo $cert | base64 -di) |
459 | + [[ -n "$key" ]] && key=$(echo $key | base64 -di) |
460 | + [[ -n "$ca_cert" ]] && ca_cert=$(echo $ca_cert | base64 -di) |
461 | + else |
462 | + juju-log "Using SSL certificate provided in service config." |
463 | + fi |
464 | + |
465 | + [[ -z "$cert" ]] || [[ -z "$key" ]] && |
466 | + juju-log "Expected but could not find SSL certificate data, not "\ |
467 | + "configuring HTTPS!" && return 1 |
468 | + |
469 | + apt-get -y install apache2 |
470 | + a2enmod ssl proxy proxy_http | grep -v "To activate the new configuration" && |
471 | + http_restart=1 |
472 | + |
473 | + mkdir -p /etc/apache2/ssl/$CHARM |
474 | + echo "$cert" >/etc/apache2/ssl/$CHARM/cert |
475 | + echo "$key" >/etc/apache2/ssl/$CHARM/key |
476 | + if [[ -n "$ca_cert" ]] ; then |
477 | + juju-log "Installing Keystone supplied CA cert." |
478 | + echo "$ca_cert" >/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt |
479 | + update-ca-certificates --fresh |
480 | + |
481 | + # XXX TODO: Find a better way of exporting this? |
482 | + if [[ "$CHARM" == "nova-cloud-controller" ]] ; then |
483 | + [[ -e /var/www/keystone_juju_ca_cert.crt ]] && |
484 | + rm -rf /var/www/keystone_juju_ca_cert.crt |
485 | + ln -s /usr/local/share/ca-certificates/keystone_juju_ca_cert.crt \ |
486 | + /var/www/keystone_juju_ca_cert.crt |
487 | + fi |
488 | + |
489 | + fi |
490 | + for port_map in $port_maps ; do |
491 | + local ext_port=$(echo $port_map | cut -d: -f1) |
492 | + local int_port=$(echo $port_map | cut -d: -f2) |
493 | + juju-log "Creating apache2 reverse proxy vhost for $port_map." |
494 | + cat >/etc/apache2/sites-available/${CHARM}_${ext_port} <<END |
495 | +Listen $ext_port |
496 | +NameVirtualHost *:$ext_port |
497 | +<VirtualHost *:$ext_port> |
498 | + ServerName $(unit-get private-address) |
499 | + SSLEngine on |
500 | + SSLCertificateFile /etc/apache2/ssl/$CHARM/cert |
501 | + SSLCertificateKeyFile /etc/apache2/ssl/$CHARM/key |
502 | + ProxyPass / http://localhost:$int_port/ |
503 | + ProxyPassReverse / http://localhost:$int_port/ |
504 | + ProxyPreserveHost on |
505 | +</VirtualHost> |
506 | +<Proxy *> |
507 | + Order deny,allow |
508 | + Allow from all |
509 | +</Proxy> |
510 | +<Location /> |
511 | + Order allow,deny |
512 | + Allow from all |
513 | +</Location> |
514 | +END |
515 | + a2ensite ${CHARM}_${ext_port} | grep -v "To activate the new configuration" && |
516 | + http_restart=1 |
517 | + done |
518 | + if [[ -n "$http_restart" ]] ; then |
519 | + service apache2 restart |
520 | + fi |
521 | +} |
522 | + |
523 | +########################################################################## |
524 | +# Description: Ensure HTTPS reverse proxying is disabled for given port |
525 | +# mappings. |
526 | +# Parameters: Variable number of proxy port mappings as |
527 | +# $internal:$external. |
528 | +# Returns: 0 if reverse proxy is not active for all portmaps, 1 on error. |
529 | +########################################################################## |
530 | +disable_https() { |
531 | + local port_maps="$@" |
532 | + local http_restart="" |
533 | + juju-log "Ensuring HTTPS disabled for $port_maps." |
534 | + ( [[ ! -d /etc/apache2 ]] || [[ ! -d /etc/apache2/ssl/$CHARM ]] ) && return 0 |
535 | + for port_map in $port_maps ; do |
536 | + local ext_port=$(echo $port_map | cut -d: -f1) |
537 | + local int_port=$(echo $port_map | cut -d: -f2) |
538 | + if [[ -e /etc/apache2/sites-available/${CHARM}_${ext_port} ]] ; then |
539 | + juju-log "Disabling HTTPS reverse proxy for $CHARM $port_map." |
540 | + a2dissite ${CHARM}_${ext_port} | grep -v "To activate the new configuration" && |
541 | + http_restart=1 |
542 | + fi |
543 | + done |
544 | + if [[ -n "$http_restart" ]] ; then |
545 | + service apache2 restart |
546 | + fi |
547 | +} |
548 | + |
549 | + |
550 | +########################################################################## |
551 | +# Description: Ensures HTTPS is either enabled or disabled for given port |
552 | +# mapping. |
553 | +# Parameters: Variable number of proxy port mappings as |
554 | +# $internal:$external. |
555 | +# Returns: 0 if HTTPS reverse proxy is in place, 1 if it is not. |
556 | +########################################################################## |
557 | +setup_https() { |
558 | + # configure https via apache reverse proxying either |
559 | + # using certs provided by config or keystone. |
560 | + [[ -z "$CHARM" ]] && |
561 | + error_out "setup_https(): CHARM not set." |
562 | + if ! https ; then |
563 | + disable_https $@ |
564 | + else |
565 | + enable_https $@ |
566 | + fi |
567 | +} |
568 | + |
569 | +########################################################################## |
570 | +# Description: Determine correct API server listening port based on |
571 | +# existence of HTTPS reverse proxy and/or haproxy. |
572 | +# Paremeters: The standard public port for given service. |
573 | +# Returns: The correct listening port for API service. |
574 | +########################################################################## |
575 | +determine_api_port() { |
576 | + local public_port="$1" |
577 | + local i=0 |
578 | + ( [[ -n "$(peer_units)" ]] || is_clustered >/dev/null 2>&1 ) && i=$[$i + 1] |
579 | + https >/dev/null 2>&1 && i=$[$i + 1] |
580 | + echo $[$public_port - $[$i * 10]] |
581 | +} |
582 | + |
583 | +########################################################################## |
584 | +# Description: Determine correct proxy listening port based on public IP + |
585 | +# existence of HTTPS reverse proxy. |
586 | +# Paremeters: The standard public port for given service. |
587 | +# Returns: The correct listening port for haproxy service public address. |
588 | +########################################################################## |
589 | +determine_haproxy_port() { |
590 | + local public_port="$1" |
591 | + local i=0 |
592 | + https >/dev/null 2>&1 && i=$[$i + 1] |
593 | + echo $[$public_port - $[$i * 10]] |
594 | } |
595 | |
596 | ########################################################################## |
This update adds a few features:
1) HTTPS configuration options ssl_cert and ssl_key
HTTPS is on by default using snakeoil certs; this allows a user provided cert to be used.
2) Config options to
a) enable online compression (useful for use with trunk)
b) enable debugging
3) HA + HTTPS support
A little different in that Apache listens on 70/433 and haproxy fronts apache in full, using the tcp option for the SSL connections. This provides end-to-end SSL security.
General - I made this charm a little less bumpy by configuring the haproxy instance up-front; the cluster hook simply adds new nodes to the haproxy config using reload.