Skip to content

Commit 4fc2ea4

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Enforce image safety during image_conversion" into stable/xena
2 parents e45a32d + f45b5f0 commit 4fc2ea4

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

glance/async_/flows/plugins/image_conversion.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,29 @@ def _execute(self, action, file_path, **kwargs):
116116
virtual_size = metadata.get('virtual-size', 0)
117117
action.set_image_attribute(virtual_size=virtual_size)
118118

119+
if 'backing-filename' in metadata:
120+
LOG.warning('Refusing to process QCOW image with a backing file')
121+
raise RuntimeError(
122+
'QCOW images with backing files are not allowed')
123+
124+
if metadata.get('format') == 'vmdk':
125+
create_type = metadata.get(
126+
'format-specific', {}).get(
127+
'data', {}).get('create-type')
128+
allowed = CONF.image_format.vmdk_allowed_types
129+
if not create_type:
130+
raise RuntimeError(_('Unable to determine VMDK create-type'))
131+
if not len(allowed):
132+
LOG.warning(_('Refusing to process VMDK file as '
133+
'vmdk_allowed_types is empty'))
134+
raise RuntimeError(_('Image is a VMDK, but no VMDK createType '
135+
'is specified'))
136+
if create_type not in allowed:
137+
LOG.warning(_('Refusing to process VMDK file with create-type '
138+
'of %r which is not in allowed set of: %s'),
139+
create_type, ','.join(allowed))
140+
raise RuntimeError(_('Invalid VMDK create-type specified'))
141+
119142
if source_format == target_format:
120143
LOG.debug("Source is already in target format, "
121144
"not doing conversion for %s", self.image_id)

glance/common/config.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@
100100
"image attribute"),
101101
deprecated_opts=[cfg.DeprecatedOpt('disk_formats',
102102
group='DEFAULT')]),
103+
cfg.ListOpt('vmdk_allowed_types',
104+
default=['streamOptimized', 'monolithicSparse'],
105+
help=_("A list of strings describing allowed VMDK "
106+
"'create-type' subformats that will be allowed. "
107+
"This is recommended to only include "
108+
"single-file-with-sparse-header variants to avoid "
109+
"potential host file exposure due to processing named "
110+
"extents. If this list is empty, then no VDMK image "
111+
"types allowed. Note that this is currently only "
112+
"checked during image conversion (if enabled), and "
113+
"limits the types of VMDK images we will convert "
114+
"from.")),
103115
]
104116
task_opts = [
105117
cfg.IntOpt('task_time_to_live',

glance/tests/unit/async_/flows/plugins/test_image_conversion.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,53 @@ def test_image_convert_inspection_reports_error(self):
172172
# Make sure we did not update the image
173173
self.img_repo.save.assert_not_called()
174174

175+
def test_image_convert_invalid_qcow(self):
176+
data = {'format': 'qcow2',
177+
'backing-filename': '/etc/hosts'}
178+
179+
convert = self._setup_image_convert_info_fail()
180+
with mock.patch.object(processutils, 'execute') as exc_mock:
181+
exc_mock.return_value = json.dumps(data), ''
182+
e = self.assertRaises(RuntimeError,
183+
convert.execute, 'file:///test/path.qcow')
184+
self.assertEqual('QCOW images with backing files are not allowed',
185+
str(e))
186+
187+
def _test_image_convert_invalid_vmdk(self):
188+
data = {'format': 'vmdk',
189+
'format-specific': {
190+
'data': {
191+
'create-type': 'monolithicFlat',
192+
}}}
193+
194+
convert = self._setup_image_convert_info_fail()
195+
with mock.patch.object(processutils, 'execute') as exc_mock:
196+
exc_mock.return_value = json.dumps(data), ''
197+
convert.execute('file:///test/path.vmdk')
198+
199+
def test_image_convert_invalid_vmdk(self):
200+
e = self.assertRaises(RuntimeError,
201+
self._test_image_convert_invalid_vmdk)
202+
self.assertEqual('Invalid VMDK create-type specified', str(e))
203+
204+
def test_image_convert_valid_vmdk_no_types(self):
205+
with mock.patch.object(CONF.image_format, 'vmdk_allowed_types',
206+
new=[]):
207+
# We make it past the VMDK check and fail because our file
208+
# does not exist
209+
e = self.assertRaises(RuntimeError,
210+
self._test_image_convert_invalid_vmdk)
211+
self.assertEqual('Image is a VMDK, but no VMDK createType is '
212+
'specified', str(e))
213+
214+
def test_image_convert_valid_vmdk(self):
215+
with mock.patch.object(CONF.image_format, 'vmdk_allowed_types',
216+
new=['monolithicSparse', 'monolithicFlat']):
217+
# We make it past the VMDK check and fail because our file
218+
# does not exist
219+
self.assertRaises(FileNotFoundError,
220+
self._test_image_convert_invalid_vmdk)
221+
175222
def test_image_convert_fails(self):
176223
convert = self._setup_image_convert_info_fail()
177224
with mock.patch.object(processutils, 'execute') as exc_mock:

0 commit comments

Comments
 (0)