def set_data(self, data, size=None, backend=None, set_active=True): if size is None: size = 0 # NOTE(markwash): zero -> unknown size # Create the verifier for signature verification (if correct properties # are present) extra_props = self.image.extra_properties verifier = None if signature_utils.should_create_verifier(extra_props): # NOTE(bpoulos): if creating verifier fails, exception will be # raised img_signature = extra_props[signature_utils.SIGNATURE] hash_method = extra_props[signature_utils.HASH_METHOD] key_type = extra_props[signature_utils.KEY_TYPE] cert_uuid = extra_props[signature_utils.CERT_UUID] verifier = signature_utils.get_verifier( context=self.context, img_signature_certificate_uuid=cert_uuid, img_signature_hash_method=hash_method, img_signature=img_signature, img_signature_key_type=key_type) self._upload_to_store(data, verifier, backend, size) if set_active and self.image.status != 'active': self.image.status = 'active'
def verify_glance_image_signature(context, image_service, image_id, path): verifier = None image_meta = image_service.show(context, image_id) image_properties = image_meta.get('properties', {}) img_signature = image_properties.get('img_signature') img_sig_hash_method = image_properties.get('img_signature_hash_method') img_sig_cert_uuid = image_properties.get('img_signature_certificate_uuid') img_sig_key_type = image_properties.get('img_signature_key_type') if all(m is None for m in [img_signature, img_sig_cert_uuid, img_sig_hash_method, img_sig_key_type]): # NOTE(tommylikehu): We won't verify the image signature # if none of the signature metadata presents. return False if any(m is None for m in [img_signature, img_sig_cert_uuid, img_sig_hash_method, img_sig_key_type]): LOG.error('Image signature metadata for image %s is ' 'incomplete.', image_id) raise exception.InvalidSignatureImage(image_id=image_id) try: verifier = signature_utils.get_verifier( context=context, img_signature_certificate_uuid=img_sig_cert_uuid, img_signature_hash_method=img_sig_hash_method, img_signature=img_signature, img_signature_key_type=img_sig_key_type, ) except cursive_exception.SignatureVerificationError: message = _('Failed to get verifier for image: %s') % image_id LOG.error(message) raise exception.ImageSignatureVerificationException( reason=message) if verifier: with fileutils.remove_path_on_error(path): with open(path, "rb") as tem_file: try: tpool.execute(_verify_image, tem_file, verifier) LOG.info('Image signature verification succeeded ' 'for image: %s', image_id) return True except cryptography.exceptions.InvalidSignature: message = _('Image signature verification ' 'failed for image: %s') % image_id LOG.error(message) raise exception.ImageSignatureVerificationException( reason=message) except Exception as ex: message = _('Failed to verify signature for ' 'image: %(image)s due to ' 'error: %(error)s ') % {'image': image_id, 'error': six.text_type(ex)} LOG.error(message) raise exception.ImageSignatureVerificationException( reason=message) return False
def test_verify_signature_ECC(self, mock_get_pub_key): data = b'224626ae19824466f2a7f39ab7b80f7f' # test every ECC curve for curve in signature_utils.ECC_CURVES: key_type_name = 'ECC_' + curve.name.upper() try: signature_utils.SignatureKeyType.lookup(key_type_name) except exception.SignatureVerificationError: import warnings warnings.warn("ECC curve '%s' not supported" % curve.name) continue # Create a private key to use private_key = ec.generate_private_key(curve, default_backend()) mock_get_pub_key.return_value = private_key.public_key() for hash_name, hash_alg in signature_utils.HASH_METHODS.items(): signer = private_key.signer( ec.ECDSA(hash_alg) ) signer.update(data) signature = base64.b64encode(signer.finalize()) img_sig_cert_uuid = 'fea14bc2-d75f-4ba5-bccc-b5c924ad0693' verifier = signature_utils.get_verifier(None, img_sig_cert_uuid, hash_name, signature, key_type_name) verifier.update(data) verifier.verify()
def verify_glance_image_signature(context, image_service, image_id, path): verifier = None image_meta = image_service.show(context, image_id) image_properties = image_meta.get('properties', {}) img_signature = image_properties.get('img_signature') img_sig_hash_method = image_properties.get('img_signature_hash_method') img_sig_cert_uuid = image_properties.get('img_signature_certificate_uuid') img_sig_key_type = image_properties.get('img_signature_key_type') if all(m is None for m in [img_signature, img_sig_cert_uuid, img_sig_hash_method, img_sig_key_type]): # NOTE(tommylikehu): We won't verify the image signature # if none of the signature metadata presents. return False if any(m is None for m in [img_signature, img_sig_cert_uuid, img_sig_hash_method, img_sig_key_type]): LOG.error('Image signature metadata for image %s is ' 'incomplete.', image_id) raise exception.InvalidSignatureImage(image_id=image_id) try: verifier = signature_utils.get_verifier( context=context, img_signature_certificate_uuid=img_sig_cert_uuid, img_signature_hash_method=img_sig_hash_method, img_signature=img_signature, img_signature_key_type=img_sig_key_type, ) except cursive_exception.SignatureVerificationError: message = _('Failed to get verifier for image: %s') % image_id LOG.error(message) raise exception.ImageSignatureVerificationException( reason=message) if verifier: with fileutils.remove_path_on_error(path): with open(path, "rb") as tem_file: try: tpool.execute(_verify_image, tem_file, verifier) LOG.info('Image signature verification succeeded ' 'for image: %s', image_id) return True except cryptography.exceptions.InvalidSignature: message = _('Image signature verification ' 'failed for image: %s') % image_id LOG.error(message) raise exception.ImageSignatureVerificationException( reason=message) except Exception as ex: message = _('Failed to verify signature for ' 'image: %(image)s due to ' 'error: %(error)s ') % {'image': image_id, 'error': six.text_type(ex)} LOG.error(message) raise exception.ImageSignatureVerificationException( reason=message) return False
def _get_verifier(self, context, image_id, trusted_certs): verifier = None # Use the default certs if the user didn't provide any (and there are # default certs configured). if (not trusted_certs and CONF.glance.enable_certificate_validation and CONF.glance.default_trusted_certificate_ids): trusted_certs = objects.TrustedCerts( ids=CONF.glance.default_trusted_certificate_ids) # Verify image signature if feature is enabled or trusted # certificates were provided if trusted_certs or CONF.glance.verify_glance_signatures: image_meta_dict = self.show(context, image_id, include_locations=False) image_meta = objects.ImageMeta.from_dict(image_meta_dict) img_signature = image_meta.properties.get('img_signature') img_sig_hash_method = image_meta.properties.get( 'img_signature_hash_method' ) img_sig_cert_uuid = image_meta.properties.get( 'img_signature_certificate_uuid' ) img_sig_key_type = image_meta.properties.get( 'img_signature_key_type' ) try: verifier = signature_utils.get_verifier( context=context, img_signature_certificate_uuid=img_sig_cert_uuid, img_signature_hash_method=img_sig_hash_method, img_signature=img_signature, img_signature_key_type=img_sig_key_type, ) except cursive_exception.SignatureVerificationError: with excutils.save_and_reraise_exception(): LOG.error('Image signature verification failed ' 'for image: %s', image_id) # Validate image signature certificate if trusted certificates # were provided # NOTE(jackie-truong): Certificate validation will occur if # trusted_certs are provided, even if the certificate validation # feature is disabled. This is to provide safety for the user. # We may want to consider making this a "soft" check in the future. if trusted_certs: _verify_certs(context, img_sig_cert_uuid, trusted_certs) elif CONF.glance.enable_certificate_validation: msg = ('Image signature certificate validation enabled, ' 'but no trusted certificate IDs were provided. ' 'Unable to validate the certificate used to ' 'verify the image signature.') LOG.warning(msg) raise exception.CertificateValidationFailed(msg) else: LOG.debug('Certificate validation was not performed. A list ' 'of trusted image certificate IDs must be provided ' 'in order to validate an image certificate.') return verifier
def _get_verifier(self, context, image_id, trusted_certs): verifier = None # Use the default certs if the user didn't provide any (and there are # default certs configured). if (not trusted_certs and CONF.glance.enable_certificate_validation and CONF.glance.default_trusted_certificate_ids): trusted_certs = objects.TrustedCerts( ids=CONF.glance.default_trusted_certificate_ids) # Verify image signature if feature is enabled or trusted # certificates were provided if trusted_certs or CONF.glance.verify_glance_signatures: image_meta_dict = self.show(context, image_id, include_locations=False) image_meta = objects.ImageMeta.from_dict(image_meta_dict) img_signature = image_meta.properties.get('img_signature') img_sig_hash_method = image_meta.properties.get( 'img_signature_hash_method') img_sig_cert_uuid = image_meta.properties.get( 'img_signature_certificate_uuid') img_sig_key_type = image_meta.properties.get( 'img_signature_key_type') try: verifier = signature_utils.get_verifier( context=context, img_signature_certificate_uuid=img_sig_cert_uuid, img_signature_hash_method=img_sig_hash_method, img_signature=img_signature, img_signature_key_type=img_sig_key_type, ) except cursive_exception.SignatureVerificationError: with excutils.save_and_reraise_exception(): LOG.error( 'Image signature verification failed ' 'for image: %s', image_id) # Validate image signature certificate if trusted certificates # were provided # NOTE(jackie-truong): Certificate validation will occur if # trusted_certs are provided, even if the certificate validation # feature is disabled. This is to provide safety for the user. # We may want to consider making this a "soft" check in the future. if trusted_certs: _verify_certs(context, img_sig_cert_uuid, trusted_certs) elif CONF.glance.enable_certificate_validation: msg = ('Image signature certificate validation enabled, ' 'but no trusted certificate IDs were provided. ' 'Unable to validate the certificate used to ' 'verify the image signature.') LOG.warning(msg) raise exception.CertificateValidationFailed(msg) else: LOG.debug('Certificate validation was not performed. A list ' 'of trusted image certificate IDs must be provided ' 'in order to validate an image certificate.') return verifier
def test_verify_signature_bad_signature(self, mock_get_pub_key): data = b'224626ae19824466f2a7f39ab7b80f7f' mock_get_pub_key.return_value = TEST_RSA_PRIVATE_KEY.public_key() img_sig_cert_uuid = 'fea14bc2-d75f-4ba5-bccc-b5c924ad0693' verifier = signature_utils.get_verifier(None, img_sig_cert_uuid, 'SHA-256', 'BLAH', signature_utils.RSA_PSS) verifier.update(data) self.assertRaises(crypto_exceptions.InvalidSignature, verifier.verify)
def set_data(self, data, size=None): if size is None: size = 0 # NOTE(markwash): zero -> unknown size # Create the verifier for signature verification (if correct properties # are present) extra_props = self.image.extra_properties if (signature_utils.should_create_verifier(extra_props)): # NOTE(bpoulos): if creating verifier fails, exception will be # raised img_signature = extra_props[signature_utils.SIGNATURE] hash_method = extra_props[signature_utils.HASH_METHOD] key_type = extra_props[signature_utils.KEY_TYPE] cert_uuid = extra_props[signature_utils.CERT_UUID] verifier = signature_utils.get_verifier( context=self.context, img_signature_certificate_uuid=cert_uuid, img_signature_hash_method=hash_method, img_signature=img_signature, img_signature_key_type=key_type) else: verifier = None location, size, checksum, loc_meta = self.store_api.add_to_backend( CONF, self.image.image_id, utils.LimitingReader(utils.CooperativeReader(data), CONF.image_size_cap), size, context=self.context, verifier=verifier) # NOTE(bpoulos): if verification fails, exception will be raised if verifier: try: verifier.verify() LOG.info(_LI("Successfully verified signature for image %s"), self.image.image_id) except crypto_exception.InvalidSignature: self.store_api.delete_from_backend(location, context=self.context) raise cursive_exception.SignatureVerificationError( _('Signature verification failed')) self.image.locations = [{ 'url': location, 'metadata': loc_meta, 'status': 'active' }] self.image.size = size self.image.checksum = checksum self.image.status = 'active'
def set_data(self, data, size=None): if size is None: size = 0 # NOTE(markwash): zero -> unknown size # Create the verifier for signature verification (if correct properties # are present) extra_props = self.image.extra_properties if (signature_utils.should_create_verifier(extra_props)): # NOTE(bpoulos): if creating verifier fails, exception will be # raised img_signature = extra_props[signature_utils.SIGNATURE] hash_method = extra_props[signature_utils.HASH_METHOD] key_type = extra_props[signature_utils.KEY_TYPE] cert_uuid = extra_props[signature_utils.CERT_UUID] verifier = signature_utils.get_verifier( context=self.context, img_signature_certificate_uuid=cert_uuid, img_signature_hash_method=hash_method, img_signature=img_signature, img_signature_key_type=key_type ) else: verifier = None location, size, checksum, loc_meta = self.store_api.add_to_backend( CONF, self.image.image_id, utils.LimitingReader(utils.CooperativeReader(data), CONF.image_size_cap), size, context=self.context, verifier=verifier) # NOTE(bpoulos): if verification fails, exception will be raised if verifier: try: verifier.verify() LOG.info(_LI("Successfully verified signature for image %s"), self.image.image_id) except crypto_exception.InvalidSignature: raise cursive_exception.SignatureVerificationError( _('Signature verification failed') ) self.image.locations = [{'url': location, 'metadata': loc_meta, 'status': 'active'}] self.image.size = size self.image.checksum = checksum self.image.status = 'active'
def test_verify_signature_DSA(self, mock_get_pub_key): data = b'224626ae19824466f2a7f39ab7b80f7f' mock_get_pub_key.return_value = TEST_DSA_PRIVATE_KEY.public_key() for hash_name, hash_alg in signature_utils.HASH_METHODS.items(): signer = TEST_DSA_PRIVATE_KEY.signer( hash_alg ) signer.update(data) signature = base64.b64encode(signer.finalize()) img_sig_cert_uuid = 'fea14bc2-d75f-4ba5-bccc-b5c924ad0693' verifier = signature_utils.get_verifier(None, img_sig_cert_uuid, hash_name, signature, signature_utils.DSA) verifier.update(data) verifier.verify()
def set_data(self, data, size=None, backend=None, set_active=True): if size is None: size = 0 # NOTE(markwash): zero -> unknown size # Create the verifier for signature verification (if correct properties # are present) extra_props = self.image.extra_properties verifier = None if signature_utils.should_create_verifier(extra_props): # NOTE(bpoulos): if creating verifier fails, exception will be # raised img_signature = extra_props[signature_utils.SIGNATURE] hash_method = extra_props[signature_utils.HASH_METHOD] key_type = extra_props[signature_utils.KEY_TYPE] cert_uuid = extra_props[signature_utils.CERT_UUID] verifier = signature_utils.get_verifier( context=self.context, img_signature_certificate_uuid=cert_uuid, img_signature_hash_method=hash_method, img_signature=img_signature, img_signature_key_type=key_type) if not self.image.virtual_size: inspector = format_inspector.get_inspector(self.image.disk_format) else: # No need to do this again inspector = None if inspector and self.image.container_format == 'bare': fmt = inspector() data = format_inspector.InfoWrapper(data, fmt) LOG.debug('Enabling in-flight format inspection for %s', fmt) else: fmt = None self._upload_to_store(data, verifier, backend, size) if fmt and fmt.format_match and fmt.virtual_size: self.image.virtual_size = fmt.virtual_size LOG.info('Image format matched and virtual size computed: %i', self.image.virtual_size) elif fmt: LOG.warning( 'Image format %s did not match; ' 'unable to calculate virtual size', self.image.disk_format) if set_active and self.image.status != 'active': self.image.status = 'active'
def download(self, context, image_id, data=None, dst_path=None): """Calls out to Glance for data and writes data.""" if CONF.glance.allowed_direct_url_schemes and dst_path is not None: image = self.show(context, image_id, include_locations=True) for entry in image.get('locations', []): loc_url = entry['url'] loc_meta = entry['metadata'] o = urlparse.urlparse(loc_url) xfer_mod = self._get_transfer_module(o.scheme) if xfer_mod: try: xfer_mod.download(context, o, dst_path, loc_meta) LOG.info("Successfully transferred using %s", o.scheme) return except Exception: LOG.exception("Download image error") try: image_chunks = self._client.call(context, 2, 'data', image_id) except Exception: _reraise_translated_image_exception(image_id) if image_chunks.wrapped is None: # None is a valid return value, but there's nothing we can do with # a image with no associated data raise exception.ImageUnacceptable( image_id=image_id, reason='Image has no associated data') # Retrieve properties for verification of Glance image signature verifier = None if CONF.glance.verify_glance_signatures: image_meta_dict = self.show(context, image_id, include_locations=False) image_meta = objects.ImageMeta.from_dict(image_meta_dict) img_signature = image_meta.properties.get('img_signature') img_sig_hash_method = image_meta.properties.get( 'img_signature_hash_method') img_sig_cert_uuid = image_meta.properties.get( 'img_signature_certificate_uuid') img_sig_key_type = image_meta.properties.get( 'img_signature_key_type') try: verifier = signature_utils.get_verifier( context=context, img_signature_certificate_uuid=img_sig_cert_uuid, img_signature_hash_method=img_sig_hash_method, img_signature=img_signature, img_signature_key_type=img_sig_key_type, ) except cursive_exception.SignatureVerificationError: with excutils.save_and_reraise_exception(): LOG.error( 'Image signature verification failed ' 'for image: %s', image_id) close_file = False if data is None and dst_path: data = open(dst_path, 'wb') close_file = True if data is None: # Perform image signature verification if verifier: try: for chunk in image_chunks: verifier.update(chunk) verifier.verify() LOG.info( 'Image signature verification succeeded ' 'for image: %s', image_id) except cryptography.exceptions.InvalidSignature: with excutils.save_and_reraise_exception(): LOG.error( 'Image signature verification failed ' 'for image: %s', image_id) return image_chunks else: try: for chunk in image_chunks: if verifier: verifier.update(chunk) data.write(chunk) if verifier: verifier.verify() LOG.info( 'Image signature verification succeeded ' 'for image %s', image_id) except cryptography.exceptions.InvalidSignature: data.truncate(0) with excutils.save_and_reraise_exception(): LOG.error( 'Image signature verification failed ' 'for image: %s', image_id) except Exception as ex: with excutils.save_and_reraise_exception(): LOG.error("Error writing to %(path)s: %(exception)s", { 'path': dst_path, 'exception': ex }) finally: if close_file: # Ensure that the data is pushed all the way down to # persistent storage. This ensures that in the event of a # subsequent host crash we don't have running instances # using a corrupt backing file. data.flush() self._safe_fsync(data) data.close()
def download(self, context, image_id, data=None, dst_path=None): """Calls out to Glance for data and writes data.""" if CONF.glance.allowed_direct_url_schemes and dst_path is not None: image = self.show(context, image_id, include_locations=True) for entry in image.get('locations', []): loc_url = entry['url'] loc_meta = entry['metadata'] o = urlparse.urlparse(loc_url) xfer_mod = self._get_transfer_module(o.scheme) if xfer_mod: try: xfer_mod.download(context, o, dst_path, loc_meta) LOG.info("Successfully transferred using %s", o.scheme) return except Exception: LOG.exception("Download image error") try: image_chunks = self._client.call(context, 2, 'data', image_id) except Exception: _reraise_translated_image_exception(image_id) # Retrieve properties for verification of Glance image signature verifier = None if CONF.glance.verify_glance_signatures: image_meta_dict = self.show(context, image_id, include_locations=False) image_meta = objects.ImageMeta.from_dict(image_meta_dict) img_signature = image_meta.properties.get('img_signature') img_sig_hash_method = image_meta.properties.get( 'img_signature_hash_method' ) img_sig_cert_uuid = image_meta.properties.get( 'img_signature_certificate_uuid' ) img_sig_key_type = image_meta.properties.get( 'img_signature_key_type' ) try: verifier = signature_utils.get_verifier( context=context, img_signature_certificate_uuid=img_sig_cert_uuid, img_signature_hash_method=img_sig_hash_method, img_signature=img_signature, img_signature_key_type=img_sig_key_type, ) except cursive_exception.SignatureVerificationError: with excutils.save_and_reraise_exception(): LOG.error('Image signature verification failed ' 'for image: %s', image_id) close_file = False if data is None and dst_path: data = open(dst_path, 'wb') close_file = True if data is None: # Perform image signature verification if verifier: try: for chunk in image_chunks: verifier.update(chunk) verifier.verify() LOG.info('Image signature verification succeeded ' 'for image: %s', image_id) except cryptography.exceptions.InvalidSignature: with excutils.save_and_reraise_exception(): LOG.error('Image signature verification failed ' 'for image: %s', image_id) return image_chunks else: try: for chunk in image_chunks: if verifier: verifier.update(chunk) data.write(chunk) if verifier: verifier.verify() LOG.info('Image signature verification succeeded ' 'for image %s', image_id) except cryptography.exceptions.InvalidSignature: data.truncate(0) with excutils.save_and_reraise_exception(): LOG.error('Image signature verification failed ' 'for image: %s', image_id) except Exception as ex: with excutils.save_and_reraise_exception(): LOG.error("Error writing to %(path)s: %(exception)s", {'path': dst_path, 'exception': ex}) finally: if close_file: # Ensure that the data is pushed all the way down to # persistent storage. This ensures that in the event of a # subsequent host crash we don't have running instances # using a corrupt backing file. data.flush() os.fsync(data.fileno()) data.close()
def download(self, context, image_id, data=None, dst_path=None): """Calls out to Glance for data and writes data.""" if CONF.glance.allowed_direct_url_schemes and dst_path is not None: image = self.show(context, image_id, include_locations=True) for entry in image.get('locations', []): loc_url = entry['url'] loc_meta = entry['metadata'] o = urlparse.urlparse(loc_url) xfer_mod = self._get_transfer_module(o.scheme) if xfer_mod: try: xfer_mod.download(context, o, dst_path, loc_meta) LOG.info("Successfully transferred using %s", o.scheme) return except Exception: LOG.exception("Download image error") try: image_chunks = self._client.call(context, 2, 'data', image_id) except Exception: _reraise_translated_image_exception(image_id) # Retrieve properties for verification of Glance image signature verifier = None if CONF.glance.verify_glance_signatures: image_meta_dict = self.show(context, image_id, include_locations=False) image_meta = objects.ImageMeta.from_dict(image_meta_dict) img_signature = image_meta.properties.get('img_signature') img_sig_hash_method = image_meta.properties.get( 'img_signature_hash_method' ) img_sig_cert_uuid = image_meta.properties.get( 'img_signature_certificate_uuid' ) img_sig_key_type = image_meta.properties.get( 'img_signature_key_type' ) try: verifier = signature_utils.get_verifier( context=context, img_signature_certificate_uuid=img_sig_cert_uuid, img_signature_hash_method=img_sig_hash_method, img_signature=img_signature, img_signature_key_type=img_sig_key_type, ) except cursive_exception.SignatureVerificationError: with excutils.save_and_reraise_exception(): LOG.error('Image signature verification failed ' 'for image: %s', image_id) close_file = False if data is None and dst_path: data = open(dst_path, 'wb') close_file = True if data is None: # Perform image signature verification if verifier: try: for chunk in image_chunks: verifier.update(chunk) verifier.verify() LOG.info('Image signature verification succeeded ' 'for image: %s', image_id) except cryptography.exceptions.InvalidSignature: with excutils.save_and_reraise_exception(): LOG.error('Image signature verification failed ' 'for image: %s', image_id) return image_chunks else: # WRS: offload image download to another thread to reduce chances # of nova-compute getting stuck on disk IO def write_image(data, image_chunks, close_file, verifier): try: for chunk in image_chunks: if verifier: verifier.update(chunk) data.write(chunk) # Without this periodic tasks get delayed time.sleep(0) if verifier: verifier.verify() LOG.info('Image signature verification succeeded ' 'for image %s', image_id) except cryptography.exceptions.InvalidSignature: data.truncate(0) with excutils.save_and_reraise_exception(): LOG.error('Image signature verification failed ' 'for image: %s', image_id) except Exception as ex: with excutils.save_and_reraise_exception(): LOG.error("Error writing to %(path)s: " "%(exception)s", {'path': dst_path, 'exception': ex}) finally: if close_file: # Ensure that the data is pushed all the way down to # persistent storage. This ensures that in the event # of a subsequent host crash we don't have running # instances using a corrupt backing file. data.flush() os.fsync(data.fileno()) data.close() tpool.execute(write_image, data, image_chunks, close_file, verifier)