Merge lp:~hloeung/charms/trusty/apache2/ssl-security-options into lp:charms/trusty/apache2

Proposed by Haw Loeung
Status: Merged
Merged at revision: 58
Proposed branch: lp:~hloeung/charms/trusty/apache2/ssl-security-options
Merge into: lp:charms/trusty/apache2
Diff against target: 257 lines (+160/-3)
7 files modified
.bzrignore (+1/-0)
README.md (+5/-0)
config.yaml (+12/-0)
data/security.template (+6/-0)
hooks/hooks.py (+5/-0)
hooks/tests/test_balancer_hook.py (+7/-3)
hooks/tests/test_cert.py (+124/-0)
To merge this branch: bzr merge lp:~hloeung/charms/trusty/apache2/ssl-security-options
Reviewer Review Type Date Requested Status
Adam Israel (community) Approve
Review Queue (community) Needs Fixing
Charles Butler (community) Needs Fixing
Review via email: mp+233878@code.launchpad.net

Description of the change

Add option to override supported cipher suites, enforce server's preference instead of browser/client, and reorder by default so:

- PFS cipher suites are more preferred.
- for performance and to keep computation low on servers, prefer 128bits over 256bits.

We also want to keep RC4-SHA for IE6/XP clients and ignore all LOW cipher suites or those where there's potential weaknesses such as DSS and 3DES. I've also used generics such as EECDH, EDH, HIGH, MEDIUM, and LOW so that it is more future-proof as more cipher suites are added or moved between HIGH/MEDIUM/LOW.

Apache docs says the default is "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" [1]. The difference between the default are as follows:

$ diff -Naurp <(openssl ciphers -V 'ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP' | sort) <(openssl ciphers -V 'EECDH+AES128:EDH+AES128:aRSA+AES128:EECDH:EDH:HIGH:-MEDIUM:RC4-SHA:!aNULL:!NULL:!LOW:!3DES:!DSS:!EXP:!PSK:!SRP' | sort) | grep '^[+-]'
--- /dev/fd/63 2014-09-09 17:16:42.124195525 +1000
+++ /dev/fd/62 2014-09-09 17:16:42.124195525 +1000
- 0x00,0x03 - EXP-RC4-MD5 SSLv3 Kx=RSA(512) Au=RSA Enc=RC4(40) Mac=MD5 export
- 0x00,0x04 - RC4-MD5 SSLv3 Kx=RSA Au=RSA Enc=RC4(128) Mac=MD5
- 0x00,0x06 - EXP-RC2-CBC-MD5 SSLv3 Kx=RSA(512) Au=RSA Enc=RC2(40) Mac=MD5 export
- 0x00,0x08 - EXP-DES-CBC-SHA SSLv3 Kx=RSA(512) Au=RSA Enc=DES(40) Mac=SHA1 export
- 0x00,0x09 - DES-CBC-SHA SSLv3 Kx=RSA Au=RSA Enc=DES(56) Mac=SHA1
- 0x00,0x0A - DES-CBC3-SHA SSLv3 Kx=RSA Au=RSA Enc=3DES(168) Mac=SHA1
- 0x00,0x11 - EXP-EDH-DSS-DES-CBC-SHA SSLv3 Kx=DH(512) Au=DSS Enc=DES(40) Mac=SHA1 export
- 0x00,0x12 - EDH-DSS-DES-CBC-SHA SSLv3 Kx=DH Au=DSS Enc=DES(56) Mac=SHA1
- 0x00,0x13 - EDH-DSS-DES-CBC3-SHA SSLv3 Kx=DH Au=DSS Enc=3DES(168) Mac=SHA1
- 0x00,0x14 - EXP-EDH-RSA-DES-CBC-SHA SSLv3 Kx=DH(512) Au=RSA Enc=DES(40) Mac=SHA1 export
- 0x00,0x15 - EDH-RSA-DES-CBC-SHA SSLv3 Kx=DH Au=RSA Enc=DES(56) Mac=SHA1
- 0x00,0x16 - EDH-RSA-DES-CBC3-SHA SSLv3 Kx=DH Au=RSA Enc=3DES(168) Mac=SHA1
- 0x00,0x32 - DHE-DSS-AES128-SHA SSLv3 Kx=DH Au=DSS Enc=AES(128) Mac=SHA1
- 0x00,0x38 - DHE-DSS-AES256-SHA SSLv3 Kx=DH Au=DSS Enc=AES(256) Mac=SHA1
- 0x00,0x40 - DHE-DSS-AES128-SHA256 TLSv1.2 Kx=DH Au=DSS Enc=AES(128) Mac=SHA256
- 0x00,0x44 - DHE-DSS-CAMELLIA128-SHA SSLv3 Kx=DH Au=DSS Enc=Camellia(128) Mac=SHA1
- 0x00,0x6A - DHE-DSS-AES256-SHA256 TLSv1.2 Kx=DH Au=DSS Enc=AES(256) Mac=SHA256
- 0x00,0x87 - DHE-DSS-CAMELLIA256-SHA SSLv3 Kx=DH Au=DSS Enc=Camellia(256) Mac=SHA1
- 0x00,0x8A - PSK-RC4-SHA SSLv3 Kx=PSK Au=PSK Enc=RC4(128) Mac=SHA1
- 0x00,0x8B - PSK-3DES-EDE-CBC-SHA SSLv3 Kx=PSK Au=PSK Enc=3DES(168) Mac=SHA1
- 0x00,0x8C - PSK-AES128-CBC-SHA SSLv3 Kx=PSK Au=PSK Enc=AES(128) Mac=SHA1
- 0x00,0x8D - PSK-AES256-CBC-SHA SSLv3 Kx=PSK Au=PSK Enc=AES(256) Mac=SHA1
- 0x00,0x96 - SEED-SHA SSLv3 Kx=RSA Au=RSA Enc=SEED(128) Mac=SHA1
- 0x00,0x99 - DHE-DSS-SEED-SHA SSLv3 Kx=DH Au=DSS Enc=SEED(128) Mac=SHA1
- 0x00,0x9A - DHE-RSA-SEED-SHA SSLv3 Kx=DH Au=RSA Enc=SEED(128) Mac=SHA1
- 0x00,0xA2 - DHE-DSS-AES128-GCM-SHA256 TLSv1.2 Kx=DH Au=DSS Enc=AESGCM(128) Mac=AEAD
- 0x00,0xA3 - DHE-DSS-AES256-GCM-SHA384 TLSv1.2 Kx=DH Au=DSS Enc=AESGCM(256) Mac=AEAD
- 0xC0,0x02 - ECDH-ECDSA-RC4-SHA SSLv3 Kx=ECDH/ECDSA Au=ECDH Enc=RC4(128) Mac=SHA1
- 0xC0,0x03 - ECDH-ECDSA-DES-CBC3-SHA SSLv3 Kx=ECDH/ECDSA Au=ECDH Enc=3DES(168) Mac=SHA1
- 0xC0,0x07 - ECDHE-ECDSA-RC4-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=RC4(128) Mac=SHA1
- 0xC0,0x08 - ECDHE-ECDSA-DES-CBC3-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=3DES(168) Mac=SHA1
- 0xC0,0x0C - ECDH-RSA-RC4-SHA SSLv3 Kx=ECDH/RSA Au=ECDH Enc=RC4(128) Mac=SHA1
- 0xC0,0x0D - ECDH-RSA-DES-CBC3-SHA SSLv3 Kx=ECDH/RSA Au=ECDH Enc=3DES(168) Mac=SHA1
- 0xC0,0x11 - ECDHE-RSA-RC4-SHA SSLv3 Kx=ECDH Au=RSA Enc=RC4(128) Mac=SHA1
- 0xC0,0x12 - ECDHE-RSA-DES-CBC3-SHA SSLv3 Kx=ECDH Au=RSA Enc=3DES(168) Mac=SHA1
- 0xC0,0x16 - AECDH-RC4-SHA SSLv3 Kx=ECDH Au=None Enc=RC4(128) Mac=SHA1
- 0xC0,0x17 - AECDH-DES-CBC3-SHA SSLv3 Kx=ECDH Au=None Enc=3DES(168) Mac=SHA1
- 0xC0,0x18 - AECDH-AES128-SHA SSLv3 Kx=ECDH Au=None Enc=AES(128) Mac=SHA1
- 0xC0,0x19 - AECDH-AES256-SHA SSLv3 Kx=ECDH Au=None Enc=AES(256) Mac=SHA1
- 0xC0,0x1A - SRP-3DES-EDE-CBC-SHA SSLv3 Kx=SRP Au=SRP Enc=3DES(168) Mac=SHA1
- 0xC0,0x1B - SRP-RSA-3DES-EDE-CBC-SHA SSLv3 Kx=SRP Au=RSA Enc=3DES(168) Mac=SHA1
- 0xC0,0x1C - SRP-DSS-3DES-EDE-CBC-SHA SSLv3 Kx=SRP Au=DSS Enc=3DES(168) Mac=SHA1
- 0xC0,0x1D - SRP-AES-128-CBC-SHA SSLv3 Kx=SRP Au=SRP Enc=AES(128) Mac=SHA1
- 0xC0,0x1E - SRP-RSA-AES-128-CBC-SHA SSLv3 Kx=SRP Au=RSA Enc=AES(128) Mac=SHA1
- 0xC0,0x1F - SRP-DSS-AES-128-CBC-SHA SSLv3 Kx=SRP Au=DSS Enc=AES(128) Mac=SHA1
- 0xC0,0x20 - SRP-AES-256-CBC-SHA SSLv3 Kx=SRP Au=SRP Enc=AES(256) Mac=SHA1
- 0xC0,0x21 - SRP-RSA-AES-256-CBC-SHA SSLv3 Kx=SRP Au=RSA Enc=AES(256) Mac=SHA1
- 0xC0,0x22 - SRP-DSS-AES-256-CBC-SHA SSLv3 Kx=SRP Au=DSS Enc=AES(256) Mac=SHA1

[1]http://httpd.apache.org/docs/2.2/mod/mod_ssl.html#sslciphersuite

To post a comment you must log in.
Revision history for this message
Review Queue (review-queue) wrote :

The results (PASS) are in and available here: http://reports.vapour.ws/charm-tests/charm-bundle-test-967-results

review: Approve (cbt)
Revision history for this message
Charles Butler (lazypower) wrote :

Greetings Haw,

I've had a chance to review your merge proposal and this looks like a very welcome addition to the Apache2 charm. I'm a fan of using Ephemeral (temp.key) Diffie-Hellman key exchange (no cert) after hearing about it on the Security Now podcast. I see this would expose that configuration to me in the charm and I think that's wonderful.

I've deployed the requested patch and had no issues, however i'd like to see an addition to the README calling out the new feature, as well as example deployment options for users that are interested in this behavior.

Once you've gotten the requested documentation added I see no reason this won't be merged.

Thanks for the great contribution!

review: Needs Fixing
Revision history for this message
Haw Loeung (hloeung) wrote :

Charles, first of all, thanks for reviewing my changes.

Unfortunately, it seems that there's a slight misunderstanding here. My changes allows overriding the default apache settings for SSL sites and overrides them with more recommended settings.

You can see the default settings in /etc/apache2/mods-available/ssl.conf.

Revision history for this message
Review Queue (review-queue) wrote :

This items has failed automated testing! Results available here http://reports.vapour.ws/charm-tests/charm-bundle-test-1191-results

review: Needs Fixing (automated testing)
58. By Haw Loeung

[trivial] Merged changes from upstream.

59. By Haw Loeung

Disable SSLv3 to mitigate POODLE.

60. By Haw Loeung

Refine cipher suites.

Revision history for this message
Haw Loeung (hloeung) wrote :
Download full text (6.3 KiB)

Very similar to Mozilla's "Intermediate" settings but with 128 preferred over 256. Also we're not specifying specific cipher suites and use "HIGH" to support any new ciphers OpenSSL adds.

Diff between Mozilla's settings and ours:

$ diff -Naurp <(openssl ciphers -V 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:AES:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK' | grep -Ev '(DSS|SRP)') <(openssl ciphers -V 'EECDH+AESGCM+AES128:EDH+AESGCM+AES128:EECDH+AES128:EDH+AES128:ECDH+AESGCM+AES128:aRSA+AESGCM+AES128:ECDH+AES128:DH+AES128:aRSA+AES128:EECDH+AESGCM:EDH+AESGCM:EECDH:EDH:ECDH+AESGCM:aRSA+AESGCM:ECDH:DH:aRSA:HIGH:!MEDIUM:!aNULL:!NULL:!LOW:!3DES:!DSS:!EXP:!PSK:!SRP')
--- /dev/fd/63 2014-10-17 00:19:25.160785634 +1100
+++ /dev/fd/62 2014-10-17 00:19:25.160785634 +1100
@@ -1,36 +1,40 @@
           0xC0,0x2F - ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD
           0xC0,0x2B - ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD
- 0xC0,0x30 - ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD
- 0xC0,0x2C - ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
           0x00,0x9E - DHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=DH Au=RSA Enc=AESGCM(128) Mac=AEAD
- 0x00,0x9F - DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH Au=RSA Enc=AESGCM(256) Mac=AEAD
           0xC0,0x27 - ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA256
           0xC0,0x23 - ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA256
           0xC0,0x13 - ECDHE-RSA-AES128-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA1
           0xC0,0x09 - ECDHE-ECDSA-AES128-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA1
- 0xC0,0x28 - ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA384
- 0xC0,0x24 - ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384
- 0xC0,0x14 - ECDHE-RSA-AES256-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA1
- 0xC0,0x0A - ECDHE-ECDSA-AES256-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA1
           0x00,0x67 - DHE-RSA-AES128-SHA256 TLSv1.2 Kx=DH Au=RSA Enc=AES(128) Mac=SHA256
           0x00,0x33 - DHE-RSA-AES128-SHA SSLv3 Kx=DH Au=RSA Enc=AES(128) Mac=SHA1
- 0x00,0x6B - DHE-RSA-AES256-SHA256 TLSv1.2 Kx=DH Au=RSA Enc=AES(256) Mac=SHA256
- 0x00,0x39 - DHE-RSA-AES256-SHA SSLv3 Kx=DH Au=RSA Enc=AES(256) Mac=SHA1
- 0x00,0x9C - AES128-GCM-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AESGCM(128) Mac=AEAD
- ...

Read more...

Revision history for this message
Haw Loeung (hloeung) wrote :
Revision history for this message
Adam Israel (aisrael) wrote :

Hi Haw,

I've had a chance to review this merge proposal. As Charles mentioned, this will be a very nice addition to the apache2 charm. I'd also like to see some documentation added to the README to describe this new functionality, as well as unit tests against it.

There's one minor bug that could also use fixing. The Makefile needs to have `python-jinja2` added to the list of packages installed, or `make build` will fail in a clean environment.

I expect there'll be no problem promoting these changes to the store, once the above are addressed.

Thanks again for all of your work!

review: Needs Fixing
61. By Haw Loeung

Merged changes from upstream.

62. By Haw Loeung

Document new ssl_protocol, ssl_honor_cipher_order, and ssl_cipher_suite configuration options.

63. By Haw Loeung

Ignore virtualenv created by make build/test.

Revision history for this message
Adam Israel (aisrael) wrote :

Hi Haw,

Thanks for your work addressing the requested changes! I had the opportunity to retest this merge proposal today. The updates to the README and Makefile look great. I did run into one issue running the tests, unfortunately.

Both test_is_selfsigned_cert_stale_private_address_changed and test_is_selfsigned_cert_stale_public_address_changed failed, running under the local environment:

======================================================================
FAIL: tests.test_cert.CertTests.test_is_selfsigned_cert_stale_private_address_changed
----------------------------------------------------------------------
_StringException: Traceback (most recent call last):
  File "/home/stone/charms/trusty/apache2/.venv/local/lib/python2.7/site-packages/mock.py", line 1201, in patched
    return func(*args, **keywargs)
  File "/home/stone/charms/trusty/apache2/hooks/tests/test_cert.py", line 95, in test_is_selfsigned_cert_stale_private_address_changed
    hooks.is_selfsigned_cert_stale(config, cert_file, key_file))
  File "/home/stone/charms/trusty/apache2/.venv/local/lib/python2.7/site-packages/unittest2/case.py", line 678, in assertTrue
    raise self.failureException(msg)
AssertionError: False is not true

======================================================================
FAIL: tests.test_cert.CertTests.test_is_selfsigned_cert_stale_public_address_changed
----------------------------------------------------------------------
_StringException: Traceback (most recent call last):
  File "/home/stone/charms/trusty/apache2/.venv/local/lib/python2.7/site-packages/mock.py", line 1201, in patched
    return func(*args, **keywargs)
  File "/home/stone/charms/trusty/apache2/hooks/tests/test_cert.py", line 82, in test_is_selfsigned_cert_stale_public_address_changed
    hooks.is_selfsigned_cert_stale(config, cert_file, key_file))
  File "/home/stone/charms/trusty/apache2/.venv/local/lib/python2.7/site-packages/unittest2/case.py", line 678, in assertTrue
    raise self.failureException(msg)
AssertionError: False is not true

Thanks again for all your hard work on this! I'm looking forward to seeing these changes promulgated.

review: Needs Fixing
Revision history for this message
Adam Israel (aisrael) wrote :

Following up based on the precise version of this MP, the failing test is being caused upstream and should not hold up this merge proposal. You have my +1.

I'll file a bug against lp:charms/apache2, unless there's already one open.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2013-10-10 22:47:57 +0000
3+++ .bzrignore 2014-11-20 00:25:17 +0000
4@@ -8,3 +8,4 @@
5 *.key
6 lib/*
7 build/charm-helpers
8+.venv/
9
10=== modified file 'README.md'
11--- README.md 2014-06-20 21:27:04 +0000
12+++ README.md 2014-11-20 00:25:17 +0000
13@@ -261,6 +261,11 @@
14 according to `ssl_certlocation` and `ssl_keylocation` as listed
15 above.
16
17+`ssl_protocol`, `ssl_honor_cipher_order`, and `ssl_cipher_suite` can
18+be used to override SSL/TLS version and the cipher suites supported.
19+These default to what Canonical IS recommends and is using. Before
20+making any changes, please see the Mozilla Security/Server Side TLS.
21+
22 ## `{enable,disable}_modules`
23
24 Space separated list of modules to be enabled or disabled. If a module to
25
26=== modified file 'config.yaml'
27--- config.yaml 2014-01-30 09:11:53 +0000
28+++ config.yaml 2014-11-20 00:25:17 +0000
29@@ -151,6 +151,18 @@
30 base64 encoded chain certificates file. If ssl_cert is
31 specified as SELFSIGNED, this will be ignored.
32 default: ''
33+ ssl_protocol:
34+ type: string
35+ description: SSL Protocols to enable.
36+ default: "ALL -SSLv2 -SSLv3"
37+ ssl_honor_cipher_order:
38+ type: string
39+ description: Enable server cipher suite preference.
40+ default: "On"
41+ ssl_cipher_suite:
42+ type: string
43+ description: List of server cipher suites.
44+ default: "EECDH+AESGCM+AES128:EDH+AESGCM+AES128:EECDH+AES128:EDH+AES128:ECDH+AESGCM+AES128:aRSA+AESGCM+AES128:ECDH+AES128:DH+AES128:aRSA+AES128:EECDH+AESGCM:EDH+AESGCM:EECDH:EDH:ECDH+AESGCM:aRSA+AESGCM:ECDH:DH:aRSA:HIGH:!MEDIUM:!aNULL:!NULL:!LOW:!3DES:!DSS:!EXP:!PSK:!SRP"
45 server_tokens:
46 type: string
47 description: Security setting. Set to one of Full OS Minimal Minor Major Prod
48
49=== modified file 'data/security.template'
50--- data/security.template 2014-05-27 14:44:13 +0000
51+++ data/security.template 2014-11-20 00:25:17 +0000
52@@ -50,3 +50,9 @@
53 # Set to one of: On | Off | extended
54 #
55 TraceEnable {{ trace_enabled }}
56+
57+<IfModule mod_ssl.c>
58+ SSLProtocol {{ ssl_protocol }}
59+ SSLHonorCipherOrder {{ ssl_honor_cipher_order }}
60+ SSLCipherSuite {{ ssl_cipher_suite }}
61+</IfModule>
62
63=== modified file 'hooks/hooks.py'
64--- hooks/hooks.py 2014-10-29 00:10:14 +0000
65+++ hooks/hooks.py 2014-11-20 00:25:17 +0000
66@@ -23,6 +23,7 @@
67 unit_get
68 )
69 from charmhelpers.contrib.charmsupport import nrpe
70+from charmhelpers.fetch import apt_update
71
72 ###############################################################################
73 # Global variables
74@@ -287,6 +288,7 @@
75 def install_hook():
76 if not os.path.exists(default_apache2_service_config_dir):
77 os.mkdir(default_apache2_service_config_dir, 0600)
78+ apt_update(fatal=True)
79 apt_get_install("python-jinja2")
80 apt_get_install("python-pyasn1")
81 apt_get_install("python-pyasn1-modules")
82@@ -470,6 +472,9 @@
83 'server_tokens': config_data['server_tokens'],
84 'server_signature': config_data['server_signature'],
85 'trace_enabled': config_data['trace_enabled'],
86+ 'ssl_protocol': config_data['ssl_protocol'],
87+ 'ssl_honor_cipher_order': config_data['ssl_honor_cipher_order'],
88+ 'ssl_cipher_suite': config_data['ssl_cipher_suite'],
89 'is_apache24': is_apache24(),
90 }
91 template = \
92
93=== modified file 'hooks/tests/test_balancer_hook.py'
94--- hooks/tests/test_balancer_hook.py 2014-06-20 21:27:04 +0000
95+++ hooks/tests/test_balancer_hook.py 2014-11-20 00:25:17 +0000
96@@ -502,7 +502,9 @@
97 @patch('os.mkdir')
98 @patch('hooks.apt_get_install')
99 @patch('hooks.log', MagicMock())
100- def test_installs_hook(self, apt_get_install, mkdir, exists, config_get):
101+ @patch('hooks.apt_update')
102+ def test_installs_hook(
103+ self, apt_update, apt_get_install, mkdir, exists, config_get):
104 exists.return_value = self.not_a_dir
105 config_get.return_value = None
106 apt_get_install.return_value = 'some result'
107@@ -526,8 +528,9 @@
108 @patch('os.mkdir')
109 @patch('hooks.apt_get_install')
110 @patch('hooks.log', MagicMock())
111+ @patch('hooks.apt_update')
112 def test_install_hook_installs_extra_packages(
113- self, apt_get_install, mkdir, exists, config_get):
114+ self, apt_update, apt_get_install, mkdir, exists, config_get):
115 exists.return_value = self.dir_exists
116 config_get.return_value = "extra"
117 apt_get_install.return_value = 'some result'
118@@ -549,8 +552,9 @@
119 @patch('os.mkdir')
120 @patch('hooks.apt_get_install')
121 @patch('hooks.log', MagicMock())
122+ @patch('hooks.apt_update')
123 def test_doesnt_create_dir_to_install_hooks_if_not_needed(
124- self, apt_get_install, mkdir, exists, config_get):
125+ self, apt_update, apt_get_install, mkdir, exists, config_get):
126 exists.return_value = self.dir_exists
127 config_get.return_value = None
128 apt_get_install.return_value = 'some result'
129
130=== added file 'hooks/tests/test_cert.py'
131--- hooks/tests/test_cert.py 1970-01-01 00:00:00 +0000
132+++ hooks/tests/test_cert.py 2014-11-20 00:25:17 +0000
133@@ -0,0 +1,124 @@
134+import os
135+import shutil
136+import tempfile
137+
138+from testtools import TestCase
139+from mock import patch
140+
141+import hooks
142+
143+
144+original_open = open
145+
146+
147+def _unit_get(value):
148+ return value
149+
150+
151+def _unit_get_public_address_changed(value):
152+ if value == "public-address":
153+ return "changed"
154+ return "foo"
155+
156+
157+def _unit_get_private_address_changed(value):
158+ if value == "private-address":
159+ return "changed"
160+ return "foo"
161+
162+
163+class CertTests(TestCase):
164+ def setUp(self):
165+ super(CertTests, self).setUp()
166+ self.tempdir = tempfile.mkdtemp()
167+
168+ def tearDown(self):
169+ super(CertTests, self).tearDown()
170+ shutil.rmtree(self.tempdir, ignore_errors=True)
171+
172+ def test_is_selfsigned_cert_stale_missing_key(self):
173+ """Missing cert, we should regenerate."""
174+ config = {"servername": "servername"}
175+ cert_file = os.path.join(self.tempdir, "cert")
176+ key_file = os.path.join(self.tempdir, "key")
177+ with open(cert_file, "w") as f:
178+ f.write("foo")
179+ self.assertTrue(
180+ hooks.is_selfsigned_cert_stale(config, cert_file, key_file))
181+
182+ def test_is_selfsigned_cert_stale_missing_cert(self):
183+ """Missing cert, we should regenerate."""
184+ config = {"servername": "servername"}
185+ cert_file = os.path.join(self.tempdir, "cert")
186+ key_file = os.path.join(self.tempdir, "key")
187+ with open(key_file, "w") as f:
188+ f.write("foo")
189+ self.assertTrue(
190+ hooks.is_selfsigned_cert_stale(config, cert_file, key_file))
191+
192+ @patch("hooks.log")
193+ @patch("hooks.unit_get", return_value="unit")
194+ def test_is_selfsigned_cert_stale_cn_changed(self, unit_get, log):
195+ """With different servername, cert *should* be regenerated."""
196+ config = {"servername": "servername"}
197+ cert_file = os.path.join(self.tempdir, "cert")
198+ key_file = os.path.join(self.tempdir, "key")
199+ hooks.gen_selfsigned_cert(config, cert_file, key_file)
200+ config["servername"] = "changed"
201+ self.assertTrue(
202+ hooks.is_selfsigned_cert_stale(config, cert_file, key_file))
203+
204+ @patch("hooks.log")
205+ @patch("hooks.unit_get", side_effect=_unit_get)
206+ def test_is_selfsigned_cert_stale_public_address_changed(
207+ self, unit_get, log):
208+ """With different servername, cert *should* be regenerated."""
209+ config = {"servername": "servername"}
210+ cert_file = os.path.join(self.tempdir, "cert")
211+ key_file = os.path.join(self.tempdir, "key")
212+ hooks.gen_selfsigned_cert(config, cert_file, key_file)
213+ unit_get.side_effect = _unit_get_public_address_changed
214+ self.assertTrue(
215+ hooks.is_selfsigned_cert_stale(config, cert_file, key_file))
216+
217+ @patch("hooks.log")
218+ @patch("hooks.unit_get", side_effect=_unit_get)
219+ def test_is_selfsigned_cert_stale_private_address_changed(
220+ self, unit_get, log):
221+ """With different servername, cert *should* be regenerated."""
222+ config = {"servername": "servername"}
223+ cert_file = os.path.join(self.tempdir, "cert")
224+ key_file = os.path.join(self.tempdir, "key")
225+ hooks.gen_selfsigned_cert(config, cert_file, key_file)
226+ unit_get.side_effect = _unit_get_private_address_changed
227+ self.assertTrue(
228+ hooks.is_selfsigned_cert_stale(config, cert_file, key_file))
229+
230+ @patch("hooks.log")
231+ @patch("hooks.unit_get", return_value="unit")
232+ def test_is_selfsigned_cert_stale_assert_false(self, unit_get, log):
233+ """Happy path, cert exists, doesn't need to be regenerated."""
234+ config = {"servername": "servername"}
235+ cert_file = os.path.join(self.tempdir, "cert")
236+ key_file = os.path.join(self.tempdir, "key")
237+ hooks.gen_selfsigned_cert(config, cert_file, key_file)
238+ self.assertFalse(
239+ hooks.is_selfsigned_cert_stale(config, cert_file, key_file))
240+
241+ @patch("hooks.log")
242+ @patch("hooks.unit_get", return_value="unit")
243+ def test_gen_selfsigned_cert(self, unit_get, log):
244+ """
245+ Happy path, make sure we can generate a cert, invalidate,
246+ and write another.
247+ """
248+ config = {"servername": "servername"}
249+ cert_file = os.path.join(self.tempdir, "cert")
250+ key_file = os.path.join(self.tempdir, "key")
251+ hooks.gen_selfsigned_cert(config, cert_file, key_file)
252+ config["servername"] = "changed"
253+ self.assertTrue(
254+ hooks.is_selfsigned_cert_stale(config, cert_file, key_file))
255+ hooks.gen_selfsigned_cert(config, cert_file, key_file)
256+ self.assertFalse(
257+ hooks.is_selfsigned_cert_stale(config, cert_file, key_file))

Subscribers

People subscribed via source and target branches

to all changes: