~niedbalski/charms/trusty/swift-proxy/fix-lp-1308557

« back to all changes in this revision

Viewing changes to hooks/lib/apache_utils.py

  • Committer: James Page
  • Date: 2013-06-03 16:50:28 UTC
  • mfrom: (35.1.18 swift-proxy)
  • Revision ID: james.page@canonical.com-20130603165028-f9ya6llay65w3t9e
* Python rewrite

* Support for manually assigned storage zones

* Support for high availability via hacluster subordinate.

* Adds Grizzly compatibility.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# Copyright 2012 Canonical Ltd.
 
3
#
 
4
# Authors:
 
5
#  James Page <james.page@ubuntu.com>
 
6
#
 
7
 
 
8
from lib.utils import (
 
9
    relation_ids,
 
10
    relation_list,
 
11
    relation_get,
 
12
    render_template,
 
13
    juju_log,
 
14
    config_get,
 
15
    install,
 
16
    get_host_ip,
 
17
    restart
 
18
    )
 
19
from lib.cluster_utils import https
 
20
 
 
21
import os
 
22
import subprocess
 
23
from base64 import b64decode
 
24
 
 
25
APACHE_SITE_DIR = "/etc/apache2/sites-available"
 
26
SITE_TEMPLATE = "apache2_site.tmpl"
 
27
RELOAD_CHECK = "To activate the new configuration"
 
28
 
 
29
 
 
30
def get_cert():
 
31
    cert = config_get('ssl_cert')
 
32
    key = config_get('ssl_key')
 
33
    if not (cert and key):
 
34
        juju_log('INFO',
 
35
                 "Inspecting identity-service relations for SSL certificate.")
 
36
        cert = key = None
 
37
        for r_id in relation_ids('identity-service'):
 
38
            for unit in relation_list(r_id):
 
39
                if not cert:
 
40
                    cert = relation_get('ssl_cert',
 
41
                                        rid=r_id, unit=unit)
 
42
                if not key:
 
43
                    key = relation_get('ssl_key',
 
44
                                       rid=r_id, unit=unit)
 
45
    return (cert, key)
 
46
 
 
47
 
 
48
def get_ca_cert():
 
49
    ca_cert = None
 
50
    juju_log('INFO',
 
51
             "Inspecting identity-service relations for CA SSL certificate.")
 
52
    for r_id in relation_ids('identity-service'):
 
53
        for unit in relation_list(r_id):
 
54
            if not ca_cert:
 
55
                ca_cert = relation_get('ca_cert',
 
56
                                       rid=r_id, unit=unit)
 
57
    return ca_cert
 
58
 
 
59
 
 
60
def install_ca_cert(ca_cert):
 
61
    if ca_cert:
 
62
        with open('/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt',
 
63
                  'w') as crt:
 
64
            crt.write(ca_cert)
 
65
        subprocess.check_call(['update-ca-certificates', '--fresh'])
 
66
 
 
67
 
 
68
def enable_https(port_maps, namespace, cert, key, ca_cert=None):
 
69
    '''
 
70
    For a given number of port mappings, configures apache2
 
71
    HTTPs local reverse proxying using certficates and keys provided in
 
72
    either configuration data (preferred) or relation data.  Assumes ports
 
73
    are not in use (calling charm should ensure that).
 
74
 
 
75
    port_maps: dict: external to internal port mappings
 
76
    namespace: str: name of charm
 
77
    '''
 
78
    def _write_if_changed(path, new_content):
 
79
        content = None
 
80
        if os.path.exists(path):
 
81
            with open(path, 'r') as f:
 
82
                content = f.read().strip()
 
83
        if content != new_content:
 
84
            with open(path, 'w') as f:
 
85
                f.write(new_content)
 
86
            return True
 
87
        else:
 
88
            return False
 
89
 
 
90
    juju_log('INFO', "Enabling HTTPS for port mappings: {}".format(port_maps))
 
91
    http_restart = False
 
92
 
 
93
    if cert:
 
94
        cert = b64decode(cert)
 
95
    if key:
 
96
        key = b64decode(key)
 
97
    if ca_cert:
 
98
        ca_cert = b64decode(ca_cert)
 
99
 
 
100
    if not cert and not key:
 
101
        juju_log('ERROR',
 
102
                 "Expected but could not find SSL certificate data, not "
 
103
                 "configuring HTTPS!")
 
104
        return False
 
105
 
 
106
    install('apache2')
 
107
    if RELOAD_CHECK in subprocess.check_output(['a2enmod', 'ssl',
 
108
                                                'proxy', 'proxy_http']):
 
109
        http_restart = True
 
110
 
 
111
    ssl_dir = os.path.join('/etc/apache2/ssl', namespace)
 
112
    if not os.path.exists(ssl_dir):
 
113
        os.makedirs(ssl_dir)
 
114
 
 
115
    if (_write_if_changed(os.path.join(ssl_dir, 'cert'), cert)):
 
116
        http_restart = True
 
117
    if (_write_if_changed(os.path.join(ssl_dir, 'key'), key)):
 
118
        http_restart = True
 
119
    os.chmod(os.path.join(ssl_dir, 'key'), 0600)
 
120
 
 
121
    install_ca_cert(ca_cert)
 
122
 
 
123
    sites_dir = '/etc/apache2/sites-available'
 
124
    for ext_port, int_port in port_maps.items():
 
125
        juju_log('INFO',
 
126
                 'Creating apache2 reverse proxy vhost'
 
127
                 ' for {}:{}'.format(ext_port,
 
128
                                     int_port))
 
129
        site = "{}_{}".format(namespace, ext_port)
 
130
        site_path = os.path.join(sites_dir, site)
 
131
        with open(site_path, 'w') as fsite:
 
132
            context = {
 
133
                "ext": ext_port,
 
134
                "int": int_port,
 
135
                "namespace": namespace,
 
136
                "private_address": get_host_ip()
 
137
                }
 
138
            fsite.write(render_template(SITE_TEMPLATE,
 
139
                                        context))
 
140
 
 
141
        if RELOAD_CHECK in subprocess.check_output(['a2ensite', site]):
 
142
            http_restart = True
 
143
 
 
144
    if http_restart:
 
145
        restart('apache2')
 
146
 
 
147
    return True
 
148
 
 
149
 
 
150
def disable_https(port_maps, namespace):
 
151
    '''
 
152
    Ensure HTTPS reverse proxying is disables for given port mappings
 
153
 
 
154
    port_maps: dict: of ext -> int port mappings
 
155
    namespace: str: name of chamr
 
156
    '''
 
157
    juju_log('INFO', 'Ensuring HTTPS disabled for {}'.format(port_maps))
 
158
 
 
159
    if (not os.path.exists('/etc/apache2') or
 
160
        not os.path.exists(os.path.join('/etc/apache2/ssl', namespace))):
 
161
        return
 
162
 
 
163
    http_restart = False
 
164
    for ext_port in port_maps.keys():
 
165
        if os.path.exists(os.path.join(APACHE_SITE_DIR,
 
166
                                       "{}_{}".format(namespace,
 
167
                                                      ext_port))):
 
168
            juju_log('INFO',
 
169
                     "Disabling HTTPS reverse proxy"
 
170
                     " for {} {}.".format(namespace,
 
171
                                          ext_port))
 
172
            if (RELOAD_CHECK in
 
173
                subprocess.check_output(['a2dissite',
 
174
                                         '{}_{}'.format(namespace,
 
175
                                                        ext_port)])):
 
176
                http_restart = True
 
177
 
 
178
    if http_restart:
 
179
        restart(['apache2'])
 
180
 
 
181
 
 
182
def setup_https(port_maps, namespace, cert, key, ca_cert=None):
 
183
    '''
 
184
    Ensures HTTPS is either enabled or disabled for given port
 
185
    mapping.
 
186
 
 
187
    port_maps: dict: of ext -> int port mappings
 
188
    namespace: str: name of charm
 
189
    '''
 
190
    if not https:
 
191
        disable_https(port_maps, namespace)
 
192
    else:
 
193
        enable_https(port_maps, namespace, cert, key, ca_cert)