Merge lp:~dpb/charms/precise/apache2/trunk into lp:charms/apache2
- Precise Pangolin (12.04)
- trunk
- Merge into trunk
Proposed by
David Britton
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Brandon Holtsclaw | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 35 | ||||
Proposed branch: | lp:~dpb/charms/precise/apache2/trunk | ||||
Merge into: | lp:charms/apache2 | ||||
Diff against target: |
535 lines (+233/-78) 6 files modified
.bzrignore (+2/-0) README.md (+74/-45) config.yaml (+36/-5) hooks/hooks.py (+66/-27) revision (+1/-1) scripts/gen-selfsigned-cert (+54/-0) |
||||
To merge this branch: | bzr merge lp:~dpb/charms/precise/apache2/trunk | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Brandon Holtsclaw (community) | Approve | ||
Review via email: mp+146242@code.launchpad.net |
Commit message
Description of the change
- Fill out README around the reverseproxy use case.
- some minor code-cleanup in hooks.py
- strip disallowed characters from the jinja2 template data
- Add in ssl_key, ssl_cert to allow passing in base64 encoded versions of these files
- Add in ability to generate a self-signed certificate for testing
- Default servername to the public-address of the unit (which juju in turn falls back to private-address)
To post a comment you must log in.
- 35. By Brandon Holtsclaw
-
Merge of MP# 146242 - davidpbritton
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2012-10-26 02:41:40 +0000 |
3 | +++ .bzrignore 2013-02-01 23:20:27 +0000 |
4 | @@ -1,2 +1,4 @@ |
5 | revision |
6 | basenode/ |
7 | +*.crt |
8 | +*.key |
9 | |
10 | === renamed file 'README' => 'README.md' |
11 | --- README 2013-01-28 19:09:50 +0000 |
12 | +++ README.md 2013-02-01 23:20:27 +0000 |
13 | @@ -1,5 +1,5 @@ |
14 | Juju charm apache |
15 | -===================== |
16 | +================= |
17 | |
18 | The Apache Software Foundation's goal is to build a secure, efficient |
19 | and extensible HTTP server as standards-compliant open source |
20 | @@ -9,46 +9,60 @@ |
21 | filtering, many flexible authentication schemes, and more. |
22 | |
23 | How to deploy the charm |
24 | --------------------------- |
25 | -juju deploy apache2 |
26 | -juju set apache2 "vhost_http_template=$(base64 < vhost.tmpl)" |
27 | -and or |
28 | -juju set apache2 "vhost_https_template=$(base64 < vhost.tmpl)" |
29 | +----------------------- |
30 | + juju deploy apache2 |
31 | + juju set apache2 "vhost_http_template=$(base64 < vhost.tmpl)" |
32 | + # and / or |
33 | + juju set apache2 "vhost_https_template=$(base64 < vhost.tmpl)" |
34 | + juju add-relation apache2:reverseproxy haproxy:website |
35 | |
36 | Vhost templates |
37 | --------------------------- |
38 | +--------------- |
39 | The charm expects a jinja2 template to be passed in. The variables in |
40 | the template should relate to the services that apache will be proxying |
41 | -to (obviously no variables need to be specified if no proxying is needed). |
42 | +-- obviously no variables need to be specified if no proxying is needed. |
43 | |
44 | The charm will create the service variable, with the unit_name, when |
45 | the reverseproxy relationship is joined and present this to the template |
46 | -at which point the vhost will be generated from the template again. |
47 | +at which point the vhost will be generated from the template again. |
48 | +All config settings are also available to the template. |
49 | |
50 | -For example to access squid then the {{{ squid }}} variable should be used. |
51 | +For example to access squid then the {{ squid }} variable should be used. |
52 | This will be populated with the hostname:port of the squid service. The |
53 | individual hostname and port can also be accessed via squid_hostname and |
54 | squid_port. |
55 | -Note: If an alias is used when deploying a charm then the alias name needs |
56 | - to be used. |
57 | - |
58 | -If the joining charm also present an all_services variable which contains |
59 | -a list of services it provides in yaml format then variables for each |
60 | -service will be created of the format unitname_stanza. For example if |
61 | -haproxy contains stanzas named gunicorn and solr these can be accessed |
62 | -via {{{ haproxy_gunicorn }}} and {{ haproxy_solr }}}. |
63 | -Note: Currently the haproxy does not seem to be generating individual stanzas |
64 | - correctly |
65 | + |
66 | +Note: The service name should be used, not the charm name. If deploying |
67 | + a charm with a different service name, use that instaed. |
68 | + |
69 | +The joining charm may also set an all_services variable which contains |
70 | +a list of services it provides in yaml format (list of associative arrays): |
71 | + |
72 | + # ... in haproxy charm, website-relation-joined |
73 | + relation-set all_services=" |
74 | + - {service_name: gunicorn, service_port: 80} |
75 | + - {service_name: solr, service_port: 8080} |
76 | + - {service_name: my-webapp, service_port: 9090} |
77 | + " |
78 | + |
79 | +then variables for each service would be available to the jinja2 template |
80 | +in <juju_service_name>_<sub_service_name>. In our example above |
81 | +haproxy contains stanzas named gunicorn, solr and my-webapp. These are |
82 | +accessed as {{ haproxy_gunicorn }}, {{ haproxy_solr }} and |
83 | +{{ haproxy_mywebapp }} respectively. If any unsupported characters are in |
84 | +your juju service name or the service names exposed through "all_services", |
85 | +they will be stripped. |
86 | |
87 | For example a vhost that will pass all traffic on to an haproxy instance: |
88 | -<VirtualHost *:80> |
89 | - ServerName radiotiptop.org.uk |
90 | - |
91 | - CustomLog /var/log/apache2/radiotiptop-access.log combined |
92 | - ErrorLog /var/log/apache2/radiotiptop-error.log |
93 | - |
94 | + |
95 | + <VirtualHost *:80> |
96 | + ServerName radiotiptop.org.uk |
97 | + |
98 | + CustomLog /var/log/apache2/radiotiptop-access.log combined |
99 | + ErrorLog /var/log/apache2/radiotiptop-error.log |
100 | + |
101 | DocumentRoot /srv/radiotiptop/www/root |
102 | - |
103 | + |
104 | ProxyRequests off |
105 | <Proxy *> |
106 | Order Allow,Deny |
107 | @@ -58,33 +72,48 @@ |
108 | ErrorDocument 502 /offline.html |
109 | ErrorDocument 503 /offline.html |
110 | </Proxy> |
111 | - |
112 | + |
113 | ProxyPreserveHost off |
114 | ProxyPassReverse / http://{{ haproxy_gunicorn }}/ |
115 | - |
116 | + |
117 | RewriteEngine on |
118 | - |
119 | + |
120 | RewriteRule ^/$ /index.html [L] |
121 | RewriteRule ^/(.*)$ http://{{ haproxy_gunicorn }}/$1 [P,L] |
122 | - |
123 | -</VirtualHost> |
124 | + </VirtualHost> |
125 | |
126 | Certs, keys and chains |
127 | --------------------------- |
128 | +---------------------- |
129 | ssl_keylocation, ssl_certlocation and ssl_chainlocation are file names in the |
130 | -data directory. |
131 | +charm /data directory. If found, they will be copied as follows: |
132 | + |
133 | + - /etc/ssl/private/<ssl_keylocation> |
134 | + - /etc/ssl/certs/<ssl_certlocation> |
135 | + - /etc/ssl/certs/<ssl_chainlocation> |
136 | + |
137 | +ssl_key and ssl_cert can also be specified which are are assumed to be |
138 | +base64 encoded. If specified, they will be written to appropriate directories |
139 | +given the values in ssl_keylocation and ssl_certlocation as listed above. |
140 | + |
141 | +ssl_cert may also be set to SELFSIGNED, which will generate a certificate. |
142 | +This, of course, is mostly useful for testing and staging purposes. The |
143 | +generated certifcate/key will be placed according to ssl_certlocation and |
144 | +ssl_keylocation as listed above. |
145 | |
146 | {enable,disable}_modules |
147 | --------------------------- |
148 | -Lists of modules to be enabled or disabled. If a module to be enabled cannot be found |
149 | -then the charm will attempt to install it. |
150 | - |
151 | +------------------------ |
152 | +Space separated list of modules to be enabled or disabled. If a module to |
153 | +be enabled cannot be found then the charm will attempt to install it. |
154 | |
155 | TODO: |
156 | -* Method to deliver site content. This maybe by converting the charm to a subordinate |
157 | - and making it the master charms problem |
158 | -* Delivery of SSL key. Implement secure method for delivering key. |
159 | -* Tuning. No tuning options are present. Convert apache2.conf to a template and expose |
160 | - config options |
161 | -* Testing. I have only tested the relationship setup with 1 haproxy instance. Needs testing against |
162 | - multiple instances |
163 | +----- |
164 | + |
165 | + * Document the use of balancer, nrpe, logging and website-cache |
166 | + * Method to deliver site content. This maybe by converting the charm to a |
167 | + subordinate and making it the master charms problem |
168 | + * Implement secure method for delivering key. Juju will likely need to provide |
169 | + this. |
170 | + * Tuning. No tuning options are present. Convert apache2.conf to a template |
171 | + and expose config options |
172 | + * Testing. I have only tested the relationship setup with 1 haproxy instance. |
173 | + Needs testing against multiple instances |
174 | |
175 | === modified file 'config.yaml' |
176 | --- config.yaml 2013-02-01 15:56:52 +0000 |
177 | +++ config.yaml 2013-02-01 23:20:27 +0000 |
178 | @@ -1,8 +1,8 @@ |
179 | options: |
180 | servername: |
181 | type: string |
182 | - default: 'myvhost' |
183 | - description: ServerName for vhost |
184 | + default: '' |
185 | + description: ServerName for vhost, defaults to the units public-address |
186 | vhost_http_template: |
187 | type: string |
188 | default: '' |
189 | @@ -35,15 +35,28 @@ |
190 | ssl_keylocation: |
191 | type: string |
192 | default: '' |
193 | - description: Location of ssl key |
194 | + description: | |
195 | + Name and location of ssl keyfile in charm/data directory. |
196 | + If not found, will ignore. Basename of this file will be used |
197 | + as the basename of the key rooted at /etc/ssl/private. Can |
198 | + be used in conjuntion with the ssl_key parameter to specify |
199 | + the key as a configuration setting. |
200 | ssl_certlocation: |
201 | type: string |
202 | default: '' |
203 | - description: Location of ssl cert |
204 | + description: | |
205 | + Name and location of ssl certificate in charm/data directory. |
206 | + If not found, will ignore. Basename of this file will be used |
207 | + as the basename of the cert rooted at /etc/ssl/certs. Can |
208 | + be used in conjunction with the ssl_cert parameter to specify |
209 | + the cert as a configuration setting. |
210 | ssl_chainlocation: |
211 | type: string |
212 | default: '' |
213 | - description: Location of ssl chain file |
214 | + description: | |
215 | + Name and location of the ssl chain file. Basename of this file |
216 | + will be used as the basename of the chain file rooted at |
217 | + /etc/ssl/certs. |
218 | lb_balancer_timeout: |
219 | type: int |
220 | default: 60 |
221 | @@ -108,6 +121,24 @@ |
222 | description: > |
223 | Use daily extension like YYYMMDD instead of simply adding a number |
224 | default: True |
225 | + use_rsyslog: |
226 | + type: boolean |
227 | + description: >- |
228 | + Change logging behaviour to log both access and error logs via rsyslog |
229 | + default: False |
230 | + ssl_cert: |
231 | + type: string |
232 | + description: | |
233 | + base64 encoded server certificate. If the keyword 'SELFSIGNED' |
234 | + is used, the certificate and key will be autogenerated as |
235 | + self-signed. |
236 | + default: '' |
237 | + ssl_key: |
238 | + type: string |
239 | + description: | |
240 | + base64 encoded server certificate key. If ssl_cert is |
241 | + specified as SELFSIGNED, this will be ignored. |
242 | + default: '' |
243 | server_tokens: |
244 | type: string |
245 | description: Security setting. Set to one of Full OS Minimal Minor Major Prod |
246 | |
247 | === modified file 'hooks/hooks.py' |
248 | --- hooks/hooks.py 2013-02-01 15:56:52 +0000 |
249 | +++ hooks/hooks.py 2013-02-01 23:20:27 +0000 |
250 | @@ -41,6 +41,12 @@ |
251 | |
252 | |
253 | #------------------------------------------------------------------------------ |
254 | +# juju_log: Convenience wrapper around juju-log |
255 | +#------------------------------------------------------------------------------ |
256 | +def juju_log(msg="MARK"): |
257 | + subprocess.call(['juju-log', str(msg)]) |
258 | + |
259 | +#------------------------------------------------------------------------------ |
260 | # open_port: Convenience function to open a port in juju to |
261 | # expose a service |
262 | #------------------------------------------------------------------------------ |
263 | @@ -75,8 +81,12 @@ |
264 | config_cmd_line.append(scope) |
265 | config_cmd_line.append('--format=json') |
266 | config_data = json.loads(subprocess.check_output(config_cmd_line)) |
267 | + if not config_data["servername"]: |
268 | + config_data["servername"] = run( |
269 | + ["unit-get", "public-address"]).rstrip() |
270 | + |
271 | except Exception, e: |
272 | - subprocess.call(['juju-log', str(e)]) |
273 | + juju_log(e) |
274 | config_data = None |
275 | finally: |
276 | return(config_data) |
277 | @@ -101,7 +111,7 @@ |
278 | if unit_name is not None: |
279 | relation_cmd_line.append('-') |
280 | relation_cmd_line.append(unit_name) |
281 | - subprocess.call(['juju-log', 'Calling: %s' % relation_cmd_line]) |
282 | + juju_log('Calling: %s' % relation_cmd_line) |
283 | relation_data = json.loads(subprocess.check_output(relation_cmd_line)) |
284 | except Exception: |
285 | relation_data = None |
286 | @@ -114,7 +124,7 @@ |
287 | relation_cmd_line = ['relation-ids', '--format=json'] |
288 | if relation_name is not None: |
289 | relation_cmd_line.append(relation_name) |
290 | - subprocess.call(['juju-log', 'Calling: %s' % relation_cmd_line]) |
291 | + juju_log('Calling: %s' % relation_cmd_line) |
292 | relation_ids = json.loads(subprocess.check_output(relation_cmd_line)) |
293 | except Exception: |
294 | relation_ids = None |
295 | @@ -127,7 +137,7 @@ |
296 | relation_cmd_line = ['relation-list', '--format=json'] |
297 | if relation_id is not None: |
298 | relation_cmd_line.extend(('-r', relation_id)) |
299 | - subprocess.call(['juju-log', 'Calling: %s' % relation_cmd_line]) |
300 | + juju_log('Calling: %s' % relation_cmd_line) |
301 | relations = json.loads(subprocess.check_output(relation_cmd_line)) |
302 | except Exception: |
303 | relations = None |
304 | @@ -209,15 +219,12 @@ |
305 | if module is None: |
306 | return(True) |
307 | if os.path.exists("/etc/apache2/mods-enabled/%s.load" % (module)): |
308 | - subprocess.call(['juju-log', "Module already loaded"]) |
309 | + juju_log("Module already loaded: %s" % module) |
310 | return(True) |
311 | if not os.path.exists("/etc/apache2/mods-available/%s.load" % (module)): |
312 | retVal = apt_get_install("libapache2-mod-%s" % (module)) |
313 | if retVal != 0: |
314 | - subprocess.call( |
315 | - ['juju-log', |
316 | - "Installing module %s failed" % (module) |
317 | - ]) |
318 | + juju_log("Installing module %s failed" % module) |
319 | return(False) |
320 | retVal = subprocess.call(['/usr/sbin/a2enmod', module]) |
321 | if retVal != 0: |
322 | @@ -231,7 +238,7 @@ |
323 | if module is None: |
324 | return(True) |
325 | if not os.path.exists("/etc/apache2/mods-enabled/%s.load" % (module)): |
326 | - subprocess.call(['juju-log', "Module already disabled"]) |
327 | + juju_log("Module already disabled: %s" % module) |
328 | return(True) |
329 | retVal = subprocess.call(['/usr/sbin/a2dismod', module]) |
330 | if retVal != 0: |
331 | @@ -271,8 +278,14 @@ |
332 | for unit_name in relation_data.keys(): |
333 | if 'port' not in relation_data[unit_name]: |
334 | return reverseproxy_data |
335 | + |
336 | + # unit_name: <service-name>-<unit_number> |
337 | + # jinja2 templates require python-type variables, remove all characters |
338 | + # that do not comply |
339 | unit_type = re.sub(r'(.*)-[0-9]*', r'\1', unit_name) |
340 | - subprocess.call(['juju-log', 'unit_type: %s' % unit_type]) |
341 | + unit_type = re.sub('[^a-zA-Z0-9_]*', '', unit_type) |
342 | + juju_log('unit_type: %s' % unit_type) |
343 | + |
344 | host = relation_data[unit_name]['private-address'] |
345 | for config_setting in relation_data[unit_name].keys(): |
346 | config_key = '%s_%s' % (unit_type, config_setting) |
347 | @@ -293,7 +306,7 @@ |
348 | relation_data = all_relation_data_get(relation_name='balancer') |
349 | config_data = config_get() |
350 | if relation_data is None or len(relation_data) == 0: |
351 | - subprocess.call(['juju-log', 'No relation data exiting']) |
352 | + juju_log('No relation data exiting') |
353 | return |
354 | unit_dict = {} |
355 | for unit_name in relation_data.keys(): |
356 | @@ -320,6 +333,7 @@ |
357 | } |
358 | template = template_env.get_template( |
359 | 'balancer.template').render(templ_vars) |
360 | + juju_log("Writing file: %s with data: %s" % (balancer_host_file, templ_vars)) |
361 | with open(balancer_host_file, 'w') as balancer_config: |
362 | balancer_config.write(str(template)) |
363 | |
364 | @@ -327,13 +341,13 @@ |
365 | def update_nrpe_checks(): |
366 | config_data = config_get() |
367 | if 'nagios_check_http_params' not in config_data or len(config_data['nagios_check_http_params']) == 0: |
368 | - subprocess.call(['juju-log', "No vhost check data, exiting"]) |
369 | + juju_log("No nrpe configuration, skipping") |
370 | return |
371 | try: |
372 | nagios_uid = pwd.getpwnam('nagios').pw_uid |
373 | nagios_gid = grp.getgrnam('nagios').gr_gid |
374 | except: |
375 | - subprocess.call(['juju-log', "Nagios user not setup, exiting"]) |
376 | + juju_log("Nagios user not setup, exiting") |
377 | return |
378 | |
379 | unit_name = os.environ['JUJU_UNIT_NAME'].replace('/', '-') |
380 | @@ -347,8 +361,7 @@ |
381 | os.mkdir(nagios_logdir) |
382 | os.chown(nagios_logdir, nagios_uid, nagios_gid) |
383 | if not os.path.exists(nagios_exportdir): |
384 | - subprocess.call(['juju-log', 'Exiting as %s is not accessible' |
385 | - % (nagios_exportdir)]) |
386 | + juju_log('Exiting as %s is not accessible' % nagios_exportdir) |
387 | return |
388 | for f in os.listdir(nagios_exportdir): |
389 | if re.search('.*check_vhost.cfg', f): |
390 | @@ -454,6 +467,7 @@ |
391 | ports = {'http': 80, 'https': 443} |
392 | for proto in ports.keys(): |
393 | template_var = 'vhost_%s_template' % (proto) |
394 | + template_data = dict(config_data.items() + relationship_data.items()) |
395 | close_port(ports[proto]) |
396 | if template_var in config_data: |
397 | vhost_name = '%s_%s' % (config_data['servername'], proto) |
398 | @@ -461,34 +475,59 @@ |
399 | from jinja2 import Template |
400 | template = Template( |
401 | str(base64.b64decode(config_data[template_var]))) |
402 | + juju_log("Writing file: %s with data: %s" % (vhost_file, template_data)) |
403 | with open(vhost_file, 'w') as vhost: |
404 | - vhost.write( |
405 | - str(template.render( |
406 | - dict(config_data.items() + |
407 | - relationship_data.items())))) |
408 | + vhost.write(str(template.render(template_data))) |
409 | open_port(ports[proto]) |
410 | subprocess.call(['/usr/sbin/a2ensite', vhost_name]) |
411 | |
412 | + cert_file = None |
413 | if config_data['ssl_certlocation']: |
414 | + source = os.path.join( |
415 | + os.environ['CHARM_DIR'], 'data', config_data['ssl_certlocation']) |
416 | cert_file = '/etc/ssl/certs/%s' % \ |
417 | (config_data['ssl_certlocation'].rpartition('/')[2]) |
418 | - shutil.copy(os.path.join(os.environ['CHARM_DIR'], 'data', |
419 | - config_data['ssl_certlocation']), cert_file) |
420 | + if os.path.exists(source): |
421 | + shutil.copy(source, cert_file) |
422 | + else: |
423 | + juju_log("Certificate not found, ignoring: %s" % source) |
424 | |
425 | + chain_file = None |
426 | if config_data['ssl_chainlocation']: |
427 | chain_file = '/etc/ssl/certs/%s' % \ |
428 | (config_data['ssl_chainlocation'].rpartition('/')[2]) |
429 | shutil.copy(os.path.join(os.environ['CHARM_DIR'], 'data', |
430 | config_data['ssl_chainlocation']), chain_file) |
431 | |
432 | + key_file = None |
433 | if config_data['ssl_keylocation']: |
434 | + source = os.path.join( |
435 | + os.environ['CHARM_DIR'], 'data', config_data['ssl_keylocation']) |
436 | key_file = '/etc/ssl/private/%s' % \ |
437 | (config_data['ssl_keylocation'].rpartition('/')[2]) |
438 | - shutil.copy(os.path.join(os.environ['CHARM_DIR'], 'data', |
439 | - config_data['ssl_keylocation']), key_file) |
440 | - os.chmod(key_file, 0440) |
441 | - os.chown(key_file, pwd.getpwnam('root').pw_uid, |
442 | - grp.getgrnam('ssl-cert').gr_gid) |
443 | + if os.path.exists(source): |
444 | + shutil.copy(source, key_file) |
445 | + os.chmod(key_file, 0440) |
446 | + os.chown(key_file, |
447 | + pwd.getpwnam('root').pw_uid, grp.getgrnam('ssl-cert').gr_gid) |
448 | + else: |
449 | + juju_log("Key file not found, ignoring: %s" % source) |
450 | + |
451 | + if config_data['ssl_cert'] and cert_file is not None: |
452 | + if config_data['ssl_cert'] == "SELFSIGNED" and key_file is not None: |
453 | + config_data['ssl_key'] = "" |
454 | + gen_cert = os.path.join(os.environ['CHARM_DIR'], 'scripts', |
455 | + 'gen-selfsigned-cert') |
456 | + run([gen_cert, key_file, cert_file]) |
457 | + else: |
458 | + juju_log("Writing cert from config ssl_cert: %s" % cert_file) |
459 | + with open(cert_file, 'w') as f: |
460 | + f.write(str(base64.b64decode(config_data['ssl_cert']))) |
461 | + |
462 | + if config_data['ssl_key'] and key_file is not None: |
463 | + juju_log("Writing key from config ssl_key: %s" % key_file) |
464 | + with open(key_file, 'w') as f: |
465 | + f.write(str(base64.b64decode(config_data['ssl_key']))) |
466 | |
467 | # Disable the default website because we don't want people to see the |
468 | # "It works!" page on production services and remove the |
469 | |
470 | === modified file 'revision' |
471 | --- revision 2013-01-17 16:32:08 +0000 |
472 | +++ revision 2013-02-01 23:20:27 +0000 |
473 | @@ -1,1 +1,1 @@ |
474 | -1 |
475 | +2 |
476 | |
477 | === added directory 'scripts' |
478 | === added file 'scripts/gen-selfsigned-cert' |
479 | --- scripts/gen-selfsigned-cert 1970-01-01 00:00:00 +0000 |
480 | +++ scripts/gen-selfsigned-cert 2013-02-01 23:20:27 +0000 |
481 | @@ -0,0 +1,54 @@ |
482 | +#!/bin/bash |
483 | +# |
484 | +# Simple helper to generate a certificate |
485 | +# $1 = key location |
486 | +# $2 = certificate location |
487 | +# $3 = public-address (will use unit-get if unspecified) |
488 | +# $4 = private-address (will use unit-get if unspecified) |
489 | + |
490 | +KEY_FILE=${1:-key} |
491 | +CERT_FILE=${2:-cert} |
492 | +PUBLIC=${3:-$(unit-get public-address)} |
493 | +PRIVATE=${4:-$(unit-get private-address)} |
494 | +juju-log "network data: $PUBLIC $PRIVATE" |
495 | +juju-log "Generating cert: $CERT_FILE / key: $KEY_FILE" |
496 | + |
497 | +gen_certificate() { |
498 | + CN=$PUBLIC |
499 | + tmpfile=$(mktemp /tmp/XXXXXX) || exit 1 |
500 | + cat > $tmpfile <<EOF |
501 | +RANDFILE = /dev/urandom |
502 | + |
503 | +[ req ] |
504 | +default_bits = 1024 |
505 | +default_keyfile = privkey.pem |
506 | +distinguished_name = req_distinguished_name |
507 | +prompt = no |
508 | +policy = policy_anything |
509 | +x509_extensions = v3_ca |
510 | + |
511 | +[ req_distinguished_name ] |
512 | +commonName = $CN |
513 | + |
514 | +[ v3_ca ] |
515 | +# Extensions to add to a certificate request |
516 | +subjectAltName = @alt_names |
517 | + |
518 | +[alt_names] |
519 | +DNS.1 = $PUBLIC |
520 | +DNS.2 = $PRIVATE |
521 | +EOF |
522 | + cat $tmpfile |
523 | + |
524 | + openssl req \ |
525 | + -new -x509 -nodes -days 3650 \ |
526 | + -config $tmpfile \ |
527 | + -keyout $KEY_FILE |
528 | + chmod 0440 $KEYFILE |
529 | + chown root:ssl-cert $KEYFILE |
530 | + |
531 | + rm -f $tmpfile |
532 | +} |
533 | + |
534 | +cert=$(gen_certificate) |
535 | +echo "$cert" > $CERT_FILE |
Changes all look good to me, I'd have like it if this was broken up into several MP of related changes in the future to make it easier to review but other than that Thanks!