Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:chajain:branches:Cloud:Openstack:Master
openstack-glance-doc
0001-Enforce-image-safety-during-image_conversi...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0001-Enforce-image-safety-during-image_conversion.patch of Package openstack-glance-doc
From 6073d2cc51d09b61ed04ee37a580bd2a346b7b41 Mon Sep 17 00:00:00 2001 From: Dan Smith <dansmith@redhat.com> Date: Mon, 19 Dec 2022 15:00:35 +0000 Subject: [PATCH] Enforce image safety during image_conversion This does two things: 1. It makes us check that the QCOW backing_file is unset on those types of images. Nova and Cinder do this already to prevent an arbitrary (and trivial to accomplish) host file exposure exploit. 2. It makes us restrict VMDK files to only allowed subtypes. These files can name arbitrary files on disk as extents, providing the same sort of attack. Default that list to just the types we believe are actually useful for openstack, and which are monolithic. The configuration option to specify allowed subtypes is added in glance's config and not in the import options so that we can extend this check later to image ingest. The format_inspector can tell us what the type and subtype is, and we could reject those images early and even in the case where image_conversion is not enabled. Closes-Bug: #1996188 Change-Id: Idf561f6306cebf756c787d8eefdc452ce44bd5e0 (cherry picked from commit 0d6282a01691cecc2798f7858b181c4bb30f850c) (cherry picked from commit 4967ab6935cfd0274ae801ac943d01909a236a0a) (cherry picked from commit dc8e5a5cc7f5e9d1b697e520a7533cc90516db1b) (cherry picked from commit f45b5f024e765f0000884dfec5ac222124cfbc6d) (cherry picked from commit 9a98c4a7d1358cdae009cc8fb6377160a126ea7b) Conflicts: glance/tests/unit/async_/flows/plugins/test_image_conversion.py - removed code related to missing tests - 050802dd67b9135e04a65d340531157c94248c51 (cherry picked from commit 06e6be579156d8bf2ce8e021d07d6f351fb88c07) (cherry picked from commit b60fb70c9faf3b73eb4d8a73cd5cb09f2fa70a61) For backport to Rocky from cve-2022-47951-glance-stable-train.patch: - Add __init__.py to dirs under glance/tests/unit/async_ - Define unknown name FileNotFoundError as OSError in test_image_conversion.py --- glance/async/flows/plugins/image_conversion.py | 23 ++++++++ glance/common/config.py | 12 ++++ glance/tests/unit/async_/__init__.py | 0 glance/tests/unit/async_/flows/__init__.py | 0 glance/tests/unit/async_/flows/plugins/__init__.py | 0 .../async_/flows/plugins/test_image_conversion.py | 69 +++++++++++++++++++++- 6 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 glance/tests/unit/async_/__init__.py create mode 100644 glance/tests/unit/async_/flows/__init__.py create mode 100644 glance/tests/unit/async_/flows/plugins/__init__.py diff --git a/glance/async/flows/plugins/image_conversion.py b/glance/async/flows/plugins/image_conversion.py index 55c72cb..77c9494 100644 --- a/glance/async/flows/plugins/image_conversion.py +++ b/glance/async/flows/plugins/image_conversion.py @@ -105,6 +105,29 @@ class _ConvertImage(task.Task): image = self.image_repo.get(self.image_id) image.virtual_size = virtual_size + if 'backing-filename' in metadata: + LOG.warning('Refusing to process QCOW image with a backing file') + raise RuntimeError( + 'QCOW images with backing files are not allowed') + + if metadata.get('format') == 'vmdk': + create_type = metadata.get( + 'format-specific', {}).get( + 'data', {}).get('create-type') + allowed = CONF.image_format.vmdk_allowed_types + if not create_type: + raise RuntimeError(_('Unable to determine VMDK create-type')) + if not len(allowed): + LOG.warning(_('Refusing to process VMDK file as ' + 'vmdk_allowed_types is empty')) + raise RuntimeError(_('Image is a VMDK, but no VMDK createType ' + 'is specified')) + if create_type not in allowed: + LOG.warning(_('Refusing to process VMDK file with create-type ' + 'of %r which is not in allowed set of: %s'), + create_type, ','.join(allowed)) + raise RuntimeError(_('Invalid VMDK create-type specified')) + if source_format == target_format: LOG.debug("Source is already in target format, " "not doing conversion for %s", self.image_id) diff --git a/glance/common/config.py b/glance/common/config.py index 5ef92d9..d0e2b5f 100644 --- a/glance/common/config.py +++ b/glance/common/config.py @@ -97,6 +97,18 @@ image_format_opts = [ "image attribute"), deprecated_opts=[cfg.DeprecatedOpt('disk_formats', group='DEFAULT')]), + cfg.ListOpt('vmdk_allowed_types', + default=['streamOptimized', 'monolithicSparse'], + help=_("A list of strings describing allowed VMDK " + "'create-type' subformats that will be allowed. " + "This is recommended to only include " + "single-file-with-sparse-header variants to avoid " + "potential host file exposure due to processing named " + "extents. If this list is empty, then no VDMK image " + "types allowed. Note that this is currently only " + "checked during image conversion (if enabled), and " + "limits the types of VMDK images we will convert " + "from.")), ] task_opts = [ cfg.IntOpt('task_time_to_live', diff --git a/glance/tests/unit/async_/__init__.py b/glance/tests/unit/async_/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/glance/tests/unit/async_/flows/__init__.py b/glance/tests/unit/async_/flows/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/glance/tests/unit/async_/flows/plugins/__init__.py b/glance/tests/unit/async_/flows/plugins/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/glance/tests/unit/async_/flows/plugins/test_image_conversion.py b/glance/tests/unit/async_/flows/plugins/test_image_conversion.py index df6e420..d8f77c5 100644 --- a/glance/tests/unit/async_/flows/plugins/test_image_conversion.py +++ b/glance/tests/unit/async_/flows/plugins/test_image_conversion.py @@ -21,7 +21,7 @@ import glance_store from oslo_concurrency import processutils from oslo_config import cfg -import glance.async_.flows.plugins.image_conversion as image_conversion +import glance.async.flows.plugins.image_conversion as image_conversion from glance.common import utils from glance import domain from glance import gateway @@ -33,6 +33,11 @@ CONF = cfg.CONF UUID1 = 'c80a1a6c-bd1f-41c5-90ee-81afedb1d58d' TENANT1 = '6838eb7b-6ded-434a-882c-b344c77fe8df' +try: + FileNotFoundError +except NameError: + FileNotFoundError = OSError + class TestConvertImageTask(test_utils.BaseTestCase): @@ -105,6 +110,68 @@ class TestConvertImageTask(test_utils.BaseTestCase): self.assertIn('-f', exc_mock.call_args[0]) self.assertEqual("qcow2", image.disk_format) + def _setup_image_convert_info_fail(self): + image_convert = image_conversion._ConvertImage(self.context, + self.task.task_id, + self.task_type, + self.img_repo, + self.image_id) + + self.task_repo.get.return_value = self.task + image = mock.MagicMock(image_id=self.image_id, virtual_size=None, + extra_properties={ + 'os_glance_import_task': self.task.task_id}, + disk_format='qcow2') + self.img_repo.get.return_value = image + return image_convert + + def test_image_convert_invalid_qcow(self): + data = {'format': 'qcow2', + 'backing-filename': '/etc/hosts'} + + convert = self._setup_image_convert_info_fail() + with mock.patch.object(processutils, 'execute') as exc_mock: + exc_mock.return_value = json.dumps(data), '' + e = self.assertRaises(RuntimeError, + convert.execute, 'file:///test/path.qcow') + self.assertEqual('QCOW images with backing files are not allowed', + str(e)) + + def _test_image_convert_invalid_vmdk(self): + data = {'format': 'vmdk', + 'format-specific': { + 'data': { + 'create-type': 'monolithicFlat', + }}} + + convert = self._setup_image_convert_info_fail() + with mock.patch.object(processutils, 'execute') as exc_mock: + exc_mock.return_value = json.dumps(data), '' + convert.execute('file:///test/path.vmdk') + + def test_image_convert_invalid_vmdk(self): + e = self.assertRaises(RuntimeError, + self._test_image_convert_invalid_vmdk) + self.assertEqual('Invalid VMDK create-type specified', str(e)) + + def test_image_convert_valid_vmdk_no_types(self): + with mock.patch.object(CONF.image_format, 'vmdk_allowed_types', + new=[]): + # We make it past the VMDK check and fail because our file + # does not exist + e = self.assertRaises(RuntimeError, + self._test_image_convert_invalid_vmdk) + self.assertEqual('Image is a VMDK, but no VMDK createType is ' + 'specified', str(e)) + + def test_image_convert_valid_vmdk(self): + with mock.patch.object(CONF.image_format, 'vmdk_allowed_types', + new=['monolithicSparse', 'monolithicFlat']): + # We make it past the VMDK check and fail because our file + # does not exist + self.assertRaises(FileNotFoundError, + self._test_image_convert_invalid_vmdk) + @mock.patch.object(os, 'remove') def test_image_convert_revert_success(self, mock_os_remove): mock_os_remove.return_value = None -- 2.7.4
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor