Merge lp:~frankban/charms/precise/juju-gui/bug-1092515-certificates into lp:~juju-gui/charms/precise/juju-gui/trunk

Proposed by Francesco Banconi
Status: Merged
Merged at revision: 19
Proposed branch: lp:~frankban/charms/precise/juju-gui/bug-1092515-certificates
Merge into: lp:~juju-gui/charms/precise/juju-gui/trunk
Diff against target: 283 lines (+94/-24)
7 files modified
config.yaml (+12/-0)
config/nginx.conf.template (+2/-2)
hooks/config-changed (+13/-4)
hooks/install (+5/-1)
hooks/start (+3/-1)
hooks/utils.py (+30/-14)
tests/test_utils.py (+29/-2)
To merge this branch: bzr merge lp:~frankban/charms/precise/juju-gui/bug-1092515-certificates
Reviewer Review Type Date Requested Status
Juju GUI Hackers Pending
Review via email: mp+141105@code.launchpad.net

Description of the change

Allow using an own SSL cert and private key.

Added two new config options, one for the certificate, one
for the private key. If they are both provided, they are used
by nginx, otherwise, a new certificate is automatically
generated.

Fixed a pre-existent bug: even if you can specify the
directory where to store the certificates, this path
was not used by nginx, because an hardcoded one was
present in the configuration file.

Improved how ssl options are handled in config-changes.
If the SSL path is changed using 'juju set', now that
change is reflected in the nginx config file, and the
service correctly restarted.

Added tests for the process of saving or generating SSL
certificates.

Some code clean up.

Please note that all the SSL stuff is still
disabled/commented.

https://codereview.appspot.com/6976046/

To post a comment you must log in.
Revision history for this message
Francesco Banconi (frankban) wrote :

Reviewers: mp+141105_code.launchpad.net,

Message:
Please take a look.

Description:
Allow using an own SSL cert and private key.

Added two new config options, one for the certificate, one
for the private key. If they are both provided, they are used
by nginx, otherwise, a new certificate is automatically
generated.

Fixed a pre-existent bug: even if you can specify the
directory where to store the certificates, this path
was not used by nginx, because an hardcoded one was
present in the configuration file.

Improved how ssl options are handled in config-changes.
If the SSL path is changed using 'juju set', now that
change is reflected in the nginx config file, and the
service correctly restarted.

Added tests for the process of saving or generating SSL
certificates.

Some code clean up.

Please note that all the SSL stuff is still
disabled/commented.

https://code.launchpad.net/~frankban/charms/precise/juju-gui/bug-1092515-certificates/+merge/141105

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/6976046/

Affected files:
   [revision details]
   config.yaml
   config/nginx.conf.template
   hooks/config-changed
   hooks/install
   hooks/start
   hooks/utils.py
   tests/test_utils.py

Revision history for this message
Francesco Banconi (frankban) wrote :
Revision history for this message
Nicola Larosa (teknico) wrote :
Revision history for this message
Madison Scott-Clary (makyo) wrote :

Looks good to me, land as is.

Thanks for the changes.

https://codereview.appspot.com/6976046/

Revision history for this message
Gary Poster (gary) wrote :

I'm distracted that I can't run the tests successfully--part of the
problem was that a new d3 release broke our build, and I asked bac to
fix that with a better package spec--but this looks good and should
land. Thank you!

Gary

https://codereview.appspot.com/6976046/

Revision history for this message
Francesco Banconi (frankban) wrote :

*** Submitted:

Allow using an own SSL cert and private key.

Added two new config options, one for the certificate, one
for the private key. If they are both provided, they are used
by nginx, otherwise, a new certificate is automatically
generated.

Fixed a pre-existent bug: even if you can specify the
directory where to store the certificates, this path
was not used by nginx, because an hardcoded one was
present in the configuration file.

Improved how ssl options are handled in config-changes.
If the SSL path is changed using 'juju set', now that
change is reflected in the nginx config file, and the
service correctly restarted.

Added tests for the process of saving or generating SSL
certificates.

Some code clean up.

Please note that all the SSL stuff is still
disabled/commented.

R=teknico, matthew.scott, gary.poster
CC=
https://codereview.appspot.com/6976046

https://codereview.appspot.com/6976046/

Revision history for this message
Kapil Thangavelu (hazmat) wrote :

fwiw, jujucharms.com supports ssl

On Sat, Dec 22, 2012 at 4:55 AM, Francesco Banconi <
<email address hidden>> wrote:

> *** Submitted:
>
> Allow using an own SSL cert and private key.
>
> Added two new config options, one for the certificate, one
> for the private key. If they are both provided, they are used
> by nginx, otherwise, a new certificate is automatically
> generated.
>
> Fixed a pre-existent bug: even if you can specify the
> directory where to store the certificates, this path
> was not used by nginx, because an hardcoded one was
> present in the configuration file.
>
> Improved how ssl options are handled in config-changes.
> If the SSL path is changed using 'juju set', now that
> change is reflected in the nginx config file, and the
> service correctly restarted.
>
> Added tests for the process of saving or generating SSL
> certificates.
>
> Some code clean up.
>
> Please note that all the SSL stuff is still
> disabled/commented.
>
> R=teknico, matthew.scott, gary.poster
> CC=
> https://codereview.appspot.com/6976046
>
>
> https://codereview.appspot.com/6976046/
>
> --
>
> https://code.launchpad.net/~frankban/charms/precise/juju-gui/bug-1092515-certificates/+merge/141105
> Your team Juju GUI Hackers is requested to review the proposed merge of
> lp:~frankban/charms/precise/juju-gui/bug-1092515-certificates into
> lp:~juju-gui/charms/precise/juju-gui/trunk.
>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'config.yaml'
--- config.yaml 2012-12-20 14:56:29 +0000
+++ config.yaml 2012-12-21 17:47:23 +0000
@@ -51,3 +51,15 @@
51 The path to the directory where the SSL certificates are stored.51 The path to the directory where the SSL certificates are stored.
52 type: string52 type: string
53 default: /etc/ssl/private/juju-gui53 default: /etc/ssl/private/juju-gui
54 ssl-cert-contents:
55 description: |
56 The contents of the certificate file to be used in SSL connections to
57 the GUI. Both ssl-cert-contents and ssl-key-contents must be provided.
58 If not, cetificates will be automatically generated.
59 type: string
60 ssl-key-contents:
61 description: |
62 The contents of the private key file to be used in SSL connections to
63 the GUI. Both ssl-cert-contents and ssl-key-contents must be provided.
64 If not, cetificates will be automatically generated.
65 type: string
5466
=== modified file 'config/nginx.conf.template'
--- config/nginx.conf.template 2012-12-20 18:02:44 +0000
+++ config/nginx.conf.template 2012-12-21 17:47:23 +0000
@@ -13,8 +13,8 @@
13 root %(server_root)s;13 root %(server_root)s;
14 index index.html;14 index index.html;
15 # Uncomment to switch back to TLS connections.15 # Uncomment to switch back to TLS connections.
16 # ssl_certificate /etc/ssl/private/juju-gui/server.pem;16 # ssl_certificate %(ssl_cert_path)s/server.pem;
17 # ssl_certificate_key /etc/ssl/private/juju-gui/server.key;17 # ssl_certificate_key %(ssl_cert_path)s/server.key;
1818
19 # Serve static assets.19 # Serve static assets.
20 location ^~ /juju-ui/ {20 location ^~ /juju-ui/ {
2121
=== modified file 'hooks/config-changed'
--- hooks/config-changed 2012-12-20 14:56:29 +0000
+++ hooks/config-changed 2012-12-21 17:47:23 +0000
@@ -27,8 +27,8 @@
27 fetch_gui,27 fetch_gui,
28 GUI,28 GUI,
29 IMPROV,29 IMPROV,
30 save_or_create_certificates,
30 setup_gui,31 setup_gui,
31 setup_nginx,
32 start_agent,32 start_agent,
33 start_gui,33 start_gui,
34 start_improv,34 start_improv,
@@ -55,11 +55,18 @@
55 release_tarball = fetch_gui(55 release_tarball = fetch_gui(
56 config['juju-gui-source'], config['command-log-file'])56 config['juju-gui-source'], config['command-log-file'])
57 setup_gui(release_tarball)57 setup_gui(release_tarball)
58 setup_nginx(config['ssl-cert-path'])
59 if 'juju-api-branch' in added_or_changed:58 if 'juju-api-branch' in added_or_changed:
60 juju_api_branch_changed = True59 juju_api_branch_changed = True
61 fetch_api(config['juju-api-branch'])60 fetch_api(config['juju-api-branch'])
6261
62 # Handle changes to SSL certificates.
63 ssl_properties = set(
64 ['ssl-cert-path', 'ssl-cert-contents', 'ssl-key-contents'])
65 if added_or_changed & ssl_properties:
66 save_or_create_certificates(
67 config['ssl-cert-path'], config.get('ssl-cert-contents'),
68 config.get('ssl-key-contents'))
69
63 # Handle changes to the improv server configuration.70 # Handle changes to the improv server configuration.
64 if staging:71 if staging:
65 staging_properties = set(72 staging_properties = set(
@@ -99,11 +106,13 @@
99 gui_properties = set(106 gui_properties = set(
100 ['juju-gui-console-enabled', 'juju-api-port', 'staging'])107 ['juju-gui-console-enabled', 'juju-api-port', 'staging'])
101 gui_changed = added_or_changed & gui_properties108 gui_changed = added_or_changed & gui_properties
102 if gui_changed or juju_gui_source_changed:109 ssl_cert_path_changed = 'ssl-cert-path' in added_or_changed
110 if gui_changed or juju_gui_source_changed or ssl_cert_path_changed:
103 with su('root'):111 with su('root'):
104 service_control(GUI, STOP)112 service_control(GUI, STOP)
105 console_enabled = config.get('juju-gui-console-enabled')113 console_enabled = config.get('juju-gui-console-enabled')
106 start_gui(juju_api_port, console_enabled, staging)114 ssl_cert_path = config['ssl-cert-path']
115 start_gui(juju_api_port, console_enabled, staging, ssl_cert_path)
107116
108117
109def main():118def main():
110119
=== modified file 'hooks/install'
--- hooks/install 2012-12-20 14:56:29 +0000
+++ hooks/install 2012-12-21 17:47:23 +0000
@@ -28,6 +28,7 @@
28 config_json,28 config_json,
29 fetch_api,29 fetch_api,
30 fetch_gui,30 fetch_gui,
31 save_or_create_certificates,
31 setup_gui,32 setup_gui,
32 setup_nginx,33 setup_nginx,
33)34)
@@ -51,7 +52,10 @@
51 release_tarball = fetch_gui(52 release_tarball = fetch_gui(
52 config['juju-gui-source'], config['command-log-file'])53 config['juju-gui-source'], config['command-log-file'])
53 setup_gui(release_tarball)54 setup_gui(release_tarball)
54 setup_nginx(config['ssl-cert-path'])55 setup_nginx()
56 save_or_create_certificates(
57 config['ssl-cert-path'], config.get('ssl-cert-contents'),
58 config.get('ssl-key-contents'))
55 fetch_api(config['juju-api-branch'])59 fetch_api(config['juju-api-branch'])
56 config_json.set(config)60 config_json.set(config)
5761
5862
=== modified file 'hooks/start'
--- hooks/start 2012-12-20 18:02:44 +0000
+++ hooks/start 2012-12-21 17:47:23 +0000
@@ -31,7 +31,9 @@
31 config = get_config()31 config = get_config()
32 juju_api_port = config['juju-api-port']32 juju_api_port = config['juju-api-port']
33 staging = config.get('staging')33 staging = config.get('staging')
34 start_gui(juju_api_port, config['juju-gui-console-enabled'], staging)34 start_gui(
35 juju_api_port, config['juju-gui-console-enabled'], staging,
36 config['ssl-cert-path'])
35 if staging:37 if staging:
36 start_improv(juju_api_port, config['staging-environment'])38 start_improv(juju_api_port, config['staging-environment'])
37 else:39 else:
3840
=== modified file 'hooks/utils.py'
--- hooks/utils.py 2012-12-20 14:56:29 +0000
+++ hooks/utils.py 2012-12-21 17:47:23 +0000
@@ -16,6 +16,7 @@
16 'JUJU_GUI_DIR',16 'JUJU_GUI_DIR',
17 'parse_source',17 'parse_source',
18 'render_to_file',18 'render_to_file',
19 'save_or_create_certificates',
19 'setup_gui',20 'setup_gui',
20 'setup_nginx',21 'setup_nginx',
21 'start_agent',22 'start_agent',
@@ -27,7 +28,6 @@
27import json28import json
28import os29import os
29import logging30import logging
30import shutil
31import tempfile31import tempfile
3232
33from launchpadlib.launchpad import Launchpad33from launchpadlib.launchpad import Launchpad
@@ -55,6 +55,7 @@
55CURRENT_DIR = os.getcwd()55CURRENT_DIR = os.getcwd()
56JUJU_DIR = os.path.join(CURRENT_DIR, 'juju')56JUJU_DIR = os.path.join(CURRENT_DIR, 'juju')
57JUJU_GUI_DIR = os.path.join(CURRENT_DIR, 'juju-gui')57JUJU_GUI_DIR = os.path.join(CURRENT_DIR, 'juju-gui')
58JUJU_GUI_SITE = '/etc/nginx/sites-available/juju-gui'
5859
59# Store the configuration from on invocation to the next.60# Store the configuration from on invocation to the next.
60config_json = Serializer('/tmp/config.json')61config_json = Serializer('/tmp/config.json')
@@ -210,9 +211,9 @@
210 service_control(AGENT, START)211 service_control(AGENT, START)
211212
212213
213def start_gui(juju_api_port, console_enabled, staging,214def start_gui(juju_api_port, console_enabled, staging, ssl_cert_path,
214 config_path='/etc/init/juju-gui.conf',215 config_path='/etc/init/juju-gui.conf',
215 nginx_path='/etc/nginx/sites-available/juju-gui',216 nginx_path=JUJU_GUI_SITE,
216 config_js_path=None):217 config_js_path=None):
217 """Set up and start the Juju GUI server."""218 """Set up and start the Juju GUI server."""
218 with su('root'):219 with su('root'):
@@ -233,7 +234,8 @@
233 render_to_file('config.js.template', context, config_js_path)234 render_to_file('config.js.template', context, config_js_path)
234 log('Generating the nginx site configuration file.')235 log('Generating the nginx site configuration file.')
235 context = {236 context = {
236 'server_root': build_dir237 'server_root': build_dir,
238 'ssl_cert_path': ssl_cert_path.rstrip('/'),
237 }239 }
238 render_to_file('nginx.conf.template', context, nginx_path)240 render_to_file('nginx.conf.template', context, nginx_path)
239 log('Starting Juju GUI.')241 log('Starting Juju GUI.')
@@ -307,25 +309,39 @@
307 cmd_log(run('ln', '-sf', first_path_in_dir(release_dir), JUJU_GUI_DIR))309 cmd_log(run('ln', '-sf', first_path_in_dir(release_dir), JUJU_GUI_DIR))
308310
309311
310def setup_nginx(ssl_cert_path):312def setup_nginx():
311 """Set up nginx."""313 """Set up nginx."""
312 log('Setting up nginx.')314 log('Setting up nginx.')
313 nginx_default_site = '/etc/nginx/sites-enabled/default'315 nginx_default_site = '/etc/nginx/sites-enabled/default'
314 juju_gui_site = '/etc/nginx/sites-available/juju-gui'
315 if os.path.exists(nginx_default_site):316 if os.path.exists(nginx_default_site):
316 os.remove(nginx_default_site)317 os.remove(nginx_default_site)
317 if not os.path.exists(juju_gui_site):318 if not os.path.exists(JUJU_GUI_SITE):
318 cmd_log(run('touch', juju_gui_site))319 cmd_log(run('touch', JUJU_GUI_SITE))
319 cmd_log(run('chown', 'ubuntu:', juju_gui_site))320 cmd_log(run('chown', 'ubuntu:', JUJU_GUI_SITE))
320 cmd_log(321 cmd_log(
321 run('ln', '-s', juju_gui_site,322 run('ln', '-s', JUJU_GUI_SITE,
322 '/etc/nginx/sites-enabled/juju-gui'))323 '/etc/nginx/sites-enabled/juju-gui'))
323 # Generate the nginx SSL certificates, if needed.324
325
326def save_or_create_certificates(
327 ssl_cert_path, ssl_cert_contents, ssl_key_contents):
328 """Generate the SSL certificates.
329
330 If both *ssl_cert_contents* and *ssl_key_contents* are provided, use them
331 as certificates; otherwise, generate them.
332 """
324 pem_path = os.path.join(ssl_cert_path, 'server.pem')333 pem_path = os.path.join(ssl_cert_path, 'server.pem')
325 key_path = os.path.join(ssl_cert_path, 'server.key')334 key_path = os.path.join(ssl_cert_path, 'server.key')
326 if not (os.path.exists(pem_path) and os.path.exists(key_path)):335 if not os.path.exists(ssl_cert_path):
327 if not os.path.exists(ssl_cert_path):336 os.makedirs(ssl_cert_path)
328 os.makedirs(ssl_cert_path)337 if ssl_cert_contents and ssl_key_contents:
338 # Save the provided certificates.
339 with open(pem_path, 'w') as cert_file:
340 cert_file.write(ssl_cert_contents)
341 with open(key_path, 'w') as key_file:
342 key_file.write(ssl_key_contents)
343 else:
344 # Generate certificates.
329 # See http://superuser.com/questions/226192/openssl-without-prompt345 # See http://superuser.com/questions/226192/openssl-without-prompt
330 cmd_log(run(346 cmd_log(run(
331 'openssl', 'req', '-new', '-newkey', 'rsa:4096',347 'openssl', 'req', '-new', '-newkey', 'rsa:4096',
332348
=== modified file 'tests/test_utils.py'
--- tests/test_utils.py 2012-12-20 13:27:30 +0000
+++ tests/test_utils.py 2012-12-21 17:47:23 +0000
@@ -16,6 +16,7 @@
16 get_zookeeper_address,16 get_zookeeper_address,
17 parse_source,17 parse_source,
18 render_to_file,18 render_to_file,
19 save_or_create_certificates,
19 start_agent,20 start_agent,
20 start_gui,21 start_gui,
21 start_improv,22 start_improv,
@@ -288,6 +289,29 @@
288 self.assertEqual(expected, self.destination_file.read())289 self.assertEqual(expected, self.destination_file.read())
289290
290291
292class SaveOrCreateCertificatesTest(unittest.TestCase):
293
294 def setUp(self):
295 base_dir = tempfile.mkdtemp()
296 self.addCleanup(shutil.rmtree, base_dir)
297 self.cert_path = os.path.join(base_dir, 'certificates')
298 self.cert_file = os.path.join(self.cert_path, 'server.pem')
299 self.key_file = os.path.join(self.cert_path, 'server.key')
300
301 def test_generation(self):
302 """Ensure certificates are correctly generated."""
303 save_or_create_certificates(
304 self.cert_path, 'some ignored contents', None)
305 self.assertIn('CERTIFICATE', open(self.cert_file).read())
306 self.assertIn('PRIVATE KEY', open(self.key_file).read())
307
308 def test_provided_certificates(self):
309 # Ensure files are correctly saved if their contents are provided.
310 save_or_create_certificates(self.cert_path, 'mycert', 'mykey')
311 self.assertIn('mycert', open(self.cert_file).read())
312 self.assertIn('mykey', open(self.key_file).read())
313
314
291class CmdLogTest(unittest.TestCase):315class CmdLogTest(unittest.TestCase):
292 def setUp(self):316 def setUp(self):
293 # Patch the charmhelpers 'command', which powers get_config. The317 # Patch the charmhelpers 'command', which powers get_config. The
@@ -382,12 +406,15 @@
382 self.addCleanup(nginx_file.close)406 self.addCleanup(nginx_file.close)
383 config_js_file = tempfile.NamedTemporaryFile()407 config_js_file = tempfile.NamedTemporaryFile()
384 self.addCleanup(config_js_file.close)408 self.addCleanup(config_js_file.close)
385 start_gui(port, False, True, self.destination_file.name,409 start_gui(
386 nginx_file.name, config_js_file.name)410 port, False, True, '/tmp/certificates/',
411 self.destination_file.name, nginx_file.name, config_js_file.name)
387 conf = self.destination_file.read()412 conf = self.destination_file.read()
388 self.assertTrue('/usr/sbin/nginx' in conf)413 self.assertTrue('/usr/sbin/nginx' in conf)
389 nginx_conf = nginx_file.read()414 nginx_conf = nginx_file.read()
390 self.assertTrue('juju-gui/build-debug' in nginx_conf)415 self.assertTrue('juju-gui/build-debug' in nginx_conf)
416 self.assertIn('/tmp/certificates/server.pem', nginx_conf)
417 self.assertIn('/tmp/certificates/server.key', nginx_conf)
391 self.assertEqual(self.svc_ctl_call_count, 1)418 self.assertEqual(self.svc_ctl_call_count, 1)
392 self.assertEqual(self.service_names, ['juju-gui'])419 self.assertEqual(self.service_names, ['juju-gui'])
393 self.assertEqual(self.actions, [charmhelpers.START])420 self.assertEqual(self.actions, [charmhelpers.START])

Subscribers

People subscribed via source and target branches