From d1a22691865f999212097dff2d2ac49627479a1e Mon Sep 17 00:00:00 2001 From: Steve Kowalik Date: Fri, 10 Oct 2014 17:01:30 +1100 Subject: [PATCH] Revert "Make keystone_pki less keystone specific" This breaks the world due to parameters changing. This reverts commit e5f844d126d6dbe6ad0b8a4dae5c2b314f5918a0. Change-Id: Iad76855a02779018c2e2d3b0b65790ab9c7f7c99 --- os_cloud_config/cmd/generate_keystone_pki.py | 6 +- os_cloud_config/cmd/generate_ssl_cert.py | 49 ------- .../cmd/tests/test_generate_keystone_pki.py | 6 +- .../{ssl_pki.py => keystone_pki.py} | 130 +++++------------- .../{test_ssl_pki.py => test_keystone_pki.py} | 112 +++++---------- setup.cfg | 1 - 6 files changed, 75 insertions(+), 229 deletions(-) delete mode 100644 os_cloud_config/cmd/generate_ssl_cert.py rename os_cloud_config/{ssl_pki.py => keystone_pki.py} (61%) rename os_cloud_config/tests/{test_ssl_pki.py => test_keystone_pki.py} (50%) diff --git a/os_cloud_config/cmd/generate_keystone_pki.py b/os_cloud_config/cmd/generate_keystone_pki.py index c406599..db7ac16 100644 --- a/os_cloud_config/cmd/generate_keystone_pki.py +++ b/os_cloud_config/cmd/generate_keystone_pki.py @@ -15,7 +15,7 @@ import argparse import textwrap from os_cloud_config.cmd.utils import environment -from os_cloud_config import ssl_pki +from os_cloud_config import keystone_pki def parse_args(): @@ -60,6 +60,6 @@ def main(): environment._configure_logging(args) if args.heatenv: - ssl_pki.generate_cert_into_json(args.heatenv, "keystone") + keystone_pki.generate_certs_into_json(args.heatenv, args.seed) else: - ssl_pki.create_and_write_ca_and_signing_pairs(args.directory) + keystone_pki.create_and_write_ca_and_signing_pairs(args.directory) diff --git a/os_cloud_config/cmd/generate_ssl_cert.py b/os_cloud_config/cmd/generate_ssl_cert.py deleted file mode 100644 index 66ef916..0000000 --- a/os_cloud_config/cmd/generate_ssl_cert.py +++ /dev/null @@ -1,49 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import argparse -import textwrap - -from os_cloud_config import ssl_pki - - -def parse_args(): - description = textwrap.dedent(""" - Generate and sign certificate with CA - - This script generates a certificate and signes certificate using the CA - in the heat environment. If no CA is in the heat environment then a new - CA will be generated. The resulting certificate and CA (if one is made) - are added to the heat environment. - """) - - parser = argparse.ArgumentParser( - description=description, - formatter_class=argparse.RawDescriptionHelpFormatter, - ) - parser.add_argument( - 'heat_env', - metavar='', - help='path to JSON heat environment file' - ) - parser.add_argument( - 'name', - metavar='', - help='name for key/certificate pair', - ) - return parser.parse_args() - - -def main(): - args = parse_args() - ssl_pki.generate_cert_into_json(args.heat_env, args.name, args.overwrite) diff --git a/os_cloud_config/cmd/tests/test_generate_keystone_pki.py b/os_cloud_config/cmd/tests/test_generate_keystone_pki.py index 910b09b..f49d9bd 100644 --- a/os_cloud_config/cmd/tests/test_generate_keystone_pki.py +++ b/os_cloud_config/cmd/tests/test_generate_keystone_pki.py @@ -23,14 +23,14 @@ from os_cloud_config.tests import base class GenerateKeystonePKITest(base.TestCase): - @mock.patch('os_cloud_config.ssl_pki.generate_cert_into_json') + @mock.patch('os_cloud_config.keystone_pki.generate_certs_into_json') @mock.patch.object(sys, 'argv', ['generate-keystone-pki', '-j', 'foo.json', '-s']) def test_with_heatenv(self, generate_mock): generate_keystone_pki.main() - generate_mock.assert_called_once_with('foo.json', 'keystone') + generate_mock.assert_called_once_with('foo.json', True) - @mock.patch('os_cloud_config.ssl_pki.create_and_write_ca_' + @mock.patch('os_cloud_config.keystone_pki.create_and_write_ca_' 'and_signing_pairs') @mock.patch.object(sys, 'argv', ['generate-keystone-pki', '-d', 'bar']) def test_without_heatenv(self, create_mock): diff --git a/os_cloud_config/ssl_pki.py b/os_cloud_config/keystone_pki.py similarity index 61% rename from os_cloud_config/ssl_pki.py rename to os_cloud_config/keystone_pki.py index fa9b632..c3d31a5 100644 --- a/os_cloud_config/ssl_pki.py +++ b/os_cloud_config/keystone_pki.py @@ -54,7 +54,7 @@ def create_ca_pair(cert_serial=1): subject.ST = 'Unset' subject.L = 'Unset' subject.O = 'Unset' - subject.CN = 'os-cloud-config CA' + subject.CN = 'Keystone CA' ca_cert.gmtime_adj_notBefore(0) ca_cert.gmtime_adj_notAfter(60 * 60 * 24 * CA_CERT_DAYS) ca_cert.set_issuer(subject) @@ -103,7 +103,7 @@ def create_signing_pair(ca_key_pem, ca_cert_pem, cert_serial=2): subject.ST = 'Unset' subject.L = 'Unset' subject.O = 'Unset' - subject.CN = 'os-cloud-config Signing' + subject.CN = 'Keystone Signing' signing_cert.gmtime_adj_notBefore(0) signing_cert.gmtime_adj_notAfter(60 * 60 * 24 * SIGNING_CERT_DAYS) signing_cert.set_issuer(ca_cert.get_subject()) @@ -137,54 +137,18 @@ def create_and_write_ca_and_signing_pairs(directory): _write_pki_file(path.join(directory, 'signing_cert.pem'), signing_cert_pem) -def generate_cert_into_json(jsonfile, name, auto_gen_ca=True, - overwrite=False): - """Create and write out an SSL certificate. +def generate_certs_into_json(jsonfile, seed): + """Create and write out CA certificate and signing certificate/key. - Create an ssl certificate, sign it with a CA, and output the certificate - to which is a heat JSON environment. If the parameters - property exists in destination, then the following properties are added: - { - "parameters": { - "SslCertificate": PEM_DATA, - "SslCertificateKey": PEM_DATA - } - } + Generate CA certificate, signing certificate and signing key and + add them into a JSON file. If key/certs already exist in JSON file, no + change is done. - The CA certificate and key lives in the parameters "CaSslCertificate" and - "CaSslCertificateKey". If these parameters are not defined then a new CA - is created and these properties are added. - - If no "parameters" property exists then the following properties are - added: - - { - "": { - "ssl" : { - "certificate": PEM DATA, - "certificate_key: PEM_DATA - } - } - } - - The CA certificate and key in this case are: - - { - "ssl": { - "ca_certificate": PEM_DATA, - "ca_certificate_key": PEM_DATA - } - } - - :param jsonfile: Destination to write certificate and possible CA to. - :type jsonfile: string - :param cert_serial: Serial for the certificate. If None then this is - automatically determined. - :type cert_serial: integer - :param name: name for the certificate. - :type name: string - :param overwrite: overwrite certificate if it already exists - :type overwrite: boolean + :param jsonfile: JSON file where certs and key will be written + :type jsonfile: string + :param seed: JSON file for seed machine has different structure. Different + key/certs names and different parent node are used + :type seed: boolean """ if os.path.isfile(jsonfile): with open(jsonfile) as json_fd: @@ -192,60 +156,30 @@ def generate_cert_into_json(jsonfile, name, auto_gen_ca=True, else: all_data = {} - parent = all_data.get("parameters") - ca_parent = None - if parent is not None: - cert_dest = "%sSslCertificate" % name - cert_key_dest = "%sSslCertificateKey" % name - ca_dest = "CaSslCertificate" - ca_key_dest = "CaSslCertificateKey" - cert_count_dest = "SslCertificatCount" - ca_parent = parent + if seed: + parent = 'keystone' + ca_cert_name = 'ca_certificate' + signing_key_name = 'signing_key' + signing_cert_name = 'signing_certificate' else: - cert_dest = "certificate" - cert_key_dest = "certificate_key" - ca_dest = "ca_certificate" - ca_key_dest = "ca_certificate_key" - cert_count_dest = "certificate_count" + parent = 'parameters' + ca_cert_name = 'KeystoneCACertificate' + signing_key_name = 'KeystoneSigningKey' + signing_cert_name = 'KeystoneSigningCertificate' - # Make parent be a node in all_data - svc = all_data.get(name, {}) - parent = all_data.get("ssl", {}) - svc["ssl"] = parent - ca_parent = all_data.get("ssl", {}) + if parent not in all_data: + all_data[parent] = {} + parent_node = all_data[parent] - all_data[name] = svc - all_data["ssl"] = ca_parent - - # If we only have one of cert or key this is an error if not overwriting - if (cert_dest not in parent and cert_key_dest in parent or - cert_dest in parent and cert_key_dest not in parent) and \ - not overwrite: - raise ValueError("Only one of certificate or key defined.") - - if cert_dest not in parent or overwrite: - # Check that we have both a CA cert and key or neither - if (ca_dest not in parent and ca_key_dest in parent) or \ - (ca_dest in parent and ca_key_dest not in parent): - raise ValueError("Only one of CA certificate or key defined.") - - # Gen CA - if ca_dest not in parent: - ca_key_pem, ca_cert_pem = create_ca_pair() - ca_parent[ca_key_dest] = ca_key_pem - ca_parent[ca_dest] = ca_cert_pem - - # Gen cert - cert_serial = ca_parent.get(cert_count_dest, 0) - cert_serial += 1 + if not (ca_cert_name in parent_node and + signing_key_name in parent_node and + signing_cert_name in parent_node): + ca_key_pem, ca_cert_pem = create_ca_pair() signing_key_pem, signing_cert_pem = create_signing_pair(ca_key_pem, - ca_cert_pem, - cert_serial) - ca_parent[cert_count_dest] = cert_serial - parent[cert_key_dest] = signing_key_pem - parent[cert_dest] = signing_cert_pem - - # Write out env + ca_cert_pem) + parent_node.update({ca_cert_name: ca_cert_pem, + signing_key_name: signing_key_pem, + signing_cert_name: signing_cert_pem}) with open(jsonfile, 'w') as json_fd: json.dump(all_data, json_fd, sort_keys=True) LOG.debug("Wrote key/certs into '%s'.", path.abspath(jsonfile)) diff --git a/os_cloud_config/tests/test_ssl_pki.py b/os_cloud_config/tests/test_keystone_pki.py similarity index 50% rename from os_cloud_config/tests/test_ssl_pki.py rename to os_cloud_config/tests/test_keystone_pki.py index 0808902..5f81a67 100644 --- a/os_cloud_config/tests/test_ssl_pki.py +++ b/os_cloud_config/tests/test_keystone_pki.py @@ -17,18 +17,18 @@ import stat import mock from OpenSSL import crypto -from os_cloud_config import ssl_pki +from os_cloud_config import keystone_pki from os_cloud_config.tests import base -class SslPKITest(base.TestCase): +class KeystonePKITest(base.TestCase): def test_create_ca_and_signing_pairs(self): # use one common test to avoid generating CA pair twice # do not mock out pyOpenSSL, test generated keys/certs # create CA pair - ca_key_pem, ca_cert_pem = ssl_pki.create_ca_pair() + ca_key_pem, ca_cert_pem = keystone_pki.create_ca_pair() ca_key = crypto.load_privatekey(crypto.FILETYPE_PEM, ca_key_pem) ca_cert = crypto.load_certificate(crypto.FILETYPE_PEM, ca_cert_pem) @@ -38,11 +38,11 @@ class SslPKITest(base.TestCase): # check CA cert properties self.assertFalse(ca_cert.has_expired()) - self.assertEqual('os-cloud-config CA', ca_cert.get_issuer().CN) - self.assertEqual('os-cloud-config CA', ca_cert.get_subject().CN) + self.assertEqual('Keystone CA', ca_cert.get_issuer().CN) + self.assertEqual('Keystone CA', ca_cert.get_subject().CN) # create signing pair - signing_key_pem, signing_cert_pem = ssl_pki.create_signing_pair( + signing_key_pem, signing_cert_pem = keystone_pki.create_signing_pair( ca_key_pem, ca_cert_pem) signing_key = crypto.load_privatekey(crypto.FILETYPE_PEM, signing_key_pem) @@ -55,23 +55,22 @@ class SslPKITest(base.TestCase): # check signing cert properties self.assertFalse(signing_cert.has_expired()) - self.assertEqual('os-cloud-config CA', signing_cert.get_issuer().CN) - self.assertEqual('os-cloud-config Signing', - signing_cert.get_subject().CN) + self.assertEqual('Keystone CA', signing_cert.get_issuer().CN) + self.assertEqual('Keystone Signing', signing_cert.get_subject().CN) # pyOpenSSL currenty cannot verify a cert against a CA cert - @mock.patch('os_cloud_config.ssl_pki.os.chmod', create=True) - @mock.patch('os_cloud_config.ssl_pki.os.mkdir', create=True) - @mock.patch('os_cloud_config.ssl_pki.path.isdir', create=True) - @mock.patch('os_cloud_config.ssl_pki.create_ca_pair') - @mock.patch('os_cloud_config.ssl_pki.create_signing_pair') - @mock.patch('os_cloud_config.ssl_pki.open', create=True) + @mock.patch('os_cloud_config.keystone_pki.os.chmod', create=True) + @mock.patch('os_cloud_config.keystone_pki.os.mkdir', create=True) + @mock.patch('os_cloud_config.keystone_pki.path.isdir', create=True) + @mock.patch('os_cloud_config.keystone_pki.create_ca_pair') + @mock.patch('os_cloud_config.keystone_pki.create_signing_pair') + @mock.patch('os_cloud_config.keystone_pki.open', create=True) def test_create_and_write_ca_and_signing_pairs( self, open_, create_signing, create_ca, isdir, mkdir, chmod): create_ca.return_value = ('mock_ca_key', 'mock_ca_cert') create_signing.return_value = ('mock_signing_key', 'mock_signing_cert') isdir.return_value = False - ssl_pki.create_and_write_ca_and_signing_pairs('/fake_dir') + keystone_pki.create_and_write_ca_and_signing_pairs('/fake_dir') mkdir.assert_called_with('/fake_dir') chmod.assert_has_calls([ @@ -100,66 +99,31 @@ class SslPKITest(base.TestCase): mock.call('mock_signing_cert'), ]) - @mock.patch('os_cloud_config.ssl_pki.path.isfile', create=True) - @mock.patch('os_cloud_config.ssl_pki.create_ca_pair') - @mock.patch('os_cloud_config.ssl_pki.create_signing_pair') - @mock.patch('os_cloud_config.ssl_pki.open', create=True) - @mock.patch('os_cloud_config.ssl_pki.json.load') - @mock.patch('os_cloud_config.ssl_pki.json.dump') + @mock.patch('os_cloud_config.keystone_pki.path.isfile', create=True) + @mock.patch('os_cloud_config.keystone_pki.create_ca_pair') + @mock.patch('os_cloud_config.keystone_pki.create_signing_pair') + @mock.patch('os_cloud_config.keystone_pki.open', create=True) + @mock.patch('os_cloud_config.keystone_pki.json.dump') def test_generate_certs_into_json( - self, mock_json_dump, mock_json_load, open_, create_signing, - create_ca, isfile): + self, mock_json, open_, create_signing, create_ca, isfile): create_ca.return_value = ('mock_ca_key', 'mock_ca_cert') create_signing.return_value = ('mock_signing_key', 'mock_signing_cert') - isfile.return_value = True - mock_json_load.return_value = { - "Keystone": { - "mock_property": "mock_value" - } - } + isfile.return_value = False - ssl_pki.generate_cert_into_json('/jsonfile', "Keystone") + keystone_pki.generate_certs_into_json('/jsonfile', False) - ssl = mock_json_dump.call_args[0][0]["ssl"] - keystone = mock_json_dump.call_args[0][0]["Keystone"] - self.assertEqual(keystone["mock_property"], "mock_value") - self.assertEqual(keystone["ssl"]["certificate"], "mock_signing_cert") - self.assertEqual(keystone["ssl"]["certificate_key"], - "mock_signing_key") - self.assertEqual(ssl["ca_certificate"], "mock_ca_cert") - self.assertEqual(ssl["ca_certificate_key"], "mock_ca_key") - - @mock.patch('os_cloud_config.ssl_pki.path.isfile', create=True) - @mock.patch('os_cloud_config.ssl_pki.create_ca_pair') - @mock.patch('os_cloud_config.ssl_pki.create_signing_pair') - @mock.patch('os_cloud_config.ssl_pki.open', create=True) - @mock.patch('os_cloud_config.ssl_pki.json.load') - @mock.patch('os_cloud_config.ssl_pki.json.dump') - def test_generate_cert_into_json_notseed( - self, mock_json_dump, mock_json_load, open_, create_signing, - create_ca, isfile): - create_ca.return_value = ('mock_ca_key', 'mock_ca_cert') - create_signing.return_value = ('mock_signing_key', 'mock_signing_cert') - isfile.return_value = True - mock_json_load.return_value = { - "parameters": {} - } - - ssl_pki.generate_cert_into_json('/jsonfile', "Keystone") - - params = mock_json_dump.call_args[0][0]['parameters'] - self.assertEqual(params['CaSslCertificate'], 'mock_ca_cert') - self.assertEqual(params['KeystoneSslCertificateKey'], - 'mock_signing_key') - self.assertEqual(params['KeystoneSslCertificate'], + params = mock_json.call_args[0][0]['parameters'] + self.assertEqual(params['KeystoneCACertificate'], 'mock_ca_cert') + self.assertEqual(params['KeystoneSigningKey'], 'mock_signing_key') + self.assertEqual(params['KeystoneSigningCertificate'], 'mock_signing_cert') - @mock.patch('os_cloud_config.ssl_pki.path.isfile', create=True) - @mock.patch('os_cloud_config.ssl_pki.create_ca_pair') - @mock.patch('os_cloud_config.ssl_pki.create_signing_pair') - @mock.patch('os_cloud_config.ssl_pki.open', create=True) - @mock.patch('os_cloud_config.ssl_pki.json.load') - @mock.patch('os_cloud_config.ssl_pki.json.dump') + @mock.patch('os_cloud_config.keystone_pki.path.isfile', create=True) + @mock.patch('os_cloud_config.keystone_pki.create_ca_pair') + @mock.patch('os_cloud_config.keystone_pki.create_signing_pair') + @mock.patch('os_cloud_config.keystone_pki.open', create=True) + @mock.patch('os_cloud_config.keystone_pki.json.load') + @mock.patch('os_cloud_config.keystone_pki.json.dump') def test_generate_certs_into_json_with_existing_certs( self, mock_json_dump, mock_json_load, open_, create_signing, create_ca, isfile): @@ -167,12 +131,10 @@ class SslPKITest(base.TestCase): create_signing.return_value = ('mock_signing_key', 'mock_signing_cert') isfile.return_value = True mock_json_load.return_value = { - "parameters": { - 'KeystoneCACertificate': 'mock_ca_cert', - 'KeystoneSigningKey': 'mock_signing_key', - 'KeystoneSigningCertificate': 'mock_signing_cert' - } + 'KeystoneCACertificate': 'mock_ca_cert', + 'KeystoneSigningKey': 'mock_signing_key', + 'KeystoneSigningCertificate': 'mock_signing_cert' } - ssl_pki.generate_cert_into_json('/jsonfile', "keystone") + keystone_pki.generate_certs_into_json('/jsonfile', False) mock_json_dump.assert_not_called() diff --git a/setup.cfg b/setup.cfg index 34cc21e..3b3d441 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,7 +26,6 @@ packages = [entry_points] console_scripts = generate-keystone-pki = os_cloud_config.cmd.generate_keystone_pki:main - generate-ssl-cert = os_cloud_config.cmd.generate_ssl_cert:main init-keystone = os_cloud_config.cmd.init_keystone:main register-nodes = os_cloud_config.cmd.register_nodes:main setup-endpoints = os_cloud_config.cmd.setup_endpoints:main