Revert "Make keystone_pki less keystone specific"

This breaks the world due to parameters changing.

This reverts commit e5f844d126.

Change-Id: Iad76855a02779018c2e2d3b0b65790ab9c7f7c99
This commit is contained in:
Steve Kowalik 2014-10-10 17:01:30 +11:00
parent dd399dbc91
commit d1a2269186
6 changed files with 75 additions and 229 deletions

View File

@ -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)

View File

@ -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='<heat_env>',
help='path to JSON heat environment file'
)
parser.add_argument(
'name',
metavar='<name>',
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)

View File

@ -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):

View File

@ -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 <jsonfile> which is a heat JSON environment. If the parameters
property exists in destination, then the following properties are added:
{
"parameters": {
"<name>SslCertificate": PEM_DATA,
"<name>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:
{
"<name>": {
"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))

View File

@ -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()

View File

@ -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