Changes for secure boot support for iLO drivers

This patch implements common changes to support secure boot for
iLO drivers.

Change-Id: I9b5baf5db0f87c09209cd2f38c1e61ae389363aa
Implements: blueprint uefi-secure-boot
This commit is contained in:
Shivanand Tendulker 2015-03-19 09:39:51 -07:00 committed by Ruby Loo
parent f0be66f96c
commit f1e6bce0b2
5 changed files with 272 additions and 0 deletions

View File

@ -675,6 +675,21 @@ steps on the Ironic conductor node to configure PXE UEFI environment.
ironic node-update <node-uuid> add properties/capabilities='boot_mode:uefi'
#. For deploying signed images, update the Ironic node with ``secure_boot``
capability in node's properties.
field::
ironic node-update <node-uuid> add properties/capabilities='secure_boot:true'
#. Ensure the public key of the signed image is loaded into baremetal to deploy
signed images.
For HP Proliant Gen9 servers, one can enroll public key using iLO System
Utilities UI. Please refer to section ``Accessing Secure Boot options`` in
HP UEFI System Utilities User Guide http://www.hp.com/ctg/Manual/c04398276.pdf.
Also, one can refer to white paper on Secure Boot on Linux for HP Proliant
Servers at http://h20195.www2.hp.com/V2/getpdf.aspx/4AA5-4496ENW.pdf for
more details.
#. Make sure that bare metal node is configured to boot in UEFI boot mode and
boot device is set to network/pxe.

View File

@ -1013,3 +1013,21 @@ def parse_root_device_hints(node):
hints.append("%s=%s" % (key, value))
return ','.join(hints)
def is_secure_boot_requested(node):
"""Returns True if secure_boot is requested for deploy.
This method checks node property for secure_boot and returns True
if it is requested.
:param node: a single Node.
:raises: InvalidParameterValue if the capabilities string is not a
dictionary or is malformed.
:returns: True if secure_boot is requested.
"""
capabilities = parse_instance_info_capabilities(node)
sec_boot = capabilities.get('secure_boot', 'false').lower()
return sec_boot == 'true'

View File

@ -274,6 +274,96 @@ def _prepare_agent_vmedia_boot(task):
_reboot_into(task, deploy_iso, deploy_ramdisk_opts)
def _disable_secure_boot(task):
"""Disables secure boot on node, if secure boot is enabled on node.
This method checks if secure boot is enabled on node. If enabled, it
disables same and returns True.
:param task: a TaskManager instance containing the node to act on.
:returns: It returns True, if secure boot was successfully disabled on
the node.
It returns False, if secure boot on node is in disabled state
or if secure boot feature is not supported by the node.
:raises: IloOperationError, if some operation on iLO failed.
"""
cur_sec_state = False
try:
cur_sec_state = ilo_common.get_secure_boot_mode(task)
except exception.IloOperationNotSupported:
LOG.debug('Secure boot mode is not supported for node %s',
task.node.uuid)
return False
if cur_sec_state:
LOG.debug('Disabling secure boot for node %s', task.node.uuid)
ilo_common.set_secure_boot_mode(task, False)
return True
return False
def _prepare_node_for_deploy(task):
"""Common preparatory steps for all iLO drivers.
This method performs common preparatory steps required for all drivers.
1. Power off node
2. Disables secure boot, if it is in enabled state.
3. Updates boot_mode capability to 'uefi' if secure boot is requested.
4. Changes boot mode of the node if secure boot is disabled currently.
:param task: a TaskManager instance containing the node to act on.
:raises: IloOperationError, if some operation on iLO failed.
"""
manager_utils.node_power_action(task, states.POWER_OFF)
# Boot mode can be changed only if secure boot is in disabled state.
# secure boot and boot mode cannot be changed together.
change_boot_mode = True
# Disable secure boot on the node if it is in enabled state.
if _disable_secure_boot(task):
change_boot_mode = False
# Set boot_mode capability to uefi for secure boot
if deploy_utils.is_secure_boot_requested(task.node):
LOG.debug('Secure boot deploy requested for node %s', task.node.uuid)
_enable_uefi_capability(task)
if change_boot_mode:
ilo_common.update_boot_mode(task)
def _update_secure_boot_mode(task, mode):
"""Changes secure boot mode for next boot on the node.
This method changes secure boot mode on the node for next boot. It changes
the secure boot mode setting on node only if the deploy has requested for
the secure boot.
During deploy, this method is used to enable secure boot on the node by
passing 'mode' as 'True'.
During teardown, this method is used to disable secure boot on the node by
passing 'mode' as 'False'.
:param task: a TaskManager instance containing the node to act on.
:param mode: Boolean value requesting the next state for secure boot
:raises: IloOperationNotSupported, if operation is not supported on iLO
:raises: IloOperationError, if some operation on iLO failed.
"""
if deploy_utils.is_secure_boot_requested(task.node):
ilo_common.set_secure_boot_mode(task, mode)
LOG.info(_LI('Changed secure boot to %(mode)s for node %(node)s'),
{'mode': mode, 'node': task.node.uuid})
def _enable_uefi_capability(task):
"""Adds capability boot_mode='uefi' into Node property.
:param task: a TaskManager instance containing the node to act on.
"""
driver_utils.rm_node_capability(task, 'boot_mode')
driver_utils.add_node_capability(task, 'boot_mode', 'uefi')
class IloVirtualMediaIscsiDeploy(base.DeployInterface):
def get_properties(self):

View File

@ -268,6 +268,143 @@ class IloDeployPrivateMethodsTestCase(db_base.DbTestCase):
'deploy-iso-uuid',
deploy_opts)
@mock.patch.object(deploy_utils, 'is_secure_boot_requested')
@mock.patch.object(ilo_common, 'set_secure_boot_mode')
def test__update_secure_boot_passed_true(self,
func_set_secure_boot_mode,
func_is_secure_boot_requested):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
func_is_secure_boot_requested.return_value = True
ilo_deploy._update_secure_boot_mode(task, True)
func_set_secure_boot_mode.assert_called_once_with(task, True)
@mock.patch.object(deploy_utils, 'is_secure_boot_requested')
@mock.patch.object(ilo_common, 'set_secure_boot_mode')
def test__update_secure_boot_passed_False(self,
func_set_secure_boot_mode,
func_is_secure_boot_requested):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
func_is_secure_boot_requested.return_value = False
ilo_deploy._update_secure_boot_mode(task, False)
self.assertFalse(func_set_secure_boot_mode.called)
@mock.patch.object(driver_utils, 'add_node_capability')
@mock.patch.object(driver_utils, 'rm_node_capability')
def test__enable_uefi_capability(self, func_rm_node_capability,
func_add_node_capability):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
ilo_deploy._enable_uefi_capability(task)
func_rm_node_capability.assert_called_once_with(task,
'boot_mode')
func_add_node_capability.assert_called_once_with(task,
'boot_mode',
'uefi')
@mock.patch.object(ilo_common, 'set_secure_boot_mode')
@mock.patch.object(ilo_common, 'get_secure_boot_mode')
def test__disable_secure_boot_false(self,
func_get_secure_boot_mode,
func_set_secure_boot_mode):
func_get_secure_boot_mode.return_value = False
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
returned_state = ilo_deploy._disable_secure_boot(task)
func_get_secure_boot_mode.assert_called_once_with(task)
self.assertFalse(func_set_secure_boot_mode.called)
self.assertFalse(returned_state)
@mock.patch.object(ilo_common, 'set_secure_boot_mode')
@mock.patch.object(ilo_common, 'get_secure_boot_mode')
def test__disable_secure_boot_true(self,
func_get_secure_boot_mode,
func_set_secure_boot_mode):
func_get_secure_boot_mode.return_value = True
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
returned_state = ilo_deploy._disable_secure_boot(task)
func_get_secure_boot_mode.assert_called_once_with(task)
func_set_secure_boot_mode.assert_called_once_with(task, False)
self.assertTrue(returned_state)
@mock.patch.object(ilo_deploy, 'exception')
@mock.patch.object(ilo_common, 'get_secure_boot_mode')
def test__disable_secure_boot_exception(self,
func_get_secure_boot_mode,
exception_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
exception_mock.IloOperationNotSupported = Exception
func_get_secure_boot_mode.side_effect = Exception
returned_state = ilo_deploy._disable_secure_boot(task)
func_get_secure_boot_mode.assert_called_once_with(task)
self.assertFalse(returned_state)
@mock.patch.object(ilo_common, 'update_boot_mode')
@mock.patch.object(deploy_utils, 'is_secure_boot_requested')
@mock.patch.object(ilo_deploy, '_disable_secure_boot')
@mock.patch.object(manager_utils, 'node_power_action')
def test__prepare_node_for_deploy(self,
func_node_power_action,
func_disable_secure_boot,
func_is_secure_boot_requested,
func_update_boot_mode):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
func_disable_secure_boot.return_value = False
func_is_secure_boot_requested.return_value = False
ilo_deploy._prepare_node_for_deploy(task)
func_node_power_action.assert_called_once_with(task,
states.POWER_OFF)
func_disable_secure_boot.assert_called_once_with(task)
func_is_secure_boot_requested.assert_called_once_with(task.node)
func_update_boot_mode.assert_called_once_with(task)
@mock.patch.object(ilo_common, 'update_boot_mode')
@mock.patch.object(deploy_utils, 'is_secure_boot_requested')
@mock.patch.object(ilo_deploy, '_disable_secure_boot')
@mock.patch.object(manager_utils, 'node_power_action')
def test__prepare_node_for_deploy_sec_boot_on(self,
func_node_power_action,
func_disable_secure_boot,
func_is_secure_boot_req,
func_update_boot_mode):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
func_disable_secure_boot.return_value = True
func_is_secure_boot_req.return_value = False
ilo_deploy._prepare_node_for_deploy(task)
func_node_power_action.assert_called_once_with(task,
states.POWER_OFF)
func_disable_secure_boot.assert_called_once_with(task)
func_is_secure_boot_req.assert_called_once_with(task.node)
self.assertFalse(func_update_boot_mode.called)
@mock.patch.object(ilo_common, 'update_boot_mode')
@mock.patch.object(ilo_deploy, '_enable_uefi_capability')
@mock.patch.object(deploy_utils, 'is_secure_boot_requested')
@mock.patch.object(ilo_deploy, '_disable_secure_boot')
@mock.patch.object(manager_utils, 'node_power_action')
def test__prepare_node_for_deploy_sec_boot_req(self,
func_node_power_action,
func_disable_secure_boot,
func_is_secure_boot_req,
func_enable_uefi_cap,
func_update_boot_mode):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
func_disable_secure_boot.return_value = True
func_is_secure_boot_req.return_value = True
ilo_deploy._prepare_node_for_deploy(task)
func_node_power_action.assert_called_once_with(task,
states.POWER_OFF)
func_disable_secure_boot.assert_called_once_with(task)
func_is_secure_boot_req.assert_called_once_with(task.node)
func_enable_uefi_cap.assert_called_once_with(task)
self.assertFalse(func_update_boot_mode.called)
class IloVirtualMediaIscsiDeployTestCase(db_base.DbTestCase):

View File

@ -1452,6 +1452,18 @@ class ParseInstanceInfoCapabilitiesTestCase(tests_base.TestCase):
self.assertRaises(exception.InvalidParameterValue,
utils.parse_instance_info_capabilities, self.node)
def test_is_secure_boot_requested_true(self):
self.node.instance_info = {'capabilities': {"secure_boot": "true"}}
self.assertTrue(utils.is_secure_boot_requested(self.node))
def test_is_secure_boot_requested_false(self):
self.node.instance_info = {'capabilities': {"secure_boot": "false"}}
self.assertFalse(utils.is_secure_boot_requested(self.node))
def test_is_secure_boot_requested_invalid(self):
self.node.instance_info = {'capabilities': {"secure_boot": "invalid"}}
self.assertFalse(utils.is_secure_boot_requested(self.node))
class TrySetBootDeviceTestCase(db_base.DbTestCase):