Merge "Enforce image safety during image_conversion" into stable/wallaby
This commit is contained in:
commit
883fc8a73c
@ -116,6 +116,29 @@ class _ConvertImage(task.Task):
|
|||||||
virtual_size = metadata.get('virtual-size', 0)
|
virtual_size = metadata.get('virtual-size', 0)
|
||||||
action.set_image_attribute(virtual_size=virtual_size)
|
action.set_image_attribute(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:
|
if source_format == target_format:
|
||||||
LOG.debug("Source is already in target format, "
|
LOG.debug("Source is already in target format, "
|
||||||
"not doing conversion for %s", self.image_id)
|
"not doing conversion for %s", self.image_id)
|
||||||
|
@ -100,6 +100,18 @@ image_format_opts = [
|
|||||||
"image attribute"),
|
"image attribute"),
|
||||||
deprecated_opts=[cfg.DeprecatedOpt('disk_formats',
|
deprecated_opts=[cfg.DeprecatedOpt('disk_formats',
|
||||||
group='DEFAULT')]),
|
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 = [
|
task_opts = [
|
||||||
cfg.IntOpt('task_time_to_live',
|
cfg.IntOpt('task_time_to_live',
|
||||||
|
@ -169,6 +169,53 @@ class TestConvertImageTask(test_utils.BaseTestCase):
|
|||||||
# Make sure we did not update the image
|
# Make sure we did not update the image
|
||||||
self.img_repo.save.assert_not_called()
|
self.img_repo.save.assert_not_called()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
def test_image_convert_fails(self):
|
def test_image_convert_fails(self):
|
||||||
convert = self._setup_image_convert_info_fail()
|
convert = self._setup_image_convert_info_fail()
|
||||||
with mock.patch.object(processutils, 'execute') as exc_mock:
|
with mock.patch.object(processutils, 'execute') as exc_mock:
|
||||||
|
Loading…
Reference in New Issue
Block a user