From 2322b3c0ee6645ec900c9a9505cc0a1ddcbcfcdb Mon Sep 17 00:00:00 2001 From: Clif Houck Date: Wed, 15 Apr 2026 11:54:05 -0500 Subject: [PATCH] Add a flag to disable installing bootloaders Disables bootloader installations (calls to grub-install) for security reasons as part of the mitigation for CVE-2026-43003. Depends-On: https://review.opendev.org/c/openstack/ironic/+/990724 Related-Bug: 2148310 Change-Id: I10c88426d5838820ecf6853dca5b3878dc29bdf4 Signed-off-by: Clif Houck Signed-off-by: Jay Faulkner (cherry picked from commit 6cd463a657edcddf7b79416ac69bdef5b6f30099) (cherry picked from commit 1dc404837a2b234af690417f9dc534d3fd405ce7) (cherry picked from commit 10d133a522245f7228131bfc82b28e3f3c2ddd60) --- ironic_python_agent/agent.py | 3 ++ ironic_python_agent/config.py | 7 ++++ ironic_python_agent/extensions/image.py | 31 +++++++++++------- .../tests/unit/extensions/test_image.py | 32 +++++++++++++++++++ ...otloaders-by-default-1029ed4faaf049aa.yaml | 9 ++++++ 5 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 releasenotes/notes/disable-installing-bootloaders-by-default-1029ed4faaf049aa.yaml diff --git a/ironic_python_agent/agent.py b/ironic_python_agent/agent.py index 6ff986a2a..14c44bb4a 100644 --- a/ironic_python_agent/agent.py +++ b/ironic_python_agent/agent.py @@ -402,6 +402,9 @@ def process_lookup_data(self, content): # Update config with values from Ironic config = content.get('config', {}) + if config.get('enable_bios_bootloader_install'): + cfg.CONF.set_override('enable_bios_bootloader_install', + config['enable_bios_bootloader_install']) if config.get('metrics'): for opt, val in config.items(): setattr(cfg.CONF.metrics, opt, val) diff --git a/ironic_python_agent/config.py b/ironic_python_agent/config.py index 0fe26785b..f46d99c9f 100644 --- a/ironic_python_agent/config.py +++ b/ironic_python_agent/config.py @@ -346,6 +346,13 @@ 'image validation logic will fail the deployment ' 'process. This check is skipped if deep image ' 'inspection is disabled.'), + cfg.BoolOpt('enable_bios_bootloader_install', + default=False, + help='Enables support for partition images which require a ' + 'legacy bootloader -- and a call to ``grub-install``. ' + 'Generally, this should remain disabled for maximum ' + 'security, however, this option allows it to be ' + 're-enabled for compatibility.'), ] disk_utils_opts = [ diff --git a/ironic_python_agent/extensions/image.py b/ironic_python_agent/extensions/image.py index 46be48c2a..6931e8957 100644 --- a/ironic_python_agent/extensions/image.py +++ b/ironic_python_agent/extensions/image.py @@ -716,15 +716,24 @@ def install_bootloader(self, root_uuid, efi_system_part_uuid=None, ' Assuming a whole disk image') return - # In case we can't use efibootmgr for uefi we will continue using grub2 - LOG.debug('Using grub2-install to set up boot files') - try: - _install_grub2(device, - root_uuid=root_uuid, - efi_system_part_uuid=efi_system_part_uuid, - prep_boot_part_uuid=prep_boot_part_uuid, - target_boot_mode=target_boot_mode) - except Exception as e: - LOG.error('Error setting up bootloader. Error %s', e) + if CONF.enable_bios_bootloader_install: + # In case we can't use efibootmgr for uefi we will continue + # using grub2 + LOG.debug('Using grub2-install to set up boot files') + try: + _install_grub2(device, + root_uuid=root_uuid, + efi_system_part_uuid=efi_system_part_uuid, + prep_boot_part_uuid=prep_boot_part_uuid, + target_boot_mode=target_boot_mode) + except Exception as e: + LOG.error('Error setting up bootloader. Error %s', e) + if not ignore_failure: + raise + else: + msg = ("Install of legacy BIOS bootloaders disabled by " + "CONF.enable_bios_bootloader_install as part of " + "CVE-2026-43003 mitigation.") + LOG.error(msg) if not ignore_failure: - raise + raise errors.InvalidImage(details=msg) diff --git a/ironic_python_agent/tests/unit/extensions/test_image.py b/ironic_python_agent/tests/unit/extensions/test_image.py index 2bfa782cf..51c6ecc7e 100644 --- a/ironic_python_agent/tests/unit/extensions/test_image.py +++ b/ironic_python_agent/tests/unit/extensions/test_image.py @@ -52,6 +52,7 @@ def setUp(self): self.fake_efi_system_part_uuid = '45AB-2312' self.fake_prep_boot_part_uuid = '76937797-3253-8843-999999999999' self.fake_dir = '/tmp/fake-dir' + self.config(enable_bios_bootloader_install=True) @mock.patch.object(image, '_install_grub2', autospec=True) def test__install_bootloader_bios(self, mock_grub2, @@ -68,8 +69,39 @@ def test__install_bootloader_bios(self, mock_grub2, self.fake_dev, root_uuid=self.fake_root_uuid, efi_system_part_uuid=None, prep_boot_part_uuid=None, target_boot_mode='bios' + ) + @mock.patch.object(image, '_install_grub2', autospec=True) + def test__install_bootloader_bios_disabled(self, mock_grub2, + mock_execute, mock_dispatch): + self.config(enable_bios_bootloader_install=False) + mock_dispatch.side_effect = [ + self.fake_dev, hardware.BootInfo(current_boot_mode='bios') + ] + self.agent_extension.install_bootloader( + root_uuid=self.fake_root_uuid).join() + mock_dispatch.assert_any_call('get_os_install_device') + mock_dispatch.assert_any_call('get_boot_info') + self.assertEqual(2, mock_dispatch.call_count) + mock_grub2.assert_not_called() + + @mock.patch.object(image, '_install_grub2', autospec=True) + def test__install_bootloader_bios_disabled_dont_ignore_failures( + self, mock_grub2, mock_execute, mock_dispatch): + self.config(enable_bios_bootloader_install=False) + self.config(ignore_bootloader_failure=False) + mock_dispatch.side_effect = [ + self.fake_dev, hardware.BootInfo(current_boot_mode='bios') + ] + result = self.agent_extension.install_bootloader( + root_uuid=self.fake_root_uuid).join() + mock_dispatch.assert_any_call('get_os_install_device') + mock_dispatch.assert_any_call('get_boot_info') + self.assertEqual(2, mock_dispatch.call_count) + self.assertIsNotNone(result.command_error) + mock_grub2.assert_not_called() + @mock.patch.object(efi_utils, 'manage_uefi', autospec=True) @mock.patch.object(image, '_install_grub2', autospec=True) def test__install_bootloader_uefi(self, mock_grub2, mock_uefi, diff --git a/releasenotes/notes/disable-installing-bootloaders-by-default-1029ed4faaf049aa.yaml b/releasenotes/notes/disable-installing-bootloaders-by-default-1029ed4faaf049aa.yaml new file mode 100644 index 000000000..28a771345 --- /dev/null +++ b/releasenotes/notes/disable-installing-bootloaders-by-default-1029ed4faaf049aa.yaml @@ -0,0 +1,9 @@ +--- +security: + - | + Disable installation of bootloaders (via grub-install) by default in order + to improve security posture by adding a new configuration option + `enable_bios_bootloader_install` which defaults to `False`. Operators + who still need this functionality can re-enable installation of + bootloaders by setting `enable_bios_bootloader_install` to `True`. + Addresses CVE-2026-43003.