Generate certs while creating bay

Bay needs two certificates for CA and magnum conductor to support TLS
between Kubernetes API server to Magnum. This patch generates these
certs while creating bay.

Change-Id: Ide13a0a5dbb43f2acc085283859edf6373106d7f
Partial-Implements: blueprint magnum-as-a-ca
This commit is contained in:
OTSUKA, Yuanying 2015-08-19 16:05:22 +09:00
parent 0fc4ff0002
commit d6916e8bb1
5 changed files with 219 additions and 1 deletions

View File

@ -179,6 +179,15 @@ function create_magnum_conf {
if is_service_enabled ceilometer; then if is_service_enabled ceilometer; then
iniset $MAGNUM_CONF DEFAULT notification_driver "messaging" iniset $MAGNUM_CONF DEFAULT notification_driver "messaging"
fi fi
# Temporary work until barbican is available
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"
} }
function update_heat_policy { function update_heat_policy {

View File

@ -21,6 +21,7 @@ from oslo_service import loopingcall
from magnum.common import clients from magnum.common import clients
from magnum.common import exception from magnum.common import exception
from magnum.common import short_id from magnum.common import short_id
from magnum.conductor.handlers.common import cert_manager
from magnum.conductor import scale_manager from magnum.conductor import scale_manager
from magnum.conductor.template_definition import TemplateDefinition as TDef from magnum.conductor.template_definition import TemplateDefinition as TDef
from magnum.conductor import utils as conductor_utils from magnum.conductor import utils as conductor_utils
@ -130,6 +131,8 @@ class Handler(object):
osc = clients.OpenStackClients(context) osc = clients.OpenStackClients(context)
try: try:
# Generate certificate and set the cert reference to bay
cert_manager.generate_certificates_to_bay(bay)
created_stack = _create_stack(context, osc, bay, created_stack = _create_stack(context, osc, bay,
bay_create_timeout) bay_create_timeout)
except exc.HTTPBadRequest as e: except exc.HTTPBadRequest as e:

View File

@ -0,0 +1,85 @@
# Copyright 2015 NEC Corporation. All rights reserved.
#
# 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_log import log as logging
import six
from magnum.common import cert_manager
from magnum.common import short_id
from magnum.common.x509 import operations as x509
CONDUCTOR_CLIENT_NAME = six.u('Magnum-Conductor')
LOG = logging.getLogger(__name__)
def _generate_ca_cert(issuer_name):
"""Generate and store ca_cert
:param issuer_name: CA subject name
:returns: CA cert uuid and CA cert, CA private key password
"""
ca_password = short_id.generate_id()
ca_cert = x509.generate_ca_certificate(issuer_name,
encryption_password=ca_password)
ca_cert_ref = cert_manager.get_backend().CertManager.store_cert(
certificate=ca_cert['certificate'],
private_key=ca_cert['private_key'],
private_key_passphrase=ca_password,
name=issuer_name,
)
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):
"""Generate and store magnum_client_cert
:param issuer_name: CA subject name
:param ca_cert: CA certificate
:param ca_password: CA private key password
:returns: Magnum client cert uuid
"""
client_password = short_id.generate_id()
client_cert = x509.generate_client_certificate(
issuer_name,
CONDUCTOR_CLIENT_NAME,
ca_cert['private_key'],
encryption_password=client_password,
ca_key_password=ca_password,
)
magnum_cert_ref = cert_manager.get_backend().CertManager.store_cert(
certificate=client_cert['certificate'],
private_key=client_cert['private_key'],
private_key_passphrase=client_password,
name=CONDUCTOR_CLIENT_NAME,
)
LOG.debug('Magnum client cert is created: %s' % magnum_cert_ref)
return magnum_cert_ref
def generate_certificates_to_bay(bay):
"""Generate ca_cert and magnum client cert and set to bay
:param bay: The bay to set CA cert and magnum client cert
:returns: CA cert uuid and magnum client cert uuid
"""
issuer_name = bay.name
LOG.debug('Start to generate certificates: %s' % issuer_name)
ca_cert_ref, ca_cert, ca_password = _generate_ca_cert(issuer_name)
magnum_cert_ref = _generate_client_cert(issuer_name, ca_cert, ca_password)
bay.ca_cert_ref = ca_cert_ref
bay.magnum_cert_ref = magnum_cert_ref

View File

@ -0,0 +1,117 @@
# Copyright 2015 NEC Corporation. All rights reserved.
#
# 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 get_backend
from magnum.conductor.handlers.common import cert_manager
from magnum.tests import base
class CertManagerTestCase(base.BaseTestCase):
@mock.patch('magnum.common.x509.operations.generate_ca_certificate')
@mock.patch('magnum.common.short_id.generate_id')
def test_generate_ca_cert(self, mock_generate_id, mock_generate_ca_cert):
expected_ca_name = 'ca-name'
expected_ca_password = 'password'
expected_ca_cert = {
'private_key': 'private_key', 'certificate': 'certificate'}
expected_ca_cert_ref = 'ca_cert_ref'
mock_generate_id.return_value = expected_ca_password
mock_generate_ca_cert.return_value = expected_ca_cert
with mock.patch.object(get_backend().CertManager,
'store_cert') as mock_store_cert:
mock_store_cert.return_value = expected_ca_cert_ref
self.assertEqual(
cert_manager._generate_ca_cert(expected_ca_name),
(expected_ca_cert_ref, expected_ca_cert,
expected_ca_password))
mock_generate_ca_cert.assert_called_once_with(
expected_ca_name, encryption_password=expected_ca_password)
mock_store_cert.assert_called_once_with(
certificate=expected_ca_cert['certificate'],
private_key=expected_ca_cert['private_key'],
private_key_passphrase=expected_ca_password,
name=expected_ca_name,
)
@mock.patch('magnum.common.x509.operations.generate_client_certificate')
@mock.patch('magnum.common.short_id.generate_id')
def test_generate_client_cert(self, mock_generate_id, mock_generate_cert):
expected_name = cert_manager.CONDUCTOR_CLIENT_NAME
expected_ca_name = 'ca-name'
expected_password = 'password'
expected_ca_password = 'ca-password'
expected_cert = {
'private_key': 'private_key', 'certificate': 'certificate'}
expected_ca_cert = {
'private_key': 'ca_private_key', 'certificate': 'ca_certificate'}
expected_cert_ref = 'cert_ref'
mock_generate_id.return_value = expected_password
mock_generate_cert.return_value = expected_cert
with mock.patch.object(get_backend().CertManager,
'store_cert') as mock_store_cert:
mock_store_cert.return_value = expected_cert_ref
self.assertEqual(
cert_manager._generate_client_cert(
expected_ca_name, expected_ca_cert, expected_ca_password),
expected_cert_ref)
mock_generate_cert.assert_called_once_with(
expected_ca_name,
expected_name,
expected_ca_cert['private_key'],
encryption_password=expected_password,
ca_key_password=expected_ca_password,
)
mock_store_cert.assert_called_once_with(
certificate=expected_cert['certificate'],
private_key=expected_cert['private_key'],
private_key_passphrase=expected_password,
name=expected_name,
)
@mock.patch('magnum.conductor.handlers.common.cert_manager.'
'_generate_client_cert')
@mock.patch('magnum.conductor.handlers.common.cert_manager.'
'_generate_ca_cert')
def test_generate_certificates(self, mock_generate_ca_cert,
mock_generate_client_cert):
expected_ca_name = 'ca-name'
expected_ca_password = 'ca-password'
expected_ca_cert = {
'private_key': 'ca_private_key', 'certificate': 'ca_certificate'}
expected_cert_ref = 'cert_ref'
expected_ca_cert_ref = 'ca-cert-ref'
mock_bay = mock.MagicMock()
mock_bay.name = expected_ca_name
mock_generate_ca_cert.return_value = (expected_ca_cert_ref,
expected_ca_cert,
expected_ca_password)
mock_generate_client_cert.return_value = expected_cert_ref
cert_manager.generate_certificates_to_bay(mock_bay)
self.assertEqual(mock_bay.ca_cert_ref, expected_ca_cert_ref)
self.assertEqual(mock_bay.magnum_cert_ref, expected_cert_ref)
mock_generate_ca_cert.assert_called_once_with(expected_ca_name)
mock_generate_client_cert.assert_called_once_with(
expected_ca_name, expected_ca_cert, expected_ca_password)

View File

@ -798,14 +798,18 @@ class TestHandler(db_base.DbTestCase):
bay = objects.Bay.get(self.context, self.bay.uuid) bay = objects.Bay.get(self.context, self.bay.uuid)
self.assertEqual(bay.node_count, 1) self.assertEqual(bay.node_count, 1)
@patch('magnum.conductor.handlers.common.cert_manager.'
'generate_certificates_to_bay')
@patch('magnum.conductor.handlers.bay_conductor._create_stack') @patch('magnum.conductor.handlers.bay_conductor._create_stack')
@patch('magnum.common.clients.OpenStackClients') @patch('magnum.common.clients.OpenStackClients')
def test_create(self, mock_openstack_client_class, mock_create_stack): def test_create(self, mock_openstack_client_class, mock_create_stack,
mock_generate_certificates):
mock_create_stack.side_effect = exc.HTTPBadRequest mock_create_stack.side_effect = exc.HTTPBadRequest
timeout = 15 timeout = 15
self.assertRaises(exception.InvalidParameterValue, self.assertRaises(exception.InvalidParameterValue,
self.handler.bay_create, self.context, self.handler.bay_create, self.context,
self.bay, timeout) self.bay, timeout)
mock_generate_certificates.assert_called_once_with(self.bay)
@patch('magnum.common.clients.OpenStackClients') @patch('magnum.common.clients.OpenStackClients')
def test_bay_delete(self, mock_openstack_client_class): def test_bay_delete(self, mock_openstack_client_class):