Merge lp:~josvaz/charms/trusty/bip/client_side_ssl-with_helper-lp1604894 into lp:charms/trusty/bip

Proposed by Jose L. VG
Status: Needs review
Proposed branch: lp:~josvaz/charms/trusty/bip/client_side_ssl-with_helper-lp1604894
Merge into: lp:charms/trusty/bip
Prerequisite: lp:~josvaz/charms/trusty/bip/charmhelpers-cleanup
Diff against target: 618 lines (+496/-7)
8 files modified
charm-helpers.yaml (+1/-0)
config.yaml (+4/-0)
hooks/hooks.py (+30/-6)
lib/charmhelpers/contrib/__init__.py (+13/-0)
lib/charmhelpers/contrib/ssl/__init__.py (+92/-0)
lib/charmhelpers/contrib/ssl/service.py (+277/-0)
templates/bip_conf.template (+1/-1)
tests/11-deploy-ssl (+78/-0)
To merge this branch: bzr merge lp:~josvaz/charms/trusty/bip/client_side_ssl-with_helper-lp1604894
Reviewer Review Type Date Requested Status
Review Queue (community) automated testing Approve
Brad Marshall (community) Approve
Pen Gale (community) Approve
charmers Pending
Review via email: mp+301802@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Jose L. VG (josvaz) wrote :

This has been tested on a juju setup on GCE

Revision history for this message
Pen Gale (pengale) wrote :

Code looks sensible, and does not break existing tests, but I would like to see unit or integration tests that cover the self signing code.

review: Needs Fixing
Revision history for this message
Jose L. VG (josvaz) wrote :

Would a test that calls gen_certs() and verifies the generated pem files are for a self signed cert & key suffice?

Revision history for this message
Jose L. VG (josvaz) wrote :

Also this MP depends on this other to be done first:
https://code.launchpad.net/~josvaz/charms/trusty/bip/charmhelpers-cleanup/+merge/301499

Do I need to fix anything in that one?

32. By Jose L. VG

Adding a bip ssl deploy test (validates cert & handshake)

Revision history for this message
Jose L. VG (josvaz) wrote :

Added a 11-deploy-ssl test that deploys a ssl enabled bip on port 6697, it retrieves the server certificate and then validates against it a SSL connection.

I guess this covers all the new feature code.

Please review!

Revision history for this message
Pen Gale (pengale) wrote :

Hello Jose,

Thank you for all your work on this. The new test looks great, and passes when I run bundletester. :-)

I am +1 on this.

There is an outstanding issue with merging and promulgating this, which I noted in the related PR: the bip charm is currently own by ~charmers, rather than a specific maintainer. I can't fix that myself, but I am poking people about it; I'll ping this ticket when the issue is fixed.

review: Approve
Revision history for this message
Pen Gale (pengale) wrote :

Dropping in an update here. The next steps for this change need to be:

@bradm needs to update his branch of this charm with the latest from ~charmers, then merge this PR into it. Then he needs to promulgate a charm from his namespace, and request that a charmer merge it.

That should update the charm store so that bip gets pointed at bradm's namespace (since he's the maintainer), and should allow bradm to more easily maintain the charm going forward.

@josvaz: you most likely don't need to do anything else with this PR; the maintainer (bradm) just needs to do some housekeeping to get things merged.

Revision history for this message
Jose L. VG (josvaz) wrote :

Thanks fo rthe update Pete!

Jose

On Thu, Aug 25, 2016 at 3:43 PM, petevg <email address hidden>
wrote:

> Dropping in an update here. The next steps for this change need to be:
>
> @bradm needs to update his branch of this charm with the latest from
> ~charmers, then merge this PR into it. Then he needs to promulgate a charm
> from his namespace, and request that a charmer merge it.
>
> That should update the charm store so that bip gets pointed at bradm's
> namespace (since he's the maintainer), and should allow bradm to more
> easily maintain the charm going forward.
>
> @josvaz: you most likely don't need to do anything else with this PR; the
> maintainer (bradm) just needs to do some housekeeping to get things merged.
> --
> https://code.launchpad.net/~josvaz/charms/trusty/bip/
> client_side_ssl-with_helper-lp1604894/+merge/301802
> You are the owner of lp:~josvaz/charms/trusty/bip/
> client_side_ssl-with_helper-lp1604894.
>

Revision history for this message
Pen Gale (pengale) wrote :

Poked bradm about confirming that bip is moved and closing this review out.

Revision history for this message
Brad Marshall (brad-marshall) wrote :

Looks good to me, merged into my own branch and am working on getting it updated in the charmstore.

review: Approve
Revision history for this message
Jose L. VG (josvaz) wrote :

Thanks Brad, let me know if/when this MP needs to be marked Merged manually.

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

The results (PASS) are in and available here: http://juju-ci.vapour.ws/job/charm-bundle-test-aws/5067/

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

The results (PASS) are in and available here: http://juju-ci.vapour.ws/job/charm-bundle-test-lxc/4866/

review: Approve (automated testing)

Unmerged revisions

32. By Jose L. VG

Adding a bip ssl deploy test (validates cert & handshake)

31. By Jose L. VG

Added support to client_side_ssl with charmhelper contrib.ssl

30. By Jose L. VG

Got charmhelpers for fetch as apt_install is now there

29. By Jose L. VG

Moved from charm-helpers/charmhelpers to just charmhelpers (plus clean-up)

28. By Jose L. VG

Result of removing charmhelpers and re-syncing it

27. By Jose L. VG

Added charm-helpers.yaml to manage helpers with charm_helpers_sync tool

26. By Jose L. VG

Flake8 suggested fixes, including unused imports

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'charm-helpers.yaml'
--- charm-helpers.yaml 2016-08-19 17:00:35 +0000
+++ charm-helpers.yaml 2016-08-19 17:00:35 +0000
@@ -3,3 +3,4 @@
3include:3include:
4 - core4 - core
5 - fetch5 - fetch
6 - contrib.ssl
67
=== modified file 'config.yaml'
--- config.yaml 2016-07-20 10:26:23 +0000
+++ config.yaml 2016-08-19 17:00:35 +0000
@@ -35,6 +35,10 @@
35 type: string35 type: string
36 default: '{"oftc": {host: "irc.oftc.net", port: 6667}, "freenode": {host: "irc.freenode.net", port: 6667}}'36 default: '{"oftc": {host: "irc.oftc.net", port: 6667}, "freenode": {host: "irc.freenode.net", port: 6667}}'
37 description: IRC Networks defined37 description: IRC Networks defined
38 client_side_ssl:
39 type: string
40 default: "false"
41 description: Use SSL to connect to bip from your client
38 user:42 user:
39 type: string43 type: string
40 description: User definitions44 description: User definitions
4145
=== modified file 'hooks/hooks.py'
--- hooks/hooks.py 2016-08-19 17:00:35 +0000
+++ hooks/hooks.py 2016-08-19 17:00:35 +0000
@@ -3,10 +3,13 @@
3# Copyright 2013 Canonical Ltd. All rights reserved.3# Copyright 2013 Canonical Ltd. All rights reserved.
4# Author: Brad Marshall <brad.marshall@canonical.com>4# Author: Brad Marshall <brad.marshall@canonical.com>
55
6import glob
7import pwd
6import os8import os
7import sys9import sys
8import json10import json
9import yaml11import yaml
12import subprocess
1013
11local_copy = os.path.join(14local_copy = os.path.join(
12 os.path.dirname(os.path.abspath(os.path.dirname(__file__))),15 os.path.dirname(os.path.abspath(os.path.dirname(__file__))),
@@ -29,6 +32,8 @@
29 open_port,32 open_port,
30)33)
3134
35from charmhelpers.contrib import ssl
36
32hook_dir = os.path.abspath(os.path.dirname(__file__))37hook_dir = os.path.abspath(os.path.dirname(__file__))
33charm_dir = os.path.dirname(hook_dir)38charm_dir = os.path.dirname(hook_dir)
3439
@@ -41,6 +46,7 @@
41user_config = config().get('user')46user_config = config().get('user')
42backlog_lines = config().get('backlog_lines')47backlog_lines = config().get('backlog_lines')
43backlog_msg_only = config().get('backlog_msg_only')48backlog_msg_only = config().get('backlog_msg_only')
49client_side_ssl = config().get('client_side_ssl')
44backlog_always = config().get('backlog_always')50backlog_always = config().get('backlog_always')
45bip_conf = '/etc/bip.conf'51bip_conf = '/etc/bip.conf'
46bip_defaults = '/etc/default/bip'52bip_defaults = '/etc/default/bip'
@@ -48,27 +54,44 @@
48required_pkgs = [54required_pkgs = [
49 'bip',55 'bip',
50 'python-jinja2',56 'python-jinja2',
57 'openssl',
51]58]
5259
53hooks = Hooks()60hooks = Hooks()
5461
5562
63def gen_certs():
64 """gen_certs generates bip SSL certificates with openssl CLI"""
65 bip_dir = "/var/lib/bip"
66 pem = "%s/bip.pem" % bip_dir
67 ssl.generate_selfsigned(keyfile=pem, certfile=pem, keysize=4096,
68 cn="BipServerSert")
69 os.chmod('%s/bip.pem' % bip_dir, 0600)
70 bip_uid = pwd.getpwnam("bip").pw_uid
71 os.chown('%s/bip.pem' % bip_dir, bip_uid, -1)
72
73def setup():
74 """setup executes post configuration actions, like certificate generation"""
75 if client_side_ssl.lower() == 'true':
76 gen_certs()
77
78
56def update_config():79def update_config():
57 from jinja2 import Environment, FileSystemLoader80 from jinja2 import Environment, FileSystemLoader
58 if network_config:81 if network_config:
59 # Try originally supported JSON formatted config82 ## Try originally supported JSON formatted config
60 try:83 try:
61 network = json.loads(network_config)84 network = json.loads(network_config)
62 # else use YAML (current):85 ## else use YAML (current):
63 except ValueError:86 except ValueError:
64 network = yaml.load(network_config)87 network = yaml.load(network_config)
65 else:88 else:
66 network = {}89 network = {}
67 if user_config:90 if user_config:
68 # Try originally supported JSON formatted config91 ## Try originally supported JSON formatted config
69 try:92 try:
70 user = json.loads(user_config)93 user = json.loads(user_config)
71 # else use YAML (current):94 ## else use YAML (current):
72 except ValueError:95 except ValueError:
73 user = yaml.load(user_config)96 user = yaml.load(user_config)
74 else:97 else:
@@ -87,12 +110,13 @@
87 'user': user,110 'user': user,
88 'backlog_lines': backlog_lines,111 'backlog_lines': backlog_lines,
89 'backlog_msg_only': backlog_msg_only,112 'backlog_msg_only': backlog_msg_only,
113 'client_side_ssl': client_side_ssl,
90 'backlog_always': backlog_always,114 'backlog_always': backlog_always,
91 }115 }
92 template = template_env.get_template('bip_conf.template').render(116 template = template_env.get_template('bip_conf.template').render(templ_vars)
93 templ_vars)
94 with open(bip_conf, 'w') as bip_conf_config:117 with open(bip_conf, 'w') as bip_conf_config:
95 bip_conf_config.write(str(template))118 bip_conf_config.write(str(template))
119 setup()
96120
97121
98@hooks.hook()122@hooks.hook()
99123
=== added directory 'lib/charmhelpers/contrib'
=== added file 'lib/charmhelpers/contrib/__init__.py'
--- lib/charmhelpers/contrib/__init__.py 1970-01-01 00:00:00 +0000
+++ lib/charmhelpers/contrib/__init__.py 2016-08-19 17:00:35 +0000
@@ -0,0 +1,13 @@
1# Copyright 2014-2015 Canonical Limited.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
014
=== added directory 'lib/charmhelpers/contrib/ssl'
=== added file 'lib/charmhelpers/contrib/ssl/__init__.py'
--- lib/charmhelpers/contrib/ssl/__init__.py 1970-01-01 00:00:00 +0000
+++ lib/charmhelpers/contrib/ssl/__init__.py 2016-08-19 17:00:35 +0000
@@ -0,0 +1,92 @@
1# Copyright 2014-2015 Canonical Limited.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import subprocess
16from charmhelpers.core import hookenv
17
18
19def generate_selfsigned(keyfile, certfile, keysize="1024", config=None, subject=None, cn=None):
20 """Generate selfsigned SSL keypair
21
22 You must provide one of the 3 optional arguments:
23 config, subject or cn
24 If more than one is provided the leftmost will be used
25
26 Arguments:
27 keyfile -- (required) full path to the keyfile to be created
28 certfile -- (required) full path to the certfile to be created
29 keysize -- (optional) SSL key length
30 config -- (optional) openssl configuration file
31 subject -- (optional) dictionary with SSL subject variables
32 cn -- (optional) cerfificate common name
33
34 Required keys in subject dict:
35 cn -- Common name (eq. FQDN)
36
37 Optional keys in subject dict
38 country -- Country Name (2 letter code)
39 state -- State or Province Name (full name)
40 locality -- Locality Name (eg, city)
41 organization -- Organization Name (eg, company)
42 organizational_unit -- Organizational Unit Name (eg, section)
43 email -- Email Address
44 """
45
46 cmd = []
47 if config:
48 cmd = ["/usr/bin/openssl", "req", "-new", "-newkey",
49 "rsa:{}".format(keysize), "-days", "365", "-nodes", "-x509",
50 "-keyout", keyfile,
51 "-out", certfile, "-config", config]
52 elif subject:
53 ssl_subject = ""
54 if "country" in subject:
55 ssl_subject = ssl_subject + "/C={}".format(subject["country"])
56 if "state" in subject:
57 ssl_subject = ssl_subject + "/ST={}".format(subject["state"])
58 if "locality" in subject:
59 ssl_subject = ssl_subject + "/L={}".format(subject["locality"])
60 if "organization" in subject:
61 ssl_subject = ssl_subject + "/O={}".format(subject["organization"])
62 if "organizational_unit" in subject:
63 ssl_subject = ssl_subject + "/OU={}".format(subject["organizational_unit"])
64 if "cn" in subject:
65 ssl_subject = ssl_subject + "/CN={}".format(subject["cn"])
66 else:
67 hookenv.log("When using \"subject\" argument you must "
68 "provide \"cn\" field at very least")
69 return False
70 if "email" in subject:
71 ssl_subject = ssl_subject + "/emailAddress={}".format(subject["email"])
72
73 cmd = ["/usr/bin/openssl", "req", "-new", "-newkey",
74 "rsa:{}".format(keysize), "-days", "365", "-nodes", "-x509",
75 "-keyout", keyfile,
76 "-out", certfile, "-subj", ssl_subject]
77 elif cn:
78 cmd = ["/usr/bin/openssl", "req", "-new", "-newkey",
79 "rsa:{}".format(keysize), "-days", "365", "-nodes", "-x509",
80 "-keyout", keyfile,
81 "-out", certfile, "-subj", "/CN={}".format(cn)]
82
83 if not cmd:
84 hookenv.log("No config, subject or cn provided,"
85 "unable to generate self signed SSL certificates")
86 return False
87 try:
88 subprocess.check_call(cmd)
89 return True
90 except Exception as e:
91 print("Execution of openssl command failed:\n{}".format(e))
92 return False
093
=== added file 'lib/charmhelpers/contrib/ssl/service.py'
--- lib/charmhelpers/contrib/ssl/service.py 1970-01-01 00:00:00 +0000
+++ lib/charmhelpers/contrib/ssl/service.py 2016-08-19 17:00:35 +0000
@@ -0,0 +1,277 @@
1# Copyright 2014-2015 Canonical Limited.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import os
16from os.path import join as path_join
17from os.path import exists
18import subprocess
19
20from charmhelpers.core.hookenv import log, DEBUG
21
22STD_CERT = "standard"
23
24# Mysql server is fairly picky about cert creation
25# and types, spec its creation separately for now.
26MYSQL_CERT = "mysql"
27
28
29class ServiceCA(object):
30
31 default_expiry = str(365 * 2)
32 default_ca_expiry = str(365 * 6)
33
34 def __init__(self, name, ca_dir, cert_type=STD_CERT):
35 self.name = name
36 self.ca_dir = ca_dir
37 self.cert_type = cert_type
38
39 ###############
40 # Hook Helper API
41 @staticmethod
42 def get_ca(type=STD_CERT):
43 service_name = os.environ['JUJU_UNIT_NAME'].split('/')[0]
44 ca_path = os.path.join(os.environ['CHARM_DIR'], 'ca')
45 ca = ServiceCA(service_name, ca_path, type)
46 ca.init()
47 return ca
48
49 @classmethod
50 def get_service_cert(cls, type=STD_CERT):
51 service_name = os.environ['JUJU_UNIT_NAME'].split('/')[0]
52 ca = cls.get_ca()
53 crt, key = ca.get_or_create_cert(service_name)
54 return crt, key, ca.get_ca_bundle()
55
56 ###############
57
58 def init(self):
59 log("initializing service ca", level=DEBUG)
60 if not exists(self.ca_dir):
61 self._init_ca_dir(self.ca_dir)
62 self._init_ca()
63
64 @property
65 def ca_key(self):
66 return path_join(self.ca_dir, 'private', 'cacert.key')
67
68 @property
69 def ca_cert(self):
70 return path_join(self.ca_dir, 'cacert.pem')
71
72 @property
73 def ca_conf(self):
74 return path_join(self.ca_dir, 'ca.cnf')
75
76 @property
77 def signing_conf(self):
78 return path_join(self.ca_dir, 'signing.cnf')
79
80 def _init_ca_dir(self, ca_dir):
81 os.mkdir(ca_dir)
82 for i in ['certs', 'crl', 'newcerts', 'private']:
83 sd = path_join(ca_dir, i)
84 if not exists(sd):
85 os.mkdir(sd)
86
87 if not exists(path_join(ca_dir, 'serial')):
88 with open(path_join(ca_dir, 'serial'), 'w') as fh:
89 fh.write('02\n')
90
91 if not exists(path_join(ca_dir, 'index.txt')):
92 with open(path_join(ca_dir, 'index.txt'), 'w') as fh:
93 fh.write('')
94
95 def _init_ca(self):
96 """Generate the root ca's cert and key.
97 """
98 if not exists(path_join(self.ca_dir, 'ca.cnf')):
99 with open(path_join(self.ca_dir, 'ca.cnf'), 'w') as fh:
100 fh.write(
101 CA_CONF_TEMPLATE % (self.get_conf_variables()))
102
103 if not exists(path_join(self.ca_dir, 'signing.cnf')):
104 with open(path_join(self.ca_dir, 'signing.cnf'), 'w') as fh:
105 fh.write(
106 SIGNING_CONF_TEMPLATE % (self.get_conf_variables()))
107
108 if exists(self.ca_cert) or exists(self.ca_key):
109 raise RuntimeError("Initialized called when CA already exists")
110 cmd = ['openssl', 'req', '-config', self.ca_conf,
111 '-x509', '-nodes', '-newkey', 'rsa',
112 '-days', self.default_ca_expiry,
113 '-keyout', self.ca_key, '-out', self.ca_cert,
114 '-outform', 'PEM']
115 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
116 log("CA Init:\n %s" % output, level=DEBUG)
117
118 def get_conf_variables(self):
119 return dict(
120 org_name="juju",
121 org_unit_name="%s service" % self.name,
122 common_name=self.name,
123 ca_dir=self.ca_dir)
124
125 def get_or_create_cert(self, common_name):
126 if common_name in self:
127 return self.get_certificate(common_name)
128 return self.create_certificate(common_name)
129
130 def create_certificate(self, common_name):
131 if common_name in self:
132 return self.get_certificate(common_name)
133 key_p = path_join(self.ca_dir, "certs", "%s.key" % common_name)
134 crt_p = path_join(self.ca_dir, "certs", "%s.crt" % common_name)
135 csr_p = path_join(self.ca_dir, "certs", "%s.csr" % common_name)
136 self._create_certificate(common_name, key_p, csr_p, crt_p)
137 return self.get_certificate(common_name)
138
139 def get_certificate(self, common_name):
140 if common_name not in self:
141 raise ValueError("No certificate for %s" % common_name)
142 key_p = path_join(self.ca_dir, "certs", "%s.key" % common_name)
143 crt_p = path_join(self.ca_dir, "certs", "%s.crt" % common_name)
144 with open(crt_p) as fh:
145 crt = fh.read()
146 with open(key_p) as fh:
147 key = fh.read()
148 return crt, key
149
150 def __contains__(self, common_name):
151 crt_p = path_join(self.ca_dir, "certs", "%s.crt" % common_name)
152 return exists(crt_p)
153
154 def _create_certificate(self, common_name, key_p, csr_p, crt_p):
155 template_vars = self.get_conf_variables()
156 template_vars['common_name'] = common_name
157 subj = '/O=%(org_name)s/OU=%(org_unit_name)s/CN=%(common_name)s' % (
158 template_vars)
159
160 log("CA Create Cert %s" % common_name, level=DEBUG)
161 cmd = ['openssl', 'req', '-sha1', '-newkey', 'rsa:2048',
162 '-nodes', '-days', self.default_expiry,
163 '-keyout', key_p, '-out', csr_p, '-subj', subj]
164 subprocess.check_call(cmd, stderr=subprocess.PIPE)
165 cmd = ['openssl', 'rsa', '-in', key_p, '-out', key_p]
166 subprocess.check_call(cmd, stderr=subprocess.PIPE)
167
168 log("CA Sign Cert %s" % common_name, level=DEBUG)
169 if self.cert_type == MYSQL_CERT:
170 cmd = ['openssl', 'x509', '-req',
171 '-in', csr_p, '-days', self.default_expiry,
172 '-CA', self.ca_cert, '-CAkey', self.ca_key,
173 '-set_serial', '01', '-out', crt_p]
174 else:
175 cmd = ['openssl', 'ca', '-config', self.signing_conf,
176 '-extensions', 'req_extensions',
177 '-days', self.default_expiry, '-notext',
178 '-in', csr_p, '-out', crt_p, '-subj', subj, '-batch']
179 log("running %s" % " ".join(cmd), level=DEBUG)
180 subprocess.check_call(cmd, stderr=subprocess.PIPE)
181
182 def get_ca_bundle(self):
183 with open(self.ca_cert) as fh:
184 return fh.read()
185
186
187CA_CONF_TEMPLATE = """
188[ ca ]
189default_ca = CA_default
190
191[ CA_default ]
192dir = %(ca_dir)s
193policy = policy_match
194database = $dir/index.txt
195serial = $dir/serial
196certs = $dir/certs
197crl_dir = $dir/crl
198new_certs_dir = $dir/newcerts
199certificate = $dir/cacert.pem
200private_key = $dir/private/cacert.key
201RANDFILE = $dir/private/.rand
202default_md = default
203
204[ req ]
205default_bits = 1024
206default_md = sha1
207
208prompt = no
209distinguished_name = ca_distinguished_name
210
211x509_extensions = ca_extensions
212
213[ ca_distinguished_name ]
214organizationName = %(org_name)s
215organizationalUnitName = %(org_unit_name)s Certificate Authority
216
217
218[ policy_match ]
219countryName = optional
220stateOrProvinceName = optional
221organizationName = match
222organizationalUnitName = optional
223commonName = supplied
224
225[ ca_extensions ]
226basicConstraints = critical,CA:true
227subjectKeyIdentifier = hash
228authorityKeyIdentifier = keyid:always, issuer
229keyUsage = cRLSign, keyCertSign
230"""
231
232
233SIGNING_CONF_TEMPLATE = """
234[ ca ]
235default_ca = CA_default
236
237[ CA_default ]
238dir = %(ca_dir)s
239policy = policy_match
240database = $dir/index.txt
241serial = $dir/serial
242certs = $dir/certs
243crl_dir = $dir/crl
244new_certs_dir = $dir/newcerts
245certificate = $dir/cacert.pem
246private_key = $dir/private/cacert.key
247RANDFILE = $dir/private/.rand
248default_md = default
249
250[ req ]
251default_bits = 1024
252default_md = sha1
253
254prompt = no
255distinguished_name = req_distinguished_name
256
257x509_extensions = req_extensions
258
259[ req_distinguished_name ]
260organizationName = %(org_name)s
261organizationalUnitName = %(org_unit_name)s machine resources
262commonName = %(common_name)s
263
264[ policy_match ]
265countryName = optional
266stateOrProvinceName = optional
267organizationName = match
268organizationalUnitName = optional
269commonName = supplied
270
271[ req_extensions ]
272basicConstraints = CA:false
273subjectKeyIdentifier = hash
274authorityKeyIdentifier = keyid:always, issuer
275keyUsage = digitalSignature, keyEncipherment, keyAgreement
276extendedKeyUsage = serverAuth, clientAuth
277"""
0278
=== modified file 'templates/bip_conf.template'
--- templates/bip_conf.template 2016-07-20 10:26:23 +0000
+++ templates/bip_conf.template 2016-08-19 17:00:35 +0000
@@ -2,7 +2,7 @@
22
3port = {{ listen_port }};3port = {{ listen_port }};
44
5client_side_ssl = false;5client_side_ssl = {{ client_side_ssl }};
66
7log_level = 3;7log_level = 3;
88
99
=== added file 'tests/11-deploy-ssl'
--- tests/11-deploy-ssl 1970-01-01 00:00:00 +0000
+++ tests/11-deploy-ssl 2016-08-19 17:00:35 +0000
@@ -0,0 +1,78 @@
1#!/usr/bin/env python3
2
3import amulet
4import os
5import pprint
6import ssl
7import socket
8import unittest
9
10class TestSSLDeployment(unittest.TestCase):
11 @classmethod
12 def setUpClass(cls):
13 cls.deployment = amulet.Deployment()
14
15 cls.deployment.add('bip-ssl', charm='bip')
16
17 # configure ssl and new port
18 cls.deployment.configure('bip-ssl', {
19 'client_side_ssl': 'true',
20 'listen_port': '6697'
21 })
22
23 cls.deployment.expose('bip-ssl')
24
25 try:
26 cls.deployment.setup(timeout=900, cleanup=False)
27 cls.deployment.sentry.wait()
28 except amulet.helpers.TimeoutError:
29 amulet.raise_status(amulet.SKIP,
30 msg="Environment wasn't stood up in time")
31 except:
32 raise
33
34 cls.unit = cls.deployment.sentry['bip-ssl'][0]
35 cls.ipaddr = cls.unit.info['public-address']
36 cls.ports = cls.unit.info['open-ports']
37
38 def test_1_check_running(self):
39 output = self.unit.run('service bip status')
40 service_active = 'bip is running' in str(output)
41 if service_active:
42 print("Found running bip on %s" % self.ipaddr)
43 #amulet.raise_status(amulet.PASS, msg=message)
44 else:
45 message = "Failed to find running bip"
46 amulet.raise_status(amulet.FAIL, msg=message)
47
48 def test_2_check_ssl(self):
49 #print("bip.conf: {}".format(self.unit.run('cat /etc/bip.conf')))
50 s = socket.socket()
51 for p in self.ports:
52 (port, proto) = p.split("/")
53 crt_pem = ssl.get_server_certificate((self.ipaddr, int(port)))
54 #print(crt_pem)
55 cert_file = "_test-self-signed-cert.pem"
56 with open(cert_file, 'wt', encoding='utf-8') as f:
57 f.write(crt_pem)
58 sslsock = ssl.wrap_socket(s,
59 ca_certs=cert_file,
60 cert_reqs=ssl.CERT_REQUIRED)
61 try:
62 sslsock.connect((self.ipaddr, int(port)))
63 #print(repr(sslsock.getpeername()))
64 #print(sslsock.cipher())
65 #print(pprint.pformat(sslsock.getpeercert()))
66 message = "Connect to %s on port %s succeeded" % (
67 self.ipaddr, port)
68 #amulet.raise_status(amulet.PASS, msg=message)
69 print(message)
70 sslsock.close()
71 except socket.error as e:
72 message = "Connection to %s on port %s failed: %s" % (
73 self.ipaddr, port, e)
74 amulet.raise_status(amulet.FAIL, msg=message)
75 os.remove(cert_file)
76
77if __name__ == '__main__':
78 unittest.main()

Subscribers

People subscribed via source and target branches

to all changes: