Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:chajain
openstack-nova
0001-stable-only-cve-Check-VMDK-create-type-aga...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0001-stable-only-cve-Check-VMDK-create-type-against-an-al.patch of Package openstack-nova
From 0d9b0e568fe0fabce2cb814edf1a57a7707e2eed Mon Sep 17 00:00:00 2001 From: Dan Smith <dansmith@redhat.com> Date: Thu, 10 Nov 2022 09:55:48 -0800 Subject: [PATCH] [stable-only][cve] Check VMDK create-type against an allowed list Trivial conflicts on xena only in: nova/conf/compute.py NOTE(sbauza): Stable policy allows us to proactively merge a backport without waiting for the parent patch to be merged (exception to rule #4 in [1]. Marking [stable-only] in order to silence nova-tox-validate-backport [1] https://docs.openstack.org/project-team-guide/stable-branches.html#appropriate-fixes Related-Bug: #1996188 Change-Id: I5a399f1d3d702bfb76c067893e9c924904c8c360 (cherry picked from commit 867c4dd893ea7211e89b78b22b8da920a74622ff) For Rocky backport, changed qemu_img_info mock in test_fetch_checks_vmdk_rules. Changed qemu_img_info() so it calls qemu-img with output='json' when called from fetch_to_raw() so format-specific fields are parsed. --- nova/conf/compute.py | 9 ++++++ nova/tests/unit/virt/libvirt/test_utils.py | 2 +- nova/tests/unit/virt/test_images.py | 47 ++++++++++++++++++++++++++++++ nova/virt/images.py | 42 ++++++++++++++++++++++++-- 4 files changed, 96 insertions(+), 4 deletions(-) diff --git a/nova/conf/compute.py b/nova/conf/compute.py index 5436374..44edaf4 100644 --- a/nova/conf/compute.py +++ b/nova/conf/compute.py @@ -621,6 +621,15 @@ Possible values: ] compute_group_opts = [ + 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 +form of VMDK image will be allowed. +"""), cfg.IntOpt('consecutive_build_service_disable_threshold', default=10, help=""" diff --git a/nova/tests/unit/virt/libvirt/test_utils.py b/nova/tests/unit/virt/libvirt/test_utils.py index 898d517..1e3293c 100644 --- a/nova/tests/unit/virt/libvirt/test_utils.py +++ b/nova/tests/unit/virt/libvirt/test_utils.py @@ -649,7 +649,7 @@ disk size: 4.4M def fake_rm_on_error(path, remove=None): self.executes.append(('rm', '-f', path)) - def fake_qemu_img_info(path): + def fake_qemu_img_info(path, use_json=False): class FakeImgInfo(object): pass diff --git a/nova/tests/unit/virt/test_images.py b/nova/tests/unit/virt/test_images.py index 2baf445..e448d7a 100644 --- a/nova/tests/unit/virt/test_images.py +++ b/nova/tests/unit/virt/test_images.py @@ -16,6 +16,8 @@ import os import mock from oslo_concurrency import processutils +from oslo_serialization import jsonutils +from oslo_utils import imageutils import six from nova import exception @@ -120,3 +122,48 @@ class QemuTestCase(test.NoDBTestCase): expected = ('qemu-img', 'convert', '-t', 'writethrough', '-O', 'out_format', '-f', 'in_format', 'source', 'dest') self.assertTupleEqual(expected, mock_execute.call_args[0]) + + def test_convert_image_vmdk_allowed_list_checking(self): + info = {'format': 'vmdk', + 'format-specific': { + 'type': 'vmdk', + 'data': { + 'create-type': 'monolithicFlat', + }}} + + # If the format is not in the allowed list, we should get an error + self.assertRaises(exception.ImageUnacceptable, + images.check_vmdk_image, 'foo', + imageutils.QemuImgInfo(jsonutils.dumps(info), + format='json')) + + # With the format in the allowed list, no error + self.flags(vmdk_allowed_types=['streamOptimized', 'monolithicFlat', + 'monolithicSparse'], + group='compute') + images.check_vmdk_image('foo', + imageutils.QemuImgInfo(jsonutils.dumps(info), + format='json')) + + # With an empty list, allow nothing + self.flags(vmdk_allowed_types=[], group='compute') + self.assertRaises(exception.ImageUnacceptable, + images.check_vmdk_image, 'foo', + imageutils.QemuImgInfo(jsonutils.dumps(info), + format='json')) + + @mock.patch.object(images, 'fetch') + @mock.patch.object(images, 'qemu_img_info') + def test_fetch_checks_vmdk_rules(self, mock_info, mock_fetch): + info = {'format': 'vmdk', + 'format-specific': { + 'type': 'vmdk', + 'data': { + 'create-type': 'monolithicFlat', + }}} + mock_info.return_value = imageutils.QemuImgInfo(jsonutils.dumps(info), + format='json') + with mock.patch('os.path.exists', return_value=True): + e = self.assertRaises(exception.ImageUnacceptable, + images.fetch_to_raw, None, 'foo', 'anypath') + self.assertIn('Invalid VMDK create-type specified', str(e)) diff --git a/nova/virt/images.py b/nova/virt/images.py index dbaa4e7..4778b05 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -50,7 +50,7 @@ QEMU_VERSION = None QEMU_VERSION_REQ_SHARED = 2010000 -def qemu_img_info(path, format=None): +def qemu_img_info(path, format=None, use_json=False): """Return an object containing the parsed output from qemu-img info.""" # TODO(mikal): this code should not be referring to a libvirt specific # flag. @@ -65,6 +65,8 @@ def qemu_img_info(path, format=None): path = os.path.join(path, "root.hds") cmd = ('env', 'LC_ALL=C', 'LANG=C', 'qemu-img', 'info', path) + if use_json: + cmd = cmd + ('--output=json',) if format is not None: cmd = cmd + ('-f', format) # Check to see if the qemu version is >= 2.10 because if so, we need @@ -92,7 +94,10 @@ def qemu_img_info(path, format=None): {'path': path, 'error': err}) raise exception.InvalidDiskInfo(reason=msg) - return imageutils.QemuImgInfo(out) + if use_json: + return imageutils.QemuImgInfo(out, format='json') + else: + return imageutils.QemuImgInfo(out) def convert_image(source, dest, in_format, out_format, run_as_root=False): @@ -141,12 +146,40 @@ def get_info(context, image_href): return IMAGE_API.get(context, image_href) +def check_vmdk_image(image_id, data): + # Check some rules about VMDK files. Specifically we want to make + # sure that the "create-type" of the image is one that we allow. + # Some types of VMDK files can reference files outside the disk + # image and we do not want to allow those for obvious reasons. + + types = CONF.compute.vmdk_allowed_types + + if not len(types): + LOG.warning('Refusing to allow VMDK image as vmdk_allowed_' + 'types is empty') + msg = _('Invalid VMDK create-type specified') + raise exception.ImageUnacceptable(image_id=image_id, reason=msg) + + try: + create_type = data.format_specific['data']['create-type'] + except KeyError: + msg = _('Unable to determine VMDK create-type') + raise exception.ImageUnacceptable(image_id=image_id, reason=msg) + + if create_type not in CONF.compute.vmdk_allowed_types: + LOG.warning('Refusing to process VMDK file with create-type of %r ' + 'which is not in allowed set of: %s', create_type, + ','.join(CONF.compute.vmdk_allowed_types)) + msg = _('Invalid VMDK create-type specified') + raise exception.ImageUnacceptable(image_id=image_id, reason=msg) + + def fetch_to_raw(context, image_href, path, trusted_certs=None): path_tmp = "%s.part" % path fetch(context, image_href, path_tmp, trusted_certs) with fileutils.remove_path_on_error(path_tmp): - data = qemu_img_info(path_tmp) + data = qemu_img_info(path_tmp, use_json=True) fmt = data.file_format if fmt is None: @@ -160,6 +193,9 @@ def fetch_to_raw(context, image_href, path, trusted_certs=None): reason=(_("fmt=%(fmt)s backed by: %(backing_file)s") % {'fmt': fmt, 'backing_file': backing_file})) + if fmt == 'vmdk': + check_vmdk_image(image_href, data) + if fmt != "raw" and CONF.force_raw_images: staged = "%s.converted" % path LOG.debug("%s was %s, converting to raw", image_href, fmt) -- 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