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:
parent
0fc4ff0002
commit
d6916e8bb1
@ -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 {
|
||||||
|
@ -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:
|
||||||
|
85
magnum/conductor/handlers/common/cert_manager.py
Normal file
85
magnum/conductor/handlers/common/cert_manager.py
Normal 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
|
117
magnum/tests/unit/conductor/handlers/common/test_cert_manager.py
Normal file
117
magnum/tests/unit/conductor/handlers/common/test_cert_manager.py
Normal 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)
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user