Generate keystone PKI into JSON file

For devtest scripts it's more useful to generate keystone PKI
into JSON Heat env file directly instead of reading PKI files
and adding them into JSON file manually.

If keystone key/certs already exist in JSON file, they are not
overwritten.

Change-Id: Iad9c670dba0f57219f47792f44d60341ab58cdb3
This commit is contained in:
Jan Provaznik 2014-07-03 06:28:05 -04:00
parent 9102604e74
commit 78e62e6117
4 changed files with 157 additions and 6 deletions

View File

@ -70,3 +70,49 @@ Where /tmp/one-node contains::
"cpu": "1"
}
]
----------------------------------------------------------
Generating keys and certificates for use with Keystone PKI
----------------------------------------------------------
The generate-keystone-pki line utility generates keys and certificates
which Keystone uses for signing authentication tokens.
- Keys and certificates can be generated into separate files::
generate-keystone-pki /tmp/certificates
That creates four files with signing and CA keys and certificates in
/tmp/certificates directory.
- Key and certificates can be generated into heat environment file::
generate-keystone-pki -j overcloud-env.json
That adds following values into overcloud-env.json file::
{
"parameters": {
"KeystoneSigningKey": "some_key",
"KeystoneSigningCertificate": "some_cert",
"KeystoneCACertificate": "some_cert"
}
}
CA key is not added because this file is not needed by Keystone PKI.
- Key and certificates can be generated into os-apply-config metadata file::
generate-keystone-pki -s -j local.json
This adds following values into local.json file::
{
"keystone": {
"signing_certificate": "some_cert",
"signing_key": "some_key",
"ca_certificate": "some_cert"
}
}
CA key is not added because this file is not needed by Keystone PKI.

View File

@ -28,6 +28,9 @@ def parse_args():
signing_cert.pem - certificate for verifying token validity, the
certificate itself is verifiable by ca_cert.pem
Alternatively write generated key/certs into <heatenv> JSON file
or into an os-apply-config metadata file for use without Heat.
ca_key.pem doesn't have to (shouldn't) be uploaded to Keystone nodes.
""")
@ -35,14 +38,24 @@ def parse_args():
description=description,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
'directory',
metavar='<directory>',
help='directory where keys/certs will be generated',
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-d', '--directory', dest='directory',
help='directory where keys/certs will be generated')
group.add_argument('-j', '--heatenv', dest='heatenv',
help='write signing key/cert and CA cert into JSON '
'Heat environment file, CA key is omitted')
parser.add_argument('-s', '--seed', action='store_true',
help='JSON file for seed machine has different '
'structure (for seed machine we update directly '
'heat metadata file injected into image). '
'Different key/certs names and different '
'parent node are used (default: false)')
return parser.parse_args()
def main():
args = parse_args()
keystone_pki.create_and_write_ca_and_signing_pairs(args.directory)
if args.heatenv:
keystone_pki.generate_certs_into_json(args.heatenv, args.seed)
else:
keystone_pki.create_and_write_ca_and_signing_pairs(args.directory)

View File

@ -15,6 +15,7 @@
import logging
import os
from os import path
import simplejson
import stat
from OpenSSL import crypto
@ -136,6 +137,57 @@ def create_and_write_ca_and_signing_pairs(directory):
_write_pki_file(path.join(directory, 'signing_cert.pem'), signing_cert_pem)
def generate_certs_into_json(jsonfile, seed):
"""Create and write out CA certificate and signing certificate/key.
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.
: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:
all_data = simplejson.load(json_fd)
else:
all_data = {}
if seed:
parent = 'keystone'
ca_cert_name = 'ca_certificate'
signing_key_name = 'signing_key'
signing_cert_name = 'signing_certificate'
else:
parent = 'parameters'
ca_cert_name = 'KeystoneCACertificate'
signing_key_name = 'KeystoneSigningKey'
signing_cert_name = 'KeystoneSigningCertificate'
if parent not in all_data:
all_data[parent] = {}
parent_node = all_data[parent]
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)
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:
simplejson.dump(all_data, json_fd, sort_keys=True)
LOG.debug("Wrote key/certs into '%s'.", path.abspath(jsonfile))
else:
LOG.info("Key/certs are already present in '%s', skipping.",
path.abspath(jsonfile))
def _write_pki_file(file_path, contents):
with open(file_path, 'w') as f:
f.write(contents)

View File

@ -98,3 +98,43 @@ class KeystonePKITest(base.TestCase):
mock.call('mock_signing_key'),
mock.call('mock_signing_cert'),
])
@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.simplejson.dump')
def test_generate_certs_into_json(
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 = False
keystone_pki.generate_certs_into_json('/jsonfile', False)
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.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.simplejson.load')
@mock.patch('os_cloud_config.keystone_pki.simplejson.dump')
def test_generate_certs_into_json_with_existing_certs(
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 = {
'KeystoneCACertificate': 'mock_ca_cert',
'KeystoneSigningKey': 'mock_signing_key',
'KeystoneSigningCertificate': 'mock_signing_cert'
}
keystone_pki.generate_certs_into_json('/jsonfile', False)
mock_json_dump.assert_not_called()