Add x509keypair_cert_manager to store certs in DB

x509keypair_cert_manager stores certs in Magnum DB.

Change-Id: Id8106d7bd5c270679bc189fc0dc17bcbde367d73
Implements: blueprint  blueprint barbican-alternative-store
This commit is contained in:
Madhuri Kumari 2016-05-31 11:53:11 +05:30
parent aacc9c327c
commit 5f193f2e0e
11 changed files with 254 additions and 43 deletions

View File

@ -201,13 +201,7 @@ function create_magnum_conf {
if is_service_enabled barbican; then
iniset $MAGNUM_CONF certificates cert_manager_type "barbican"
else
MAGNUM_LOCAL_CERT_DIR=${MAGNUM_LOCAL_CERT_DIR:-/var/lib/magnum/certificates/}
if [[ ! -d $MAGNUM_LOCAL_CERT_DIR ]]; then
sudo mkdir -p $MAGNUM_LOCAL_CERT_DIR
sudo chown $STACK_USER $MAGNUM_LOCAL_CERT_DIR
fi
iniset $MAGNUM_CONF certificates storage_path "$MAGNUM_LOCAL_CERT_DIR"
iniset $MAGNUM_CONF certificates cert_manager_type "local"
iniset $MAGNUM_CONF certificates cert_manager_type "x509keypair"
fi
trustee_domain_id=$(get_or_create_domain magnum 'Owns users and projects created by magnum')

View File

@ -0,0 +1,103 @@
# Copyright (c) 2016 Intel, Inc.
#
# 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.
from oslo_config import cfg
from oslo_log import log as logging
from magnum.common.cert_manager import cert_manager
from magnum import objects
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
class Cert(cert_manager.Cert):
"""Representation of a Cert for Magnum DB storage."""
def __init__(self, certificate, private_key, intermediates=None,
private_key_passphrase=None):
self.certificate = certificate
self.intermediates = intermediates
self.private_key = private_key
self.private_key_passphrase = private_key_passphrase
def get_certificate(self):
return self.certificate
def get_intermediates(self):
return self.intermediates
def get_private_key(self):
return self.private_key
def get_private_key_passphrase(self):
return self.private_key_passphrase
class CertManager(cert_manager.CertManager):
"""Cert Manager Interface that stores data locally in Magnum db.
"""
@staticmethod
def store_cert(certificate, private_key, intermediates=None,
private_key_passphrase=None, context=None, **kwargs):
"""Stores (i.e., registers) a cert with the cert manager.
This method stores the specified cert to x509keypair model and returns
a UUID that can be used to retrieve it.
:param certificate: PEM encoded TLS certificate
:param private_key: private key for the supplied certificate
:param intermediates: ordered and concatenated intermediate certs
:param private_key_passphrase: optional passphrase for the supplied key
:returns: the UUID of the stored cert
"""
x509keypair = {'certificate': certificate, 'private_key': private_key,
'private_key_passphrase': private_key_passphrase,
'intermediates': intermediates,
'project_id': context.project_id,
'user_id': context.user_id}
x509keypair_obj = objects.X509KeyPair(context, **x509keypair)
x509keypair_obj.create()
return x509keypair_obj.uuid
@staticmethod
def get_cert(cert_ref, context=None, **kwargs):
"""Retrieves the specified cert.
:param cert_ref: the UUID of the cert to retrieve
:return: magnum.common.cert_manager.cert_manager.Cert
representation of the certificate data
"""
cert_data = dict()
x509keypair_obj = objects.X509KeyPair.get_by_uuid(context, cert_ref)
cert_data['certificate'] = x509keypair_obj.certificate
cert_data['private_key'] = x509keypair_obj.private_key
cert_data['private_key_passphrase'] = \
x509keypair_obj.private_key_passphrase
cert_data['intermediates'] = x509keypair_obj.intermediates
return Cert(**cert_data)
@staticmethod
def delete_cert(cert_ref, context=None, **kwargs):
"""Deletes the specified cert.
:param cert_ref: the UUID of the cert to delete
"""
x509keypair_obj = objects.X509KeyPair.get_by_uuid(context, cert_ref)
x509keypair_obj.destroy()

View File

@ -130,13 +130,13 @@ class Handler(object):
# Create trustee/trust and set them to bay
trust_manager.create_trustee_and_trust(osc, bay)
# Generate certificate and set the cert reference to bay
cert_manager.generate_certificates_to_bay(bay)
cert_manager.generate_certificates_to_bay(bay, context=context)
conductor_utils.notify_about_bay_operation(
context, taxonomy.ACTION_CREATE, taxonomy.OUTCOME_PENDING)
created_stack = _create_stack(context, osc, bay,
bay_create_timeout)
except Exception as e:
cert_manager.delete_certificates_from_bay(bay)
cert_manager.delete_certificates_from_bay(bay, context=context)
trust_manager.delete_trustee_and_trust(osc, context, bay)
conductor_utils.notify_about_bay_operation(
context, taxonomy.ACTION_CREATE, taxonomy.OUTCOME_FAILURE)
@ -212,7 +212,7 @@ class Handler(object):
' deletion.'), stack_id)
try:
trust_manager.delete_trustee_and_trust(osc, context, bay)
cert_manager.delete_certificates_from_bay(bay)
cert_manager.delete_certificates_from_bay(bay, context=context)
bay.destroy()
except exception.BayNotFound:
LOG.info(_LI('The bay %s has been deleted by others.'), uuid)
@ -227,7 +227,6 @@ class Handler(object):
conductor_utils.notify_about_bay_operation(
context, taxonomy.ACTION_DELETE, taxonomy.OUTCOME_FAILURE)
raise
self._poll_and_check(osc, bay)
return None
@ -319,7 +318,8 @@ class HeatPoller(object):
trust_manager.delete_trustee_and_trust(self.openstack_client,
self.context,
self.bay)
cert_manager.delete_certificates_from_bay(self.bay)
cert_manager.delete_certificates_from_bay(self.bay,
context=self.context)
self.bay.destroy()
except exception.BayNotFound:
LOG.info(_LI('The bay %s has been deleted by others.')

View File

@ -34,12 +34,13 @@ class Handler(object):
def sign_certificate(self, context, bay, certificate):
LOG.debug("Creating self signed x509 certificate")
signed_cert = cert_manager.sign_node_certificate(bay,
certificate.csr)
certificate.csr,
context=context)
certificate.pem = signed_cert
return certificate
def get_ca_certificate(self, context, bay):
ca_cert = cert_manager.get_bay_ca_certificate(bay)
ca_cert = cert_manager.get_bay_ca_certificate(bay, context=context)
certificate = objects.Certificate.from_object_bay(bay)
certificate.pem = ca_cert.get_certificate()
return certificate

View File

@ -29,7 +29,7 @@ CONDUCTOR_CLIENT_NAME = six.u('Magnum-Conductor')
LOG = logging.getLogger(__name__)
def _generate_ca_cert(issuer_name):
def _generate_ca_cert(issuer_name, context=None):
"""Generate and store ca_cert
:param issuer_name: CA subject name
@ -43,12 +43,13 @@ def _generate_ca_cert(issuer_name):
private_key=ca_cert['private_key'],
private_key_passphrase=ca_password,
name=issuer_name,
context=context,
)
LOG.debug('CA cert is created: %s', ca_cert_ref)
return ca_cert_ref, ca_cert, ca_password
def _generate_client_cert(issuer_name, ca_cert, ca_password):
def _generate_client_cert(issuer_name, ca_cert, ca_password, context=None):
"""Generate and store magnum_client_cert
:param issuer_name: CA subject name
@ -69,6 +70,7 @@ def _generate_client_cert(issuer_name, ca_cert, ca_password):
private_key=client_cert['private_key'],
private_key_passphrase=client_password,
name=CONDUCTOR_CLIENT_NAME,
context=context
)
LOG.debug('Magnum client cert is created: %s', magnum_cert_ref)
return magnum_cert_ref
@ -83,7 +85,7 @@ def _get_issuer_name(bay):
return issuer_name
def generate_certificates_to_bay(bay):
def generate_certificates_to_bay(bay, context=None):
"""Generate ca_cert and magnum client cert and set to bay
:param bay: The bay to set CA cert and magnum client cert
@ -94,10 +96,12 @@ def generate_certificates_to_bay(bay):
LOG.debug('Start to generate certificates: %s', issuer_name)
ca_cert_ref, ca_cert, ca_password = _generate_ca_cert(issuer_name)
ca_cert_ref, ca_cert, ca_password = _generate_ca_cert(issuer_name,
context=context)
magnum_cert_ref = _generate_client_cert(issuer_name,
ca_cert,
ca_password)
ca_password,
context=context)
bay.ca_cert_ref = ca_cert_ref
bay.magnum_cert_ref = magnum_cert_ref
@ -107,27 +111,29 @@ def generate_certificates_to_bay(bay):
raise exception.CertificatesToBayFailed(bay_uuid=bay.uuid)
def get_bay_ca_certificate(bay):
def get_bay_ca_certificate(bay, context=None):
ca_cert = cert_manager.get_backend().CertManager.get_cert(
bay.ca_cert_ref,
resource_ref=bay.uuid
resource_ref=bay.uuid,
context=context
)
return ca_cert
def get_bay_magnum_cert(bay):
def get_bay_magnum_cert(bay, context=None):
magnum_cert = cert_manager.get_backend().CertManager.get_cert(
bay.magnum_cert_ref,
resource_ref=bay.uuid
resource_ref=bay.uuid,
context=context
)
return magnum_cert
def create_client_files(bay):
ca_cert = get_bay_ca_certificate(bay)
magnum_cert = get_bay_magnum_cert(bay)
def create_client_files(bay, context=None):
ca_cert = get_bay_ca_certificate(bay, context)
magnum_cert = get_bay_magnum_cert(bay, context)
ca_cert_file = tempfile.NamedTemporaryFile()
ca_cert_file.write(ca_cert.get_certificate())
@ -144,10 +150,11 @@ def create_client_files(bay):
return ca_cert_file, magnum_key_file, magnum_cert_file
def sign_node_certificate(bay, csr):
def sign_node_certificate(bay, csr, context=None):
ca_cert = cert_manager.get_backend().CertManager.get_cert(
bay.ca_cert_ref,
resource_ref=bay.uuid
resource_ref=bay.uuid,
context=context
)
node_cert = x509.sign(csr,
@ -157,7 +164,7 @@ def sign_node_certificate(bay, csr):
return node_cert
def delete_certificates_from_bay(bay):
def delete_certificates_from_bay(bay, context=None):
"""Delete ca cert and magnum client cert from bay
:param bay: The bay which has certs
@ -167,6 +174,6 @@ def delete_certificates_from_bay(bay):
cert_ref = getattr(bay, cert_ref, None)
if cert_ref:
cert_manager.get_backend().CertManager.delete_cert(
cert_ref, resource_ref=bay.uuid)
cert_ref, resource_ref=bay.uuid, context=context)
except Exception:
LOG.warning(_LW("Deleting certs is failed for Bay %s"), bay.uuid)

View File

@ -47,7 +47,7 @@ class K8sAPI(apiv_api.ApivApi):
if bay.magnum_cert_ref:
(self.ca_file, self.key_file,
self.cert_file) = create_client_files(bay)
self.cert_file) = create_client_files(bay, context)
# build a connection with Kubernetes master
client = api_client.ApiClient(bay.api_address,

View File

@ -0,0 +1,96 @@
# Copyright 2016 Intel, Inc
#
# 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 mock
from magnum.common.cert_manager import x509keypair_cert_manager as x509_cm
from magnum.common import context
from magnum.tests import base
from magnum.tests.unit.db import base as db_base
from magnum.tests.unit.db import utils
class TestX509keypairCert(base.BaseTestCase):
def setUp(self):
self.certificate = "My Certificate"
self.intermediates = "My Intermediates"
self.private_key = "My Private Key"
self.private_key_passphrase = "My Private Key Passphrase"
super(TestX509keypairCert, self).setUp()
def test_x509keypair_cert(self):
# Create a cert
cert = x509_cm.Cert(
certificate=self.certificate,
intermediates=self.intermediates,
private_key=self.private_key,
private_key_passphrase=self.private_key_passphrase
)
# Validate the cert functions
self.assertEqual(self.certificate, cert.get_certificate())
self.assertEqual(self.intermediates, cert.get_intermediates())
self.assertEqual(self.private_key, cert.get_private_key())
self.assertEqual(self.private_key_passphrase,
cert.get_private_key_passphrase())
class TestX509keypairManager(db_base.DbTestCase):
def setUp(self):
self.certificate = "My Certificate"
self.intermediates = "My Intermediates"
self.private_key = "My Private Key"
self.private_key_passphrase = "My Private Key Passphrase"
self.context = context.make_admin_context()
super(TestX509keypairManager, self).setUp()
def test_store_cert(self):
x509keypair = utils.get_test_x509keypair()
with mock.patch.object(self.dbapi, 'create_x509keypair',
autospec=True) as mock_create_x509keypair:
mock_create_x509keypair.return_value = x509keypair
uuid = x509_cm.CertManager.store_cert(context=self.context,
**x509keypair)
self.assertEqual(uuid, '72625085-c507-4410-9b28-cd7cf1fbf1ad')
def test_get_cert(self):
x509keypair = utils.get_test_x509keypair(uuid='fake-uuid')
with mock.patch.object(self.dbapi, 'get_x509keypair_by_uuid',
autospec=True) as mock_get_x509keypair:
mock_get_x509keypair.return_value = x509keypair
cert_obj = x509_cm.CertManager.get_cert('fake-uuid',
context=self.context)
self.assertEqual(cert_obj.certificate, 'certificate')
self.assertEqual(cert_obj.private_key, 'private_key')
self.assertEqual(cert_obj.private_key_passphrase,
'private_key_passphrase')
self.assertEqual(cert_obj.intermediates, 'intermediates')
mock_get_x509keypair.assert_called_once_with(self.context,
'fake-uuid')
def test_delete_cert(self):
x509keypair = utils.get_test_x509keypair(uuid='fake-uuid')
with mock.patch.object(self.dbapi, 'get_x509keypair_by_uuid',
autospec=True) as mock_get_x509keypair:
mock_get_x509keypair.return_value = x509keypair
with mock.patch.object(self.dbapi, 'destroy_x509keypair',
autospec=True) as mock_destroy_x509keypair:
x509_cm.CertManager.delete_cert('fake-uuid',
context=self.context)
mock_get_x509keypair.assert_called_once_with(self.context,
'fake-uuid')
mock_destroy_x509keypair.assert_called_once_with('fake-uuid')

View File

@ -57,6 +57,7 @@ class CertManagerTestCase(base.BaseTestCase):
private_key=expected_ca_cert['private_key'],
private_key_passphrase=expected_ca_password,
name=expected_ca_name,
context=None
)
@mock.patch('magnum.common.x509.operations.generate_client_certificate')
@ -96,6 +97,7 @@ class CertManagerTestCase(base.BaseTestCase):
private_key=expected_cert['private_key'],
private_key_passphrase=expected_password,
name=expected_name,
context=None
)
def _test_generate_certificates(self,
@ -118,9 +120,11 @@ class CertManagerTestCase(base.BaseTestCase):
self.assertEqual(expected_ca_cert_ref, mock_bay.ca_cert_ref)
self.assertEqual(expected_cert_ref, mock_bay.magnum_cert_ref)
mock_generate_ca_cert.assert_called_once_with(expected_ca_name)
mock_generate_ca_cert.assert_called_once_with(expected_ca_name,
context=None)
mock_generate_client_cert.assert_called_once_with(
expected_ca_name, expected_ca_cert, expected_ca_password)
expected_ca_name, expected_ca_cert, expected_ca_password,
context=None)
@mock.patch('magnum.conductor.handlers.common.cert_manager.'
'_generate_client_cert')
@ -178,7 +182,8 @@ class CertManagerTestCase(base.BaseTestCase):
bay_ca_cert = cert_manager.sign_node_certificate(mock_bay, mock_csr)
self.CertManager.get_cert.assert_called_once_with(
mock_bay.ca_cert_ref, resource_ref=mock_bay.uuid)
mock_bay.ca_cert_ref, resource_ref=mock_bay.uuid,
context=None)
mock_x509_sign.assert_called_once_with(mock_csr, mock_bay.name,
mock.sentinel.priv_key,
passphrase)
@ -200,7 +205,7 @@ class CertManagerTestCase(base.BaseTestCase):
bay_ca_cert = cert_manager.sign_node_certificate(mock_bay, mock_csr)
self.CertManager.get_cert.assert_called_once_with(
mock_bay.ca_cert_ref, resource_ref=mock_bay.uuid)
mock_bay.ca_cert_ref, resource_ref=mock_bay.uuid, context=None)
mock_x509_sign.assert_called_once_with(mock_csr, mock_bay.uuid,
mock.sentinel.priv_key,
passphrase)
@ -215,7 +220,7 @@ class CertManagerTestCase(base.BaseTestCase):
bay_ca_cert = cert_manager.get_bay_ca_certificate(mock_bay)
self.CertManager.get_cert.assert_called_once_with(
mock_bay.ca_cert_ref, resource_ref=mock_bay.uuid)
mock_bay.ca_cert_ref, resource_ref=mock_bay.uuid, context=None)
self.assertEqual(mock_ca_cert, bay_ca_cert)
def test_delete_certtificate(self):
@ -229,9 +234,11 @@ class CertManagerTestCase(base.BaseTestCase):
cert_manager.delete_certificates_from_bay(mock_bay)
mock_delete_cert.assert_any_call(expected_ca_cert_ref,
resource_ref=mock_bay.uuid)
resource_ref=mock_bay.uuid,
context=None)
mock_delete_cert.assert_any_call(expected_cert_ref,
resource_ref=mock_bay.uuid)
resource_ref=mock_bay.uuid,
context=None)
def test_delete_certtificate_if_raise_error(self):
mock_delete_cert = self.CertManager.delete_cert
@ -245,9 +252,11 @@ class CertManagerTestCase(base.BaseTestCase):
cert_manager.delete_certificates_from_bay(mock_bay)
mock_delete_cert.assert_any_call(expected_ca_cert_ref,
resource_ref=mock_bay.uuid)
resource_ref=mock_bay.uuid,
context=None)
mock_delete_cert.assert_any_call(expected_cert_ref,
resource_ref=mock_bay.uuid)
resource_ref=mock_bay.uuid,
context=None)
def test_delete_certtificate_without_cert_ref(self):
mock_delete_cert = self.CertManager.delete_cert

View File

@ -216,7 +216,7 @@ class TestHandler(db_base.DbTestCase):
mock.sentinel.osc,
self.bay, timeout)
mock_cert_manager.generate_certificates_to_bay.assert_called_once_with(
self.bay)
self.bay, context=self.context)
self.assertEqual(bay_status.CREATE_IN_PROGRESS, bay.status)
mock_trust_manager.create_trustee_and_trust.assert_called_once_with(
osc, self.bay)
@ -241,7 +241,7 @@ class TestHandler(db_base.DbTestCase):
gctb = mock_cert_manager.generate_certificates_to_bay
if is_create_cert_called:
gctb.assert_called_once_with(self.bay)
gctb.assert_called_once_with(self.bay, context=self.context)
else:
gctb.assert_not_called()
ctat = mock_trust_manager.create_trustee_and_trust

View File

@ -35,7 +35,7 @@ class TestSignConductor(base.TestCase):
mock_certificate)
mock_cert_manager.sign_node_certificate.assert_called_once_with(
mock_bay, 'fake-csr'
mock_bay, 'fake-csr', context=self.context
)
self.assertEqual('fake-pem', actual_cert.pem)

View File

@ -67,6 +67,7 @@ magnum.database.migration_backend =
magnum.cert_manager.backend =
barbican = magnum.common.cert_manager.barbican_cert_manager
local = magnum.common.cert_manager.local_cert_manager
x509keypair = magnum.common.cert_manager.x509keypair_cert_manager
tempest.test_plugins =
magnum_tests = magnum.tests.functional.tempest_tests.plugin:MagnumTempestPlugin